Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrhid3d12.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qrhid3d12_p.h"
6#include <qmath.h>
7#include <QtCore/private/qsystemerror_p.h>
8#include <comdef.h>
10#include "cs_mipmap_p.h"
11#include "cs_mipmap_3d_p.h"
12
13#if __has_include(<pix.h>)
14#include <pix.h>
15#define QRHI_D3D12_HAS_OLD_PIX
16#endif
17
18#ifdef __ID3D12Device2_INTERFACE_DEFINED__
19
20QT_BEGIN_NAMESPACE
21
22/*
23 Direct 3D 12 backend.
24*/
25
26/*!
27 \class QRhiD3D12InitParams
28 \inmodule QtGuiPrivate
29 \inheaderfile rhi/qrhi.h
30 \brief Direct3D 12 specific initialization parameters.
31
32 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
33 for details.
34
35 A D3D12-based QRhi needs no special parameters for initialization. If
36 desired, enableDebugLayer can be set to \c true to enable the Direct3D
37 debug layer. This can be useful during development, but should be avoided
38 in production builds.
39
40 \badcode
41 QRhiD3D12InitParams params;
42 params.enableDebugLayer = true;
43 rhi = QRhi::create(QRhi::D3D12, &params);
44 \endcode
45
46 \note QRhiSwapChain should only be used in combination with QWindow
47 instances that have their surface type set to QSurface::Direct3DSurface.
48
49 \section2 Working with existing Direct3D 12 devices
50
51 When interoperating with another graphics engine, it may be necessary to
52 get a QRhi instance that uses the same Direct3D device. This can be
53 achieved by passing a pointer to a QRhiD3D12NativeHandles to
54 QRhi::create(). QRhi does not take ownership of any of the external
55 objects.
56
57 Sometimes, for example when using QRhi in combination with OpenXR, one will
58 want to specify which adapter to use, and optionally, which feature level
59 to request on the device, while leaving the device creation to QRhi. This
60 is achieved by leaving the device pointer set to null, while specifying the
61 adapter LUID and feature level.
62
63 Optionally the ID3D12CommandQueue can be specified as well, by setting \c
64 commandQueue to a non-null value.
65 */
66
67/*!
68 \variable QRhiD3D12InitParams::enableDebugLayer
69
70 When set to true, the debug layer is enabled, if installed and available.
71 The default value is false.
72*/
73
74/*!
75 \class QRhiD3D12NativeHandles
76 \inmodule QtGuiPrivate
77 \inheaderfile rhi/qrhi.h
78 \brief Holds the D3D12 device used by the QRhi.
79
80 \note The class uses \c{void *} as the type since including the COM-based
81 \c{d3d12.h} headers is not acceptable here. The actual types are
82 \c{ID3D12Device *} and \c{ID3D12CommandQueue *}.
83
84 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
85 for details.
86 */
87
88/*!
89 \variable QRhiD3D12NativeHandles::dev
90
91 Points to a
92 \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12device}{ID3D12Device}
93 or left set to \nullptr if no existing device is to be imported.
94*/
95
96/*!
97 \variable QRhiD3D12NativeHandles::minimumFeatureLevel
98
99 Specifies the \b minimum feature level passed to
100 \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12createdevice}{D3D12CreateDevice()}.
101 When not set, \c{D3D_FEATURE_LEVEL_11_0} is used. See
102 \l{https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels}{this
103 page} for details.
104
105 Relevant only when QRhi creates the device, ignored when importing a device
106 and device context.
107*/
108
109/*!
110 \variable QRhiD3D12NativeHandles::adapterLuidLow
111
112 The low part of the local identifier (LUID) of the DXGI adapter to use.
113 Relevant only when QRhi creates the device, ignored when importing a device
114 and device context.
115*/
116
117/*!
118 \variable QRhiD3D12NativeHandles::adapterLuidHigh
119
120 The high part of the local identifier (LUID) of the DXGI adapter to use.
121 Relevant only when QRhi creates the device, ignored when importing a device
122 and device context.
123*/
124
125/*!
126 \variable QRhiD3D12NativeHandles::commandQueue
127
128 When set, must point to a
129 \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12commandqueue}{ID3D12CommandQueue}.
130 It allows to optionally import a command queue as well, in addition to a
131 device.
132*/
133
134/*!
135 \class QRhiD3D12CommandBufferNativeHandles
136 \inmodule QtGuiPrivate
137 \inheaderfile rhi/qrhi.h
138 \brief Holds the ID3D12GraphicsCommandList1 object that is backing a QRhiCommandBuffer.
139
140 \note The command list object is only guaranteed to be valid, and
141 in recording state, while recording a frame. That is, between a
142 \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
143 \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
144 \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} pair.
145
146 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
147 for details.
148 */
149
150/*!
151 \variable QRhiD3D12CommandBufferNativeHandles::commandList
152*/
153
154// https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels
155static const D3D_FEATURE_LEVEL MIN_FEATURE_LEVEL = D3D_FEATURE_LEVEL_11_0;
156
157QRhiD3D12::QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *importParams)
158{
159 debugLayer = params->enableDebugLayer;
160 if (importParams) {
161 if (importParams->dev) {
162 ID3D12Device *d3d12Device = reinterpret_cast<ID3D12Device *>(importParams->dev);
163 if (SUCCEEDED(d3d12Device->QueryInterface(__uuidof(ID3D12Device2), reinterpret_cast<void **>(&dev)))) {
164 // get rid of the ref added by QueryInterface
165 d3d12Device->Release();
166 importedDevice = true;
167 } else {
168 qWarning("ID3D12Device2 not supported, cannot import device");
169 }
170 }
171 if (importParams->commandQueue) {
172 cmdQueue = reinterpret_cast<ID3D12CommandQueue *>(importParams->commandQueue);
173 importedCommandQueue = true;
174 }
175 minimumFeatureLevel = D3D_FEATURE_LEVEL(importParams->minimumFeatureLevel);
176 adapterLuid.LowPart = importParams->adapterLuidLow;
177 adapterLuid.HighPart = importParams->adapterLuidHigh;
178 }
179}
180
181template <class Int>
182inline Int aligned(Int v, Int byteAlign)
183{
184 return (v + byteAlign - 1) & ~(byteAlign - 1);
185}
186
187static inline UINT calcSubresource(UINT mipSlice, UINT arraySlice, UINT mipLevels)
188{
189 return mipSlice + arraySlice * mipLevels;
190}
191
192static inline QD3D12RenderTargetData *rtData(QRhiRenderTarget *rt)
193{
194 switch (rt->resourceType()) {
195 case QRhiResource::SwapChainRenderTarget:
196 return &QRHI_RES(QD3D12SwapChainRenderTarget, rt)->d;
197 case QRhiResource::TextureRenderTarget:
198 return &QRHI_RES(QD3D12TextureRenderTarget, rt)->d;
199 break;
200 default:
201 break;
202 }
203 Q_UNREACHABLE_RETURN(nullptr);
204}
205
206bool QRhiD3D12::create(QRhi::Flags flags)
207{
208 rhiFlags = flags;
209
210 UINT factoryFlags = 0;
211 if (debugLayer)
212 factoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
213 HRESULT hr = CreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory));
214 if (FAILED(hr)) {
215 // retry without debug, if it was requested (to match D3D11 backend behavior)
216 if (debugLayer) {
217 qCDebug(QRHI_LOG_INFO, "Debug layer was requested but is not available. "
218 "Attempting to create DXGIFactory2 without it.");
219 factoryFlags &= ~DXGI_CREATE_FACTORY_DEBUG;
220 hr = CreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory));
221 }
222 if (SUCCEEDED(hr)) {
223 debugLayer = false;
224 } else {
225 qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s",
226 qPrintable(QSystemError::windowsComString(hr)));
227 return false;
228 }
229 }
230
231 if (qEnvironmentVariableIsSet("QT_D3D_MAX_FRAME_LATENCY"))
232 maxFrameLatency = UINT(qMax(0, qEnvironmentVariableIntValue("QT_D3D_MAX_FRAME_LATENCY")));
233 if (maxFrameLatency != 0)
234 qCDebug(QRHI_LOG_INFO, "Using frame latency waitable object with max frame latency %u", maxFrameLatency);
235
236 supportsAllowTearing = false;
237 IDXGIFactory5 *factory5 = nullptr;
238 if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast<void **>(&factory5)))) {
239 BOOL allowTearing = false;
240 if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
241 supportsAllowTearing = allowTearing;
242 factory5->Release();
243 }
244
245 if (debugLayer) {
246 ID3D12Debug1 *debug = nullptr;
247 if (SUCCEEDED(D3D12GetDebugInterface(__uuidof(ID3D12Debug1), reinterpret_cast<void **>(&debug)))) {
248 qCDebug(QRHI_LOG_INFO, "Enabling D3D12 debug layer");
249 debug->EnableDebugLayer();
250 debug->Release();
251 }
252 }
253
254 activeAdapter = nullptr;
255
256 if (!importedDevice) {
257 IDXGIAdapter1 *adapter;
258 int requestedAdapterIndex = -1;
259 if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
260 requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
261
262 if (requestedRhiAdapter)
263 adapterLuid = static_cast<QD3D12Adapter *>(requestedRhiAdapter)->luid;
264
265 // importParams or requestedRhiAdapter may specify an adapter by the luid, use that in the absence of an env.var. override.
266 if (requestedAdapterIndex < 0 && (adapterLuid.LowPart || adapterLuid.HighPart)) {
267 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
268 DXGI_ADAPTER_DESC1 desc;
269 adapter->GetDesc1(&desc);
270 adapter->Release();
271 if (desc.AdapterLuid.LowPart == adapterLuid.LowPart
272 && desc.AdapterLuid.HighPart == adapterLuid.HighPart)
273 {
274 requestedAdapterIndex = adapterIndex;
275 break;
276 }
277 }
278 }
279
280 if (requestedAdapterIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
281 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
282 DXGI_ADAPTER_DESC1 desc;
283 adapter->GetDesc1(&desc);
284 adapter->Release();
285 if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
286 requestedAdapterIndex = adapterIndex;
287 break;
288 }
289 }
290 }
291
292 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
293 DXGI_ADAPTER_DESC1 desc;
294 adapter->GetDesc1(&desc);
295 const QString name = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description));
296 qCDebug(QRHI_LOG_INFO, "Adapter %d: '%s' (vendor 0x%X device 0x%X flags 0x%X)",
297 adapterIndex,
298 qPrintable(name),
299 desc.VendorId,
300 desc.DeviceId,
301 desc.Flags);
302 if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
303 activeAdapter = adapter;
304 adapterLuid = desc.AdapterLuid;
305 QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
306 qCDebug(QRHI_LOG_INFO, " using this adapter");
307 } else {
308 adapter->Release();
309 }
310 }
311 if (!activeAdapter) {
312 qWarning("No adapter");
313 return false;
314 }
315
316 if (minimumFeatureLevel == 0)
317 minimumFeatureLevel = MIN_FEATURE_LEVEL;
318
319 hr = D3D12CreateDevice(activeAdapter,
320 minimumFeatureLevel,
321 __uuidof(ID3D12Device2),
322 reinterpret_cast<void **>(&dev));
323 if (FAILED(hr)) {
324 qWarning("Failed to create D3D12 device: %s", qPrintable(QSystemError::windowsComString(hr)));
325 return false;
326 }
327 } else {
328 Q_ASSERT(dev);
329 // cannot just get a IDXGIDevice from the ID3D12Device anymore, look up the adapter instead
330 adapterLuid = dev->GetAdapterLuid();
331 IDXGIAdapter1 *adapter;
332 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
333 DXGI_ADAPTER_DESC1 desc;
334 adapter->GetDesc1(&desc);
335 if (desc.AdapterLuid.LowPart == adapterLuid.LowPart
336 && desc.AdapterLuid.HighPart == adapterLuid.HighPart)
337 {
338 activeAdapter = adapter;
339 QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
340 break;
341 } else {
342 adapter->Release();
343 }
344 }
345 if (!activeAdapter) {
346 qWarning("No adapter");
347 return false;
348 }
349 qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
350 }
351
352 QDxgiVSyncService::instance()->refAdapter(adapterLuid);
353
354 if (debugLayer) {
355 ID3D12InfoQueue *infoQueue;
356 if (SUCCEEDED(dev->QueryInterface(__uuidof(ID3D12InfoQueue), reinterpret_cast<void **>(&infoQueue)))) {
357 if (qEnvironmentVariableIntValue("QT_D3D_DEBUG_BREAK")) {
358 infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
359 infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
360 infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
361 }
362 D3D12_INFO_QUEUE_FILTER filter = {};
363 D3D12_MESSAGE_ID suppressedMessages[2] = {
364 // there is no way of knowing the clear color upfront
365 D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
366 // we have no control over viewport and scissor rects
367 D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE
368 };
369 filter.DenyList.NumIDs = 2;
370 filter.DenyList.pIDList = suppressedMessages;
371 // Setting the filter would enable Info messages (e.g. about
372 // resource creation) which we don't need.
373 D3D12_MESSAGE_SEVERITY infoSev = D3D12_MESSAGE_SEVERITY_INFO;
374 filter.DenyList.NumSeverities = 1;
375 filter.DenyList.pSeverityList = &infoSev;
376 infoQueue->PushStorageFilter(&filter);
377 infoQueue->Release();
378 }
379 }
380
381 if (!importedCommandQueue) {
382 D3D12_COMMAND_QUEUE_DESC queueDesc = {};
383 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
384 queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
385 hr = dev->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), reinterpret_cast<void **>(&cmdQueue));
386 if (FAILED(hr)) {
387 qWarning("Failed to create command queue: %s", qPrintable(QSystemError::windowsComString(hr)));
388 return false;
389 }
390 }
391
392 hr = dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), reinterpret_cast<void **>(&fullFence));
393 if (FAILED(hr)) {
394 qWarning("Failed to create fence: %s", qPrintable(QSystemError::windowsComString(hr)));
395 return false;
396 }
397 fullFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
398 fullFenceCounter = 0;
399
400 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
401 hr = dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
402 __uuidof(ID3D12CommandAllocator),
403 reinterpret_cast<void **>(&cmdAllocators[i]));
404 if (FAILED(hr)) {
405 qWarning("Failed to create command allocator: %s", qPrintable(QSystemError::windowsComString(hr)));
406 return false;
407 }
408 }
409
410 if (!vma.create(dev, activeAdapter)) {
411 qWarning("Failed to initialize graphics memory suballocator");
412 return false;
413 }
414
415 if (!rtvPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, "main RTV pool")) {
416 qWarning("Could not create RTV pool");
417 return false;
418 }
419
420 if (!dsvPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, "main DSV pool")) {
421 qWarning("Could not create DSV pool");
422 return false;
423 }
424
425 if (!cbvSrvUavPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, "main CBV-SRV-UAV pool")) {
426 qWarning("Could not create CBV-SRV-UAV pool");
427 return false;
428 }
429
430 resourcePool.create("main resource pool");
431 pipelinePool.create("main pipeline pool");
432 rootSignaturePool.create("main root signature pool");
433 releaseQueue.create(&resourcePool, &pipelinePool, &rootSignaturePool);
434 barrierGen.create(&resourcePool);
435
436 if (!samplerMgr.create(dev)) {
437 qWarning("Could not create sampler pool and shader-visible sampler heap");
438 return false;
439 }
440
441 if (!mipmapGen.create(this)) {
442 qWarning("Could not initialize mipmap generator");
443 return false;
444 }
445
446 if (!mipmapGen3D.create(this)) {
447 qWarning("Could not initialize 3D texture mipmap generator");
448 return false;
449 }
450
451 const qint32 smallStagingSize = aligned(SMALL_STAGING_AREA_BYTES_PER_FRAME, QD3D12StagingArea::ALIGNMENT);
452 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
453 if (!smallStagingAreas[i].create(this, smallStagingSize, D3D12_HEAP_TYPE_UPLOAD)) {
454 qWarning("Could not create host-visible staging area");
455 return false;
456 }
457 QString decoratedName = QLatin1String("Small staging area buffer/");
458 decoratedName += QString::number(i);
459 smallStagingAreas[i].mem.buffer->SetName(reinterpret_cast<LPCWSTR>(decoratedName.utf16()));
460 }
461
462 if (!shaderVisibleCbvSrvUavHeap.create(dev,
463 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
464 SHADER_VISIBLE_CBV_SRV_UAV_HEAP_PER_FRAME_START_SIZE))
465 {
466 qWarning("Could not create first shader-visible CBV/SRV/UAV heap");
467 return false;
468 }
469
470 if (flags.testFlag(QRhi::EnableTimestamps)) {
471 static bool wantsStablePowerState = qEnvironmentVariableIntValue("QT_D3D_STABLE_POWER_STATE");
472 //
473 // https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-setstablepowerstate
474 //
475 // NB! This is a _global_ setting, affecting other processes (and 3D
476 // APIs such as Vulkan), as long as this application is running. Hence
477 // making it an env.var. for now. Never enable it in production. But
478 // extremely useful for the GPU timings with NVIDIA at least; the
479 // timestamps become stable and smooth, making the number readable and
480 // actually useful e.g. in Quick 3D's DebugView when this is enabled.
481 // (otherwise the number's all over the place)
482 //
483 // See also
484 // https://developer.nvidia.com/blog/advanced-api-performance-setstablepowerstate/
485 // for possible other approaches.
486 //
487 if (wantsStablePowerState)
488 dev->SetStablePowerState(TRUE);
489
490 hr = cmdQueue->GetTimestampFrequency(&timestampTicksPerSecond);
491 if (FAILED(hr)) {
492 qWarning("Failed to query timestamp frequency: %s",
493 qPrintable(QSystemError::windowsComString(hr)));
494 return false;
495 }
496 if (!timestampQueryHeap.create(dev, QD3D12_FRAMES_IN_FLIGHT * 2, D3D12_QUERY_HEAP_TYPE_TIMESTAMP)) {
497 qWarning("Failed to create timestamp query pool");
498 return false;
499 }
500 const quint32 readbackBufSize = QD3D12_FRAMES_IN_FLIGHT * 2 * sizeof(quint64);
501 if (!timestampReadbackArea.create(this, readbackBufSize, D3D12_HEAP_TYPE_READBACK)) {
502 qWarning("Failed to create timestamp readback buffer");
503 return false;
504 }
505 timestampReadbackArea.mem.buffer->SetName(L"Timestamp readback buffer");
506 memset(timestampReadbackArea.mem.p, 0, readbackBufSize);
507 }
508
509 caps = {};
510 D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {};
511 if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) {
512 caps.multiView = options3.ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED;
513 // https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html
514 caps.textureViewFormat = options3.CastingFullyTypedFormatSupported;
515 }
516
517#ifdef QRHI_D3D12_CL5_AVAILABLE
518 D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = {};
519 if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, sizeof(options6)))) {
520 caps.vrs = options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED;
521 caps.vrsMap = options6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2;
522 caps.vrsAdditionalRates = options6.AdditionalShadingRatesSupported;
523 shadingRateImageTileSize = options6.ShadingRateImageTileSize;
524 }
525#else
526 caps.vrs = false;
527 caps.vrsMap = false;
528 caps.vrsAdditionalRates = false;
529#endif
530
531 deviceLost = false;
532 offscreenActive = false;
533
534 nativeHandlesStruct.dev = dev;
535 nativeHandlesStruct.minimumFeatureLevel = minimumFeatureLevel;
536 nativeHandlesStruct.adapterLuidLow = adapterLuid.LowPart;
537 nativeHandlesStruct.adapterLuidHigh = adapterLuid.HighPart;
538 nativeHandlesStruct.commandQueue = cmdQueue;
539
540 return true;
541}
542
543void QRhiD3D12::destroy()
544{
545 if (!deviceLost && fullFence && fullFenceEvent)
546 waitGpu();
547
548 releaseQueue.releaseAll();
549
550 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
551 if (offscreenCb[i]) {
552 if (offscreenCb[i]->cmdList)
553 offscreenCb[i]->cmdList->Release();
554 delete offscreenCb[i];
555 offscreenCb[i] = nullptr;
556 }
557 }
558
559 timestampQueryHeap.destroy();
560 timestampReadbackArea.destroy();
561
562 shaderVisibleCbvSrvUavHeap.destroy();
563
564 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i)
565 smallStagingAreas[i].destroy();
566
567 mipmapGen.destroy();
568 mipmapGen3D.destroy();
569 samplerMgr.destroy();
570 resourcePool.destroy();
571 pipelinePool.destroy();
572 rootSignaturePool.destroy();
573 rtvPool.destroy();
574 dsvPool.destroy();
575 cbvSrvUavPool.destroy();
576
577 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
578 if (cmdAllocators[i]) {
579 cmdAllocators[i]->Release();
580 cmdAllocators[i] = nullptr;
581 }
582 }
583
584 if (fullFenceEvent) {
585 CloseHandle(fullFenceEvent);
586 fullFenceEvent = nullptr;
587 }
588
589 if (fullFence) {
590 fullFence->Release();
591 fullFence = nullptr;
592 }
593
594 if (!importedCommandQueue) {
595 if (cmdQueue) {
596 cmdQueue->Release();
597 cmdQueue = nullptr;
598 }
599 }
600
601 vma.destroy();
602
603 if (!importedDevice) {
604 if (dev) {
605 dev->Release();
606 dev = nullptr;
607 }
608 }
609
610 if (dcompDevice) {
611 dcompDevice->Release();
612 dcompDevice = nullptr;
613 }
614
615 if (activeAdapter) {
616 activeAdapter->Release();
617 activeAdapter = nullptr;
618 }
619
620 if (dxgiFactory) {
621 dxgiFactory->Release();
622 dxgiFactory = nullptr;
623 }
624
625 adapterLuid = {};
626 importedDevice = false;
627 importedCommandQueue = false;
628
629 QDxgiVSyncService::instance()->derefAdapter(adapterLuid);
630}
631
632QRhi::AdapterList QRhiD3D12::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
633{
634 LUID requestedLuid = {};
635 if (nativeHandles) {
636 QRhiD3D12NativeHandles *h = static_cast<QRhiD3D12NativeHandles *>(nativeHandles);
637 const LUID adapterLuid = { h->adapterLuidLow, h->adapterLuidHigh };
638 if (adapterLuid.LowPart || adapterLuid.HighPart)
639 requestedLuid = adapterLuid;
640 }
641
642 IDXGIFactory2 *dxgi = nullptr;
643 if (FAILED(CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgi))))
644 return {};
645
646 QRhi::AdapterList list;
647 IDXGIAdapter1 *adapter;
648 for (int adapterIndex = 0; dxgi->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
649 DXGI_ADAPTER_DESC1 desc;
650 adapter->GetDesc1(&desc);
651 adapter->Release();
652 if (requestedLuid.LowPart || requestedLuid.HighPart) {
653 if (desc.AdapterLuid.LowPart != requestedLuid.LowPart
654 || desc.AdapterLuid.HighPart != requestedLuid.HighPart)
655 {
656 continue;
657 }
658 }
659 QD3D12Adapter *a = new QD3D12Adapter;
660 a->luid = desc.AdapterLuid;
661 QRhiD3D::fillDriverInfo(&a->adapterInfo, desc);
662 list.append(a);
663 }
664
665 dxgi->Release();
666 return list;
667}
668
669QRhiDriverInfo QD3D12Adapter::info() const
670{
671 return adapterInfo;
672}
673
674QList<int> QRhiD3D12::supportedSampleCounts() const
675{
676 return { 1, 2, 4, 8 };
677}
678
679QList<QSize> QRhiD3D12::supportedShadingRates(int sampleCount) const
680{
681 QList<QSize> sizes;
682 switch (sampleCount) {
683 case 0:
684 case 1:
685 if (caps.vrsAdditionalRates) {
686 sizes.append(QSize(4, 4));
687 sizes.append(QSize(4, 2));
688 sizes.append(QSize(2, 4));
689 }
690 sizes.append(QSize(2, 2));
691 sizes.append(QSize(2, 1));
692 sizes.append(QSize(1, 2));
693 break;
694 case 2:
695 if (caps.vrsAdditionalRates)
696 sizes.append(QSize(2, 4));
697 sizes.append(QSize(2, 2));
698 sizes.append(QSize(2, 1));
699 sizes.append(QSize(1, 2));
700 break;
701 case 4:
702 sizes.append(QSize(2, 2));
703 sizes.append(QSize(2, 1));
704 sizes.append(QSize(1, 2));
705 break;
706 default:
707 break;
708 }
709 sizes.append(QSize(1, 1));
710 return sizes;
711}
712
713QRhiSwapChain *QRhiD3D12::createSwapChain()
714{
715 return new QD3D12SwapChain(this);
716}
717
718QRhiBuffer *QRhiD3D12::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
719{
720 return new QD3D12Buffer(this, type, usage, size);
721}
722
723int QRhiD3D12::ubufAlignment() const
724{
725 return D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; // 256
726}
727
728bool QRhiD3D12::isYUpInFramebuffer() const
729{
730 return false;
731}
732
733bool QRhiD3D12::isYUpInNDC() const
734{
735 return true;
736}
737
738bool QRhiD3D12::isClipDepthZeroToOne() const
739{
740 return true;
741}
742
743QMatrix4x4 QRhiD3D12::clipSpaceCorrMatrix() const
744{
745 // Like with Vulkan, but Y is already good.
746
747 static QMatrix4x4 m;
748 if (m.isIdentity()) {
749 // NB the ctor takes row-major
750 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
751 0.0f, 1.0f, 0.0f, 0.0f,
752 0.0f, 0.0f, 0.5f, 0.5f,
753 0.0f, 0.0f, 0.0f, 1.0f);
754 }
755 return m;
756}
757
758bool QRhiD3D12::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
759{
760 Q_UNUSED(flags);
761
762 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12)
763 return false;
764
765 return true;
766}
767
768bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
769{
770 switch (feature) {
771 case QRhi::MultisampleTexture:
772 return true;
773 case QRhi::MultisampleRenderBuffer:
774 return true;
775 case QRhi::DebugMarkers:
776#ifdef QRHI_D3D12_HAS_OLD_PIX
777 return true;
778#else
779 return false;
780#endif
781 case QRhi::Timestamps:
782 return true;
783 case QRhi::Instancing:
784 return true;
785 case QRhi::CustomInstanceStepRate:
786 return true;
787 case QRhi::PrimitiveRestart:
788 return true;
789 case QRhi::NonDynamicUniformBuffers:
790 return false;
791 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
792 return true;
793 case QRhi::NPOTTextureRepeat:
794 return true;
795 case QRhi::RedOrAlpha8IsRed:
796 return true;
797 case QRhi::ElementIndexUint:
798 return true;
799 case QRhi::Compute:
800 return true;
801 case QRhi::WideLines:
802 return false;
803 case QRhi::VertexShaderPointSize:
804 return false;
805 case QRhi::BaseVertex:
806 return true;
807 case QRhi::BaseInstance:
808 return true;
809 case QRhi::TriangleFanTopology:
810 return false;
811 case QRhi::ReadBackNonUniformBuffer:
812 return true;
813 case QRhi::ReadBackNonBaseMipLevel:
814 return true;
815 case QRhi::TexelFetch:
816 return true;
817 case QRhi::RenderToNonBaseMipLevel:
818 return true;
819 case QRhi::IntAttributes:
820 return true;
821 case QRhi::ScreenSpaceDerivatives:
822 return true;
823 case QRhi::ReadBackAnyTextureFormat:
824 return true;
825 case QRhi::PipelineCacheDataLoadSave:
826 return false; // ###
827 case QRhi::ImageDataStride:
828 return true;
829 case QRhi::RenderBufferImport:
830 return false;
831 case QRhi::ThreeDimensionalTextures:
832 return true;
833 case QRhi::RenderTo3DTextureSlice:
834 return true;
835 case QRhi::TextureArrays:
836 return true;
837 case QRhi::Tessellation:
838 return true;
839 case QRhi::GeometryShader:
840 return true;
841 case QRhi::TextureArrayRange:
842 return true;
843 case QRhi::NonFillPolygonMode:
844 return true;
845 case QRhi::OneDimensionalTextures:
846 return true;
847 case QRhi::OneDimensionalTextureMipmaps:
848 return false; // we generate mipmaps ourselves with compute and this is not implemented
849 case QRhi::HalfAttributes:
850 return true;
851 case QRhi::RenderToOneDimensionalTexture:
852 return true;
853 case QRhi::ThreeDimensionalTextureMipmaps:
854 return true;
855 case QRhi::MultiView:
856 return caps.multiView;
857 case QRhi::TextureViewFormat:
858 return caps.textureViewFormat;
859 case QRhi::ResolveDepthStencil:
860 // there is no Multisample Resolve support for depth/stencil formats
861 // https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
862 return false;
863 case QRhi::VariableRateShading:
864 return caps.vrs;
865 case QRhi::VariableRateShadingMap:
866 case QRhi::VariableRateShadingMapWithTexture:
867 return caps.vrsMap;
868 case QRhi::PerRenderTargetBlending:
869 case QRhi::SampleVariables:
870 return true;
871 case QRhi::InstanceIndexIncludesBaseInstance:
872 return false;
873 case QRhi::DepthClamp:
874 return true;
875 }
876 return false;
877}
878
879int QRhiD3D12::resourceLimit(QRhi::ResourceLimit limit) const
880{
881 switch (limit) {
882 case QRhi::TextureSizeMin:
883 return 1;
884 case QRhi::TextureSizeMax:
885 return 16384;
886 case QRhi::MaxColorAttachments:
887 return 8;
888 case QRhi::FramesInFlight:
889 return QD3D12_FRAMES_IN_FLIGHT;
890 case QRhi::MaxAsyncReadbackFrames:
891 return QD3D12_FRAMES_IN_FLIGHT;
892 case QRhi::MaxThreadGroupsPerDimension:
893 return 65535;
894 case QRhi::MaxThreadsPerThreadGroup:
895 return 1024;
896 case QRhi::MaxThreadGroupX:
897 return 1024;
898 case QRhi::MaxThreadGroupY:
899 return 1024;
900 case QRhi::MaxThreadGroupZ:
901 return 1024;
902 case QRhi::TextureArraySizeMax:
903 return 2048;
904 case QRhi::MaxUniformBufferRange:
905 return 65536;
906 case QRhi::MaxVertexInputs:
907 return 32;
908 case QRhi::MaxVertexOutputs:
909 return 32;
910 case QRhi::ShadingRateImageTileSize:
911 return shadingRateImageTileSize;
912 }
913 return 0;
914}
915
916const QRhiNativeHandles *QRhiD3D12::nativeHandles()
917{
918 return &nativeHandlesStruct;
919}
920
921QRhiDriverInfo QRhiD3D12::driverInfo() const
922{
923 return driverInfoStruct;
924}
925
926QRhiStats QRhiD3D12::statistics()
927{
928 QRhiStats result;
929 result.totalPipelineCreationTime = totalPipelineCreationTime();
930
931 D3D12MA::Budget budgets[2]; // [gpu, system] with discreet GPU or [shared, nothing] with UMA
932 vma.getBudget(&budgets[0], &budgets[1]);
933 for (int i = 0; i < 2; ++i) {
934 const D3D12MA::Statistics &stats(budgets[i].Stats);
935 result.blockCount += stats.BlockCount;
936 result.allocCount += stats.AllocationCount;
937 result.usedBytes += stats.AllocationBytes;
938 result.unusedBytes += stats.BlockBytes - stats.AllocationBytes;
939 result.totalUsageBytes += budgets[i].UsageBytes;
940 }
941
942 return result;
943}
944
945bool QRhiD3D12::makeThreadLocalNativeContextCurrent()
946{
947 // not applicable
948 return false;
949}
950
951void QRhiD3D12::setQueueSubmitParams(QRhiNativeHandles *)
952{
953 // not applicable
954}
955
956void QRhiD3D12::releaseCachedResources()
957{
958 shaderBytecodeCache.data.clear();
959}
960
961bool QRhiD3D12::isDeviceLost() const
962{
963 return deviceLost;
964}
965
966QByteArray QRhiD3D12::pipelineCacheData()
967{
968 return {};
969}
970
971void QRhiD3D12::setPipelineCacheData(const QByteArray &data)
972{
973 Q_UNUSED(data);
974}
975
976QRhiRenderBuffer *QRhiD3D12::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
977 int sampleCount, QRhiRenderBuffer::Flags flags,
978 QRhiTexture::Format backingFormatHint)
979{
980 return new QD3D12RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
981}
982
983QRhiTexture *QRhiD3D12::createTexture(QRhiTexture::Format format,
984 const QSize &pixelSize, int depth, int arraySize,
985 int sampleCount, QRhiTexture::Flags flags)
986{
987 return new QD3D12Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
988}
989
990QRhiSampler *QRhiD3D12::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
991 QRhiSampler::Filter mipmapMode,
992 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
993{
994 return new QD3D12Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
995}
996
997QRhiTextureRenderTarget *QRhiD3D12::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
998 QRhiTextureRenderTarget::Flags flags)
999{
1000 return new QD3D12TextureRenderTarget(this, desc, flags);
1001}
1002
1003QRhiShadingRateMap *QRhiD3D12::createShadingRateMap()
1004{
1005 return new QD3D12ShadingRateMap(this);
1006}
1007
1008QRhiGraphicsPipeline *QRhiD3D12::createGraphicsPipeline()
1009{
1010 return new QD3D12GraphicsPipeline(this);
1011}
1012
1013QRhiComputePipeline *QRhiD3D12::createComputePipeline()
1014{
1015 return new QD3D12ComputePipeline(this);
1016}
1017
1018QRhiShaderResourceBindings *QRhiD3D12::createShaderResourceBindings()
1019{
1020 return new QD3D12ShaderResourceBindings(this);
1021}
1022
1023void QRhiD3D12::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
1024{
1025 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1026 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1027 QD3D12GraphicsPipeline *psD = QRHI_RES(QD3D12GraphicsPipeline, ps);
1028 const bool pipelineChanged = cbD->currentGraphicsPipeline != psD || cbD->currentPipelineGeneration != psD->generation;
1029
1030 if (pipelineChanged) {
1031 cbD->currentGraphicsPipeline = psD;
1032 cbD->currentComputePipeline = nullptr;
1033 cbD->currentPipelineGeneration = psD->generation;
1034
1035 if (QD3D12Pipeline *pipeline = pipelinePool.lookupRef(psD->handle)) {
1036 Q_ASSERT(pipeline->type == QD3D12Pipeline::Graphics);
1037 cbD->cmdList->SetPipelineState(pipeline->pso);
1038 if (QD3D12RootSignature *rs = rootSignaturePool.lookupRef(psD->rootSigHandle))
1039 cbD->cmdList->SetGraphicsRootSignature(rs->rootSig);
1040 }
1041
1042 cbD->cmdList->IASetPrimitiveTopology(psD->topology);
1043
1044 if (psD->viewInstanceMask)
1045 cbD->cmdList->SetViewInstanceMask(psD->viewInstanceMask);
1046 }
1047}
1048
1049void QD3D12CommandBuffer::visitUniformBuffer(QD3D12Stage s,
1050 const QRhiShaderResourceBinding::Data::UniformBufferData &d,
1051 int,
1052 int binding,
1053 int dynamicOffsetCount,
1054 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1055{
1056 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf);
1057 quint32 offset = d.offset;
1058 if (d.hasDynamicOffset) {
1059 for (int i = 0; i < dynamicOffsetCount; ++i) {
1060 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1061 if (dynOfs.first == binding) {
1062 Q_ASSERT(aligned(dynOfs.second, 256u) == dynOfs.second);
1063 offset += dynOfs.second;
1064 }
1065 }
1066 }
1067 QRHI_RES_RHI(QRhiD3D12);
1068 visitorData.cbufs[s].append({ bufD->handles[rhiD->currentFrameSlot], offset });
1069}
1070
1071void QD3D12CommandBuffer::visitTexture(QD3D12Stage s,
1072 const QRhiShaderResourceBinding::TextureAndSampler &d,
1073 int)
1074{
1075 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex);
1076 visitorData.srvs[s].append(texD->srv);
1077}
1078
1079void QD3D12CommandBuffer::visitSampler(QD3D12Stage s,
1080 const QRhiShaderResourceBinding::TextureAndSampler &d,
1081 int)
1082{
1083 QD3D12Sampler *samplerD = QRHI_RES(QD3D12Sampler, d.sampler);
1084 visitorData.samplers[s].append(samplerD->lookupOrCreateShaderVisibleDescriptor());
1085}
1086
1087void QD3D12CommandBuffer::visitStorageBuffer(QD3D12Stage s,
1088 const QRhiShaderResourceBinding::Data::StorageBufferData &d,
1089 QD3D12ShaderResourceVisitor::StorageOp,
1090 int)
1091{
1092 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf);
1093 // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
1094 D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
1095 uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
1096 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
1097 uavDesc.Buffer.FirstElement = d.offset / 4;
1098 uavDesc.Buffer.NumElements = aligned(bufD->m_size - d.offset, 4u) / 4;
1099 uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
1100 visitorData.uavs[s].append({ bufD->handles[0], uavDesc });
1101}
1102
1103void QD3D12CommandBuffer::visitStorageImage(QD3D12Stage s,
1104 const QRhiShaderResourceBinding::Data::StorageImageData &d,
1105 QD3D12ShaderResourceVisitor::StorageOp,
1106 int)
1107{
1108 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex);
1109 const bool isCube = texD->m_flags.testFlag(QRhiTexture::CubeMap);
1110 const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
1111 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
1112 D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
1113 uavDesc.Format = texD->rtFormat;
1114 if (isCube) {
1115 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
1116 uavDesc.Texture2DArray.MipSlice = UINT(d.level);
1117 uavDesc.Texture2DArray.FirstArraySlice = 0;
1118 uavDesc.Texture2DArray.ArraySize = 6;
1119 } else if (isArray) {
1120 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
1121 uavDesc.Texture2DArray.MipSlice = UINT(d.level);
1122 uavDesc.Texture2DArray.FirstArraySlice = 0;
1123 uavDesc.Texture2DArray.ArraySize = UINT(qMax(0, texD->m_arraySize));
1124 } else if (is3D) {
1125 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
1126 uavDesc.Texture3D.MipSlice = UINT(d.level);
1127 uavDesc.Texture3D.WSize = UINT(-1);
1128 } else {
1129 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
1130 uavDesc.Texture2D.MipSlice = UINT(d.level);
1131 }
1132 visitorData.uavs[s].append({ texD->handle, uavDesc });
1133}
1134
1135void QRhiD3D12::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
1136 int dynamicOffsetCount,
1137 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1138{
1139 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1140 Q_ASSERT(cbD->recordingPass != QD3D12CommandBuffer::NoPass);
1141 QD3D12GraphicsPipeline *gfxPsD = QRHI_RES(QD3D12GraphicsPipeline, cbD->currentGraphicsPipeline);
1142 QD3D12ComputePipeline *compPsD = QRHI_RES(QD3D12ComputePipeline, cbD->currentComputePipeline);
1143
1144 if (!srb) {
1145 if (gfxPsD)
1146 srb = gfxPsD->m_shaderResourceBindings;
1147 else
1148 srb = compPsD->m_shaderResourceBindings;
1149 }
1150
1151 QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, srb);
1152
1153 bool pipelineChanged = false;
1154 if (gfxPsD) {
1155 pipelineChanged = srbD->lastUsedGraphicsPipeline != gfxPsD;
1156 srbD->lastUsedGraphicsPipeline = gfxPsD;
1157 } else {
1158 pipelineChanged = srbD->lastUsedComputePipeline != compPsD;
1159 srbD->lastUsedComputePipeline = compPsD;
1160 }
1161
1162 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
1163 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings[i]);
1164 switch (b->type) {
1165 case QRhiShaderResourceBinding::UniformBuffer:
1166 {
1167 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, b->u.ubuf.buf);
1168 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1169 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1170 bufD->executeHostWritesForFrameSlot(currentFrameSlot);
1171 }
1172 break;
1173 case QRhiShaderResourceBinding::SampledTexture:
1174 case QRhiShaderResourceBinding::Texture:
1175 case QRhiShaderResourceBinding::Sampler:
1176 {
1177 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1178 for (int elem = 0; elem < data->count; ++elem) {
1179 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, data->texSamplers[elem].tex);
1180 QD3D12Sampler *samplerD = QRHI_RES(QD3D12Sampler, data->texSamplers[elem].sampler);
1181 // We use the same code path for both combined and separate
1182 // images and samplers, so tex or sampler (but not both) can be
1183 // null here.
1184 Q_ASSERT(texD || samplerD);
1185 if (texD) {
1186 UINT state = 0;
1187 if (b->stage == QRhiShaderResourceBinding::FragmentStage) {
1188 state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1189 } else if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
1190 state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1191 } else {
1192 state = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
1193 }
1194 barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATES(state));
1195 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1196 }
1197 }
1198 }
1199 break;
1200 case QRhiShaderResourceBinding::ImageLoad:
1201 case QRhiShaderResourceBinding::ImageStore:
1202 case QRhiShaderResourceBinding::ImageLoadStore:
1203 {
1204 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, b->u.simage.tex);
1205 if (QD3D12Resource *res = resourcePool.lookupRef(texD->handle)) {
1206 if (res->uavUsage) {
1207 if (res->uavUsage & QD3D12Resource::UavUsageWrite) {
1208 // RaW or WaW
1209 barrierGen.enqueueUavBarrier(cbD, texD->handle);
1210 } else {
1211 if (b->type == QRhiShaderResourceBinding::ImageStore
1212 || b->type == QRhiShaderResourceBinding::ImageLoadStore)
1213 {
1214 // WaR or WaW
1215 barrierGen.enqueueUavBarrier(cbD, texD->handle);
1216 }
1217 }
1218 }
1219 res->uavUsage = 0;
1220 if (b->type == QRhiShaderResourceBinding::ImageLoad || b->type == QRhiShaderResourceBinding::ImageLoadStore)
1221 res->uavUsage |= QD3D12Resource::UavUsageRead;
1222 if (b->type == QRhiShaderResourceBinding::ImageStore || b->type == QRhiShaderResourceBinding::ImageLoadStore)
1223 res->uavUsage |= QD3D12Resource::UavUsageWrite;
1224 barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
1225 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1226 }
1227 }
1228 break;
1229 case QRhiShaderResourceBinding::BufferLoad:
1230 case QRhiShaderResourceBinding::BufferStore:
1231 case QRhiShaderResourceBinding::BufferLoadStore:
1232 {
1233 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, b->u.sbuf.buf);
1234 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1235 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1236 if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
1237 if (res->uavUsage) {
1238 if (res->uavUsage & QD3D12Resource::UavUsageWrite) {
1239 // RaW or WaW
1240 barrierGen.enqueueUavBarrier(cbD, bufD->handles[0]);
1241 } else {
1242 if (b->type == QRhiShaderResourceBinding::BufferStore
1243 || b->type == QRhiShaderResourceBinding::BufferLoadStore)
1244 {
1245 // WaR or WaW
1246 barrierGen.enqueueUavBarrier(cbD, bufD->handles[0]);
1247 }
1248 }
1249 }
1250 res->uavUsage = 0;
1251 if (b->type == QRhiShaderResourceBinding::BufferLoad || b->type == QRhiShaderResourceBinding::BufferLoadStore)
1252 res->uavUsage |= QD3D12Resource::UavUsageRead;
1253 if (b->type == QRhiShaderResourceBinding::BufferStore || b->type == QRhiShaderResourceBinding::BufferLoadStore)
1254 res->uavUsage |= QD3D12Resource::UavUsageWrite;
1255 barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
1256 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1257 }
1258 }
1259 break;
1260 }
1261 }
1262
1263 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
1264 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1265
1266 if (pipelineChanged || srbChanged || srbRebuilt || srbD->hasDynamicOffset) {
1267 const QD3D12ShaderStageData *stageData = gfxPsD ? gfxPsD->stageData.data() : &compPsD->stageData;
1268
1269 // The order of root parameters must match
1270 // QD3D12ShaderResourceBindings::createRootSignature(), meaning the
1271 // logic below must mirror that function (uniform buffers first etc.)
1272
1273 QD3D12ShaderResourceVisitor visitor(srbD, stageData, gfxPsD ? 5 : 1);
1274
1275 QD3D12CommandBuffer::VisitorData &visitorData(cbD->visitorData);
1276 visitorData = {};
1277
1278 using namespace std::placeholders;
1279 visitor.uniformBuffer = std::bind(&QD3D12CommandBuffer::visitUniformBuffer, cbD, _1, _2, _3, _4, dynamicOffsetCount, dynamicOffsets);
1280 visitor.texture = std::bind(&QD3D12CommandBuffer::visitTexture, cbD, _1, _2, _3);
1281 visitor.sampler = std::bind(&QD3D12CommandBuffer::visitSampler, cbD, _1, _2, _3);
1282 visitor.storageBuffer = std::bind(&QD3D12CommandBuffer::visitStorageBuffer, cbD, _1, _2, _3, _4);
1283 visitor.storageImage = std::bind(&QD3D12CommandBuffer::visitStorageImage, cbD, _1, _2, _3, _4);
1284
1285 visitor.visit();
1286
1287 quint32 cbvSrvUavCount = 0;
1288 for (int s = 0; s < 6; ++s) {
1289 // CBs use root constant buffer views, no need to count them here
1290 cbvSrvUavCount += visitorData.srvs[s].count();
1291 cbvSrvUavCount += visitorData.uavs[s].count();
1292 }
1293
1294 bool gotNewHeap = false;
1295 if (!ensureShaderVisibleDescriptorHeapCapacity(&shaderVisibleCbvSrvUavHeap,
1296 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
1297 currentFrameSlot,
1298 cbvSrvUavCount,
1299 &gotNewHeap))
1300 {
1301 return;
1302 }
1303 if (gotNewHeap) {
1304 qCDebug(QRHI_LOG_INFO, "Created new shader-visible CBV/SRV/UAV descriptor heap,"
1305 " per-frame slice size is now %u,"
1306 " if this happens frequently then that's not great.",
1307 shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[0].capacity);
1308 bindShaderVisibleHeaps(cbD);
1309 }
1310
1311 int rootParamIndex = 0;
1312 for (int s = 0; s < 6; ++s) {
1313 if (!visitorData.cbufs[s].isEmpty()) {
1314 for (int i = 0, count = visitorData.cbufs[s].count(); i < count; ++i) {
1315 const auto &cbuf(visitorData.cbufs[s][i]);
1316 if (QD3D12Resource *res = resourcePool.lookupRef(cbuf.first)) {
1317 quint32 offset = cbuf.second;
1318 D3D12_GPU_VIRTUAL_ADDRESS gpuAddr = res->resource->GetGPUVirtualAddress() + offset;
1319 if (cbD->currentGraphicsPipeline)
1320 cbD->cmdList->SetGraphicsRootConstantBufferView(rootParamIndex, gpuAddr);
1321 else
1322 cbD->cmdList->SetComputeRootConstantBufferView(rootParamIndex, gpuAddr);
1323 }
1324 rootParamIndex += 1;
1325 }
1326 }
1327 }
1328 for (int s = 0; s < 6; ++s) {
1329 if (!visitorData.srvs[s].isEmpty()) {
1330 QD3D12DescriptorHeap &gpuSrvHeap(shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot]);
1331 QD3D12Descriptor startDesc = gpuSrvHeap.get(visitorData.srvs[s].count());
1332 for (int i = 0, count = visitorData.srvs[s].count(); i < count; ++i) {
1333 const auto &srv(visitorData.srvs[s][i]);
1334 dev->CopyDescriptorsSimple(1, gpuSrvHeap.incremented(startDesc, i).cpuHandle, srv.cpuHandle,
1335 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
1336 }
1337
1338 if (cbD->currentGraphicsPipeline)
1339 cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
1340 else if (cbD->currentComputePipeline)
1341 cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
1342
1343 rootParamIndex += 1;
1344 }
1345 }
1346 for (int s = 0; s < 6; ++s) {
1347 // Samplers are one parameter / descriptor table each, and the
1348 // descriptor is from the shader visible sampler heap already.
1349 for (const QD3D12Descriptor &samplerDescriptor : visitorData.samplers[s]) {
1350 if (cbD->currentGraphicsPipeline)
1351 cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, samplerDescriptor.gpuHandle);
1352 else if (cbD->currentComputePipeline)
1353 cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, samplerDescriptor.gpuHandle);
1354
1355 rootParamIndex += 1;
1356 }
1357 }
1358 for (int s = 0; s < 6; ++s) {
1359 if (!visitorData.uavs[s].isEmpty()) {
1360 QD3D12DescriptorHeap &gpuUavHeap(shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot]);
1361 QD3D12Descriptor startDesc = gpuUavHeap.get(visitorData.uavs[s].count());
1362 for (int i = 0, count = visitorData.uavs[s].count(); i < count; ++i) {
1363 const auto &uav(visitorData.uavs[s][i]);
1364 if (QD3D12Resource *res = resourcePool.lookupRef(uav.first)) {
1365 dev->CreateUnorderedAccessView(res->resource, nullptr, &uav.second,
1366 gpuUavHeap.incremented(startDesc, i).cpuHandle);
1367 } else {
1368 dev->CreateUnorderedAccessView(nullptr, nullptr, nullptr,
1369 gpuUavHeap.incremented(startDesc, i).cpuHandle);
1370 }
1371 }
1372
1373 if (cbD->currentGraphicsPipeline)
1374 cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
1375 else if (cbD->currentComputePipeline)
1376 cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
1377
1378 rootParamIndex += 1;
1379 }
1380 }
1381
1382 if (gfxPsD) {
1383 cbD->currentGraphicsSrb = srb;
1384 cbD->currentComputeSrb = nullptr;
1385 } else {
1386 cbD->currentGraphicsSrb = nullptr;
1387 cbD->currentComputeSrb = srb;
1388 }
1389 cbD->currentSrbGeneration = srbD->generation;
1390 }
1391}
1392
1393void QRhiD3D12::setVertexInput(QRhiCommandBuffer *cb,
1394 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1395 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1396{
1397 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1398 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1399
1400 bool needsBindVBuf = false;
1401 for (int i = 0; i < bindingCount; ++i) {
1402 const int inputSlot = startBinding + i;
1403 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, bindings[i].first);
1404 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
1405 const bool isDynamic = bufD->m_type == QRhiBuffer::Dynamic;
1406 if (isDynamic)
1407 bufD->executeHostWritesForFrameSlot(currentFrameSlot);
1408
1409 if (cbD->currentVertexBuffers[inputSlot] != bufD->handles[isDynamic ? currentFrameSlot : 0]
1410 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
1411 {
1412 needsBindVBuf = true;
1413 cbD->currentVertexBuffers[inputSlot] = bufD->handles[isDynamic ? currentFrameSlot : 0];
1414 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
1415 }
1416 }
1417
1418 if (needsBindVBuf) {
1419 QVarLengthArray<D3D12_VERTEX_BUFFER_VIEW, 4> vbv;
1420 vbv.reserve(bindingCount);
1421
1422 QD3D12GraphicsPipeline *psD = cbD->currentGraphicsPipeline;
1423 const QRhiVertexInputLayout &inputLayout(psD->m_vertexInputLayout);
1424 const int inputBindingCount = inputLayout.cendBindings() - inputLayout.cbeginBindings();
1425
1426 for (int i = 0, ie = qMin(bindingCount, inputBindingCount); i != ie; ++i) {
1427 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, bindings[i].first);
1428 const QD3D12ObjectHandle handle = bufD->handles[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
1429 const quint32 offset = bindings[i].second;
1430 const quint32 stride = inputLayout.bindingAt(i)->stride();
1431
1432 if (bufD->m_type != QRhiBuffer::Dynamic) {
1433 barrierGen.addTransitionBarrier(handle, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
1434 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1435 }
1436
1437 if (QD3D12Resource *res = resourcePool.lookupRef(handle)) {
1438 vbv.append({
1439 res->resource->GetGPUVirtualAddress() + offset,
1440 UINT(res->desc.Width - offset),
1441 stride
1442 });
1443 }
1444 }
1445
1446 cbD->cmdList->IASetVertexBuffers(UINT(startBinding), vbv.count(), vbv.constData());
1447 }
1448
1449 if (indexBuf) {
1450 QD3D12Buffer *ibufD = QRHI_RES(QD3D12Buffer, indexBuf);
1451 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
1452 const bool isDynamic = ibufD->m_type == QRhiBuffer::Dynamic;
1453 if (isDynamic)
1454 ibufD->executeHostWritesForFrameSlot(currentFrameSlot);
1455
1456 const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
1457 : DXGI_FORMAT_R32_UINT;
1458 if (cbD->currentIndexBuffer != ibufD->handles[isDynamic ? currentFrameSlot : 0]
1459 || cbD->currentIndexOffset != indexOffset
1460 || cbD->currentIndexFormat != dxgiFormat)
1461 {
1462 cbD->currentIndexBuffer = ibufD->handles[isDynamic ? currentFrameSlot : 0];
1463 cbD->currentIndexOffset = indexOffset;
1464 cbD->currentIndexFormat = dxgiFormat;
1465
1466 if (ibufD->m_type != QRhiBuffer::Dynamic) {
1467 barrierGen.addTransitionBarrier(cbD->currentIndexBuffer, D3D12_RESOURCE_STATE_INDEX_BUFFER);
1468 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1469 }
1470
1471 if (QD3D12Resource *res = resourcePool.lookupRef(cbD->currentIndexBuffer)) {
1472 const D3D12_INDEX_BUFFER_VIEW ibv = {
1473 res->resource->GetGPUVirtualAddress() + indexOffset,
1474 UINT(res->desc.Width - indexOffset),
1475 dxgiFormat
1476 };
1477 cbD->cmdList->IASetIndexBuffer(&ibv);
1478 }
1479 }
1480 }
1481}
1482
1483void QRhiD3D12::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
1484{
1485 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1486 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1487 Q_ASSERT(cbD->currentTarget);
1488 const QSize outputSize = cbD->currentTarget->pixelSize();
1489
1490 // D3D expects top-left, QRhiViewport is bottom-left
1491 float x, y, w, h;
1492 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1493 return;
1494
1495 D3D12_VIEWPORT v;
1496 v.TopLeftX = x;
1497 v.TopLeftY = y;
1498 v.Width = w;
1499 v.Height = h;
1500 v.MinDepth = viewport.minDepth();
1501 v.MaxDepth = viewport.maxDepth();
1502 cbD->cmdList->RSSetViewports(1, &v);
1503
1504 if (cbD->currentGraphicsPipeline
1505 && !cbD->currentGraphicsPipeline->flags().testFlag(QRhiGraphicsPipeline::UsesScissor))
1506 {
1507 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1508 D3D12_RECT r;
1509 r.left = x;
1510 r.top = y;
1511 // right and bottom are exclusive
1512 r.right = x + w;
1513 r.bottom = y + h;
1514 cbD->cmdList->RSSetScissorRects(1, &r);
1515 }
1516}
1517
1518void QRhiD3D12::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
1519{
1520 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1521 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1522 Q_ASSERT(cbD->currentTarget);
1523 const QSize outputSize = cbD->currentTarget->pixelSize();
1524
1525 // D3D expects top-left, QRhiScissor is bottom-left
1526 int x, y, w, h;
1527 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1528 return;
1529
1530 D3D12_RECT r;
1531 r.left = x;
1532 r.top = y;
1533 // right and bottom are exclusive
1534 r.right = x + w;
1535 r.bottom = y + h;
1536 cbD->cmdList->RSSetScissorRects(1, &r);
1537}
1538
1539void QRhiD3D12::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
1540{
1541 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1542 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1543 float v[4] = { c.redF(), c.greenF(), c.blueF(), c.alphaF() };
1544 cbD->cmdList->OMSetBlendFactor(v);
1545}
1546
1547void QRhiD3D12::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
1548{
1549 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1550 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1551 cbD->cmdList->OMSetStencilRef(refValue);
1552}
1553
1554static inline D3D12_SHADING_RATE toD3DShadingRate(const QSize &coarsePixelSize)
1555{
1556 if (coarsePixelSize == QSize(1, 2))
1557 return D3D12_SHADING_RATE_1X2;
1558 if (coarsePixelSize == QSize(2, 1))
1559 return D3D12_SHADING_RATE_2X1;
1560 if (coarsePixelSize == QSize(2, 2))
1561 return D3D12_SHADING_RATE_2X2;
1562 if (coarsePixelSize == QSize(2, 4))
1563 return D3D12_SHADING_RATE_2X4;
1564 if (coarsePixelSize == QSize(4, 2))
1565 return D3D12_SHADING_RATE_4X2;
1566 if (coarsePixelSize == QSize(4, 4))
1567 return D3D12_SHADING_RATE_4X4;
1568 return D3D12_SHADING_RATE_1X1;
1569}
1570
1571void QRhiD3D12::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
1572{
1573 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1574 cbD->hasShadingRateSet = false;
1575
1576#ifdef QRHI_D3D12_CL5_AVAILABLE
1577 if (!caps.vrs)
1578 return;
1579
1580 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1581 const D3D12_SHADING_RATE_COMBINER combiners[] = { D3D12_SHADING_RATE_COMBINER_MAX, D3D12_SHADING_RATE_COMBINER_MAX };
1582 cbD->cmdList->RSSetShadingRate(toD3DShadingRate(coarsePixelSize), combiners);
1583 if (coarsePixelSize.width() != 1 || coarsePixelSize.height() != 1)
1584 cbD->hasShadingRateSet = true;
1585#else
1586 Q_UNUSED(cb);
1587 Q_UNUSED(coarsePixelSize);
1588 qWarning("Attempted to set ShadingRate without building Qt against a sufficiently new Windows SDK and d3d12.h. This cannot work.");
1589#endif
1590}
1591
1592void QRhiD3D12::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
1593 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1594{
1595 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1596 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1597 cbD->cmdList->DrawInstanced(vertexCount, instanceCount, firstVertex, firstInstance);
1598}
1599
1600void QRhiD3D12::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
1601 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
1602{
1603 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1604 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
1605 cbD->cmdList->DrawIndexedInstanced(indexCount, instanceCount,
1606 firstIndex, vertexOffset,
1607 firstInstance);
1608}
1609
1610void QRhiD3D12::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
1611{
1612 if (!debugMarkers)
1613 return;
1614
1615 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1616#ifdef QRHI_D3D12_HAS_OLD_PIX
1617 PIXBeginEvent(cbD->cmdList, PIX_COLOR_DEFAULT, reinterpret_cast<LPCWSTR>(QString::fromLatin1(name).utf16()));
1618#else
1619 Q_UNUSED(cbD);
1620 Q_UNUSED(name);
1621#endif
1622}
1623
1624void QRhiD3D12::debugMarkEnd(QRhiCommandBuffer *cb)
1625{
1626 if (!debugMarkers)
1627 return;
1628
1629 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1630#ifdef QRHI_D3D12_HAS_OLD_PIX
1631 PIXEndEvent(cbD->cmdList);
1632#else
1633 Q_UNUSED(cbD);
1634#endif
1635}
1636
1637void QRhiD3D12::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
1638{
1639 if (!debugMarkers)
1640 return;
1641
1642 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1643#ifdef QRHI_D3D12_HAS_OLD_PIX
1644 PIXSetMarker(cbD->cmdList, PIX_COLOR_DEFAULT, reinterpret_cast<LPCWSTR>(QString::fromLatin1(msg).utf16()));
1645#else
1646 Q_UNUSED(cbD);
1647 Q_UNUSED(msg);
1648#endif
1649}
1650
1651const QRhiNativeHandles *QRhiD3D12::nativeHandles(QRhiCommandBuffer *cb)
1652{
1653 return QRHI_RES(QD3D12CommandBuffer, cb)->nativeHandles();
1654}
1655
1656void QRhiD3D12::beginExternal(QRhiCommandBuffer *cb)
1657{
1658 Q_UNUSED(cb);
1659}
1660
1661void QRhiD3D12::endExternal(QRhiCommandBuffer *cb)
1662{
1663 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1664 cbD->resetPerPassState();
1665 bindShaderVisibleHeaps(cbD);
1666 if (cbD->currentTarget) { // could be compute, no rendertarget then
1667 QD3D12RenderTargetData *rtD = rtData(cbD->currentTarget);
1668 cbD->cmdList->OMSetRenderTargets(UINT(rtD->colorAttCount),
1669 rtD->rtv,
1670 TRUE,
1671 rtD->dsAttCount ? &rtD->dsv : nullptr);
1672 }
1673}
1674
1675double QRhiD3D12::lastCompletedGpuTime(QRhiCommandBuffer *cb)
1676{
1677 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
1678 return cbD->lastGpuTime;
1679}
1680
1681static void calculateGpuTime(QD3D12CommandBuffer *cbD,
1682 int timestampPairStartIndex,
1683 const quint8 *readbackBufPtr,
1684 quint64 timestampTicksPerSecond)
1685{
1686 const size_t byteOffset = timestampPairStartIndex * sizeof(quint64);
1687 const quint64 *p = reinterpret_cast<const quint64 *>(readbackBufPtr + byteOffset);
1688 const quint64 startTime = *p++;
1689 const quint64 endTime = *p;
1690 if (startTime < endTime) {
1691 const quint64 ticks = endTime - startTime;
1692 const double timeSec = ticks / double(timestampTicksPerSecond);
1693 cbD->lastGpuTime = timeSec;
1694 }
1695}
1696
1697QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1698{
1699 Q_UNUSED(flags);
1700
1701 QD3D12SwapChain *swapChainD = QRHI_RES(QD3D12SwapChain, swapChain);
1702 currentSwapChain = swapChainD;
1703 currentFrameSlot = swapChainD->currentFrameSlot;
1704 QD3D12SwapChain::FrameResources &fr(swapChainD->frameRes[currentFrameSlot]);
1705
1706 // We could do smarter things but mirror the Vulkan backend for now: Make
1707 // sure the previous commands for this same frame slot have finished. Do
1708 // this also for any other swapchain's commands with the same frame slot.
1709 // While this reduces concurrency in render-to-swapchain-A,
1710 // render-to-swapchain-B, repeat kind of scenarios, it keeps resource usage
1711 // safe: swapchain A starting its frame 0, followed by swapchain B starting
1712 // its own frame 0 will make B wait for A's frame 0 commands. If a resource
1713 // is written in B's frame or when B checks for pending resource releases,
1714 // that won't mess up A's in-flight commands (as they are guaranteed not to
1715 // be in flight anymore). With Qt Quick this situation cannot happen anyway
1716 // by design (one QRhi per window).
1717 for (QD3D12SwapChain *sc : std::as_const(swapchains))
1718 sc->waitCommandCompletionForFrameSlot(currentFrameSlot); // note: swapChainD->currentFrameSlot, not sc's
1719
1720 if (swapChainD->frameLatencyWaitableObject) {
1721 // only wait when endFrame() called Present(), otherwise this would become a 1 sec timeout
1722 if (swapChainD->lastFrameLatencyWaitSlot != currentFrameSlot) {
1723 WaitForSingleObjectEx(swapChainD->frameLatencyWaitableObject, 1000, true);
1724 swapChainD->lastFrameLatencyWaitSlot = currentFrameSlot;
1725 }
1726 }
1727
1728 HRESULT hr = cmdAllocators[currentFrameSlot]->Reset();
1729 if (FAILED(hr)) {
1730 qWarning("Failed to reset command allocator: %s",
1731 qPrintable(QSystemError::windowsComString(hr)));
1732 return QRhi::FrameOpError;
1733 }
1734
1735 if (!startCommandListForCurrentFrameSlot(&fr.cmdList))
1736 return QRhi::FrameOpError;
1737
1738 QD3D12CommandBuffer *cbD = &swapChainD->cbWrapper;
1739 cbD->cmdList = fr.cmdList;
1740
1741 swapChainD->rtWrapper.d.rtv[0] = swapChainD->sampleDesc.Count > 1
1742 ? swapChainD->msaaRtvs[swapChainD->currentBackBufferIndex].cpuHandle
1743 : swapChainD->rtvs[swapChainD->currentBackBufferIndex].cpuHandle;
1744
1745 swapChainD->rtWrapper.d.dsv = swapChainD->ds ? swapChainD->ds->dsv.cpuHandle
1746 : D3D12_CPU_DESCRIPTOR_HANDLE { 0 };
1747
1748 if (swapChainD->stereo) {
1749 swapChainD->rtWrapperRight.d.rtv[0] = swapChainD->sampleDesc.Count > 1
1750 ? swapChainD->msaaRtvs[swapChainD->currentBackBufferIndex].cpuHandle
1751 : swapChainD->rtvsRight[swapChainD->currentBackBufferIndex].cpuHandle;
1752
1753 swapChainD->rtWrapperRight.d.dsv =
1754 swapChainD->ds ? swapChainD->ds->dsv.cpuHandle : D3D12_CPU_DESCRIPTOR_HANDLE{ 0 };
1755 }
1756
1757
1758 // Time to release things that are marked for currentFrameSlot since due to
1759 // the wait above we know that the previous commands on the GPU for this
1760 // slot must have finished already.
1761 releaseQueue.executeDeferredReleases(currentFrameSlot);
1762
1763 // Full reset of the command buffer data.
1764 cbD->resetState();
1765
1766 // Move the head back to zero for the per-frame shader-visible descriptor heap work areas.
1767 shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
1768 // Same for the small staging area.
1769 smallStagingAreas[currentFrameSlot].head = 0;
1770
1771 bindShaderVisibleHeaps(cbD);
1772
1773 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
1774
1775 if (timestampQueryHeap.isValid() && timestampTicksPerSecond) {
1776 // Read the timestamps for the previous frame for this slot. (the
1777 // ResolveQuery() should have completed by now due to the wait above)
1778 const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
1779 calculateGpuTime(cbD,
1780 timestampPairStartIndex,
1781 timestampReadbackArea.mem.p,
1782 timestampTicksPerSecond);
1783 // Write the start timestamp for this frame for this slot.
1784 cbD->cmdList->EndQuery(timestampQueryHeap.heap,
1785 D3D12_QUERY_TYPE_TIMESTAMP,
1786 timestampPairStartIndex);
1787 }
1788
1789 QDxgiVSyncService::instance()->beginFrame(adapterLuid);
1790
1791 return QRhi::FrameOpSuccess;
1792}
1793
1794QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1795{
1796 QD3D12SwapChain *swapChainD = QRHI_RES(QD3D12SwapChain, swapChain);
1797 Q_ASSERT(currentSwapChain == swapChainD);
1798 QD3D12CommandBuffer *cbD = &swapChainD->cbWrapper;
1799
1800 QD3D12ObjectHandle backBufferResourceHandle = swapChainD->colorBuffers[swapChainD->currentBackBufferIndex];
1801 if (swapChainD->sampleDesc.Count > 1) {
1802 QD3D12ObjectHandle msaaBackBufferResourceHandle = swapChainD->msaaBuffers[swapChainD->currentBackBufferIndex];
1803 barrierGen.addTransitionBarrier(msaaBackBufferResourceHandle, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
1804 barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_RESOLVE_DEST);
1805 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1806 const QD3D12Resource *src = resourcePool.lookupRef(msaaBackBufferResourceHandle);
1807 const QD3D12Resource *dst = resourcePool.lookupRef(backBufferResourceHandle);
1808 if (src && dst)
1809 cbD->cmdList->ResolveSubresource(dst->resource, 0, src->resource, 0, swapChainD->colorFormat);
1810 }
1811
1812 barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_PRESENT);
1813 barrierGen.enqueueBufferedTransitionBarriers(cbD);
1814
1815 if (timestampQueryHeap.isValid()) {
1816 const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
1817 cbD->cmdList->EndQuery(timestampQueryHeap.heap,
1818 D3D12_QUERY_TYPE_TIMESTAMP,
1819 timestampPairStartIndex + 1);
1820 cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap,
1821 D3D12_QUERY_TYPE_TIMESTAMP,
1822 timestampPairStartIndex,
1823 2,
1824 timestampReadbackArea.mem.buffer,
1825 timestampPairStartIndex * sizeof(quint64));
1826 }
1827
1828 D3D12GraphicsCommandList *cmdList = cbD->cmdList;
1829 HRESULT hr = cmdList->Close();
1830 if (FAILED(hr)) {
1831 qWarning("Failed to close command list: %s",
1832 qPrintable(QSystemError::windowsComString(hr)));
1833 return QRhi::FrameOpError;
1834 }
1835
1836 ID3D12CommandList *execList[] = { cmdList };
1837 cmdQueue->ExecuteCommandLists(1, execList);
1838
1839 if (!flags.testFlag(QRhi::SkipPresent)) {
1840 UINT presentFlags = 0;
1841 if (swapChainD->swapInterval == 0
1842 && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
1843 {
1844 presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
1845 }
1846 if (!swapChainD->swapChain) {
1847 qWarning("Failed to present, no swapchain");
1848 return QRhi::FrameOpError;
1849 }
1850 HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
1851 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
1852 qWarning("Device loss detected in Present()");
1853 deviceLost = true;
1854 return QRhi::FrameOpDeviceLost;
1855 } else if (FAILED(hr)) {
1856 qWarning("Failed to present: %s", qPrintable(QSystemError::windowsComString(hr)));
1857 return QRhi::FrameOpError;
1858 }
1859
1860 if (dcompDevice && swapChainD->dcompTarget && swapChainD->dcompVisual)
1861 dcompDevice->Commit();
1862 }
1863
1864 swapChainD->addCommandCompletionSignalForCurrentFrameSlot();
1865
1866 // NB! The deferred-release mechanism here differs from the older QRhi
1867 // backends. There is no lastActiveFrameSlot tracking. Instead,
1868 // currentFrameSlot is written to the registered entries now, and so the
1869 // resources will get released in the frames_in_flight'th beginFrame()
1870 // counting starting from now.
1871 releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
1872
1873 if (!flags.testFlag(QRhi::SkipPresent)) {
1874 // Only move to the next slot if we presented. Otherwise will block and
1875 // wait for completion in the next beginFrame already, but SkipPresent
1876 // should be infrequent anyway.
1877 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D12_FRAMES_IN_FLIGHT;
1878 swapChainD->currentBackBufferIndex = swapChainD->swapChain->GetCurrentBackBufferIndex();
1879 }
1880
1881 currentSwapChain = nullptr;
1882 return QRhi::FrameOpSuccess;
1883}
1884
1885QRhi::FrameOpResult QRhiD3D12::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
1886{
1887 Q_UNUSED(flags);
1888
1889 // Switch to the next slot manually. Swapchains do not know about this
1890 // which is good. So for example an onscreen, onscreen, offscreen,
1891 // onscreen, onscreen, onscreen sequence of frames leads to 0, 1, 0, 0, 1,
1892 // 0. (no strict alternation anymore) But this is not different from what
1893 // happens when multiple swapchains are involved. Offscreen frames are
1894 // synchronous anyway in the sense that they wait for execution to complete
1895 // in endOffscreenFrame, so no resources used in that frame are busy
1896 // anymore in the next frame.
1897
1898 currentFrameSlot = (currentFrameSlot + 1) % QD3D12_FRAMES_IN_FLIGHT;
1899
1900 for (QD3D12SwapChain *sc : std::as_const(swapchains))
1901 sc->waitCommandCompletionForFrameSlot(currentFrameSlot); // note: not sc's currentFrameSlot
1902
1903 HRESULT hr = cmdAllocators[currentFrameSlot]->Reset();
1904 if (FAILED(hr)) {
1905 qWarning("Failed to reset command allocator: %s",
1906 qPrintable(QSystemError::windowsComString(hr)));
1907 return QRhi::FrameOpError;
1908 }
1909
1910 if (!offscreenCb[currentFrameSlot])
1911 offscreenCb[currentFrameSlot] = new QD3D12CommandBuffer(this);
1912 QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot];
1913 if (!startCommandListForCurrentFrameSlot(&cbD->cmdList))
1914 return QRhi::FrameOpError;
1915
1916 releaseQueue.executeDeferredReleases(currentFrameSlot);
1917 cbD->resetState();
1918 shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
1919 smallStagingAreas[currentFrameSlot].head = 0;
1920
1921 bindShaderVisibleHeaps(cbD);
1922
1923 if (timestampQueryHeap.isValid() && timestampTicksPerSecond) {
1924 cbD->cmdList->EndQuery(timestampQueryHeap.heap,
1925 D3D12_QUERY_TYPE_TIMESTAMP,
1926 currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT);
1927 }
1928
1929 offscreenActive = true;
1930 *cb = cbD;
1931
1932 return QRhi::FrameOpSuccess;
1933}
1934
1935QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags)
1936{
1937 Q_UNUSED(flags);
1938 Q_ASSERT(offscreenActive);
1939 offscreenActive = false;
1940
1941 QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot];
1942 if (timestampQueryHeap.isValid()) {
1943 const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
1944 cbD->cmdList->EndQuery(timestampQueryHeap.heap,
1945 D3D12_QUERY_TYPE_TIMESTAMP,
1946 timestampPairStartIndex + 1);
1947 cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap,
1948 D3D12_QUERY_TYPE_TIMESTAMP,
1949 timestampPairStartIndex,
1950 2,
1951 timestampReadbackArea.mem.buffer,
1952 timestampPairStartIndex * sizeof(quint64));
1953 }
1954
1955 D3D12GraphicsCommandList *cmdList = cbD->cmdList;
1956 HRESULT hr = cmdList->Close();
1957 if (FAILED(hr)) {
1958 qWarning("Failed to close command list: %s",
1959 qPrintable(QSystemError::windowsComString(hr)));
1960 return QRhi::FrameOpError;
1961 }
1962
1963 ID3D12CommandList *execList[] = { cmdList };
1964 cmdQueue->ExecuteCommandLists(1, execList);
1965
1966 releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
1967
1968 // wait for completion
1969 waitGpu();
1970
1971 // Here we know that executing the host-side reads for this (or any
1972 // previous) frame is safe since we waited for completion above.
1973 finishActiveReadbacks(true);
1974
1975 // the timestamp query results should be available too, given the wait
1976 if (timestampQueryHeap.isValid()) {
1977 calculateGpuTime(cbD,
1978 currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT,
1979 timestampReadbackArea.mem.p,
1980 timestampTicksPerSecond);
1981 }
1982
1983 return QRhi::FrameOpSuccess;
1984}
1985
1986QRhi::FrameOpResult QRhiD3D12::finish()
1987{
1988 QD3D12CommandBuffer *cbD = nullptr;
1989 if (inFrame) {
1990 if (offscreenActive) {
1991 Q_ASSERT(!currentSwapChain);
1992 cbD = offscreenCb[currentFrameSlot];
1993 } else {
1994 Q_ASSERT(currentSwapChain);
1995 cbD = &currentSwapChain->cbWrapper;
1996 }
1997 if (!cbD)
1998 return QRhi::FrameOpError;
1999
2000 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
2001
2002 D3D12GraphicsCommandList *cmdList = cbD->cmdList;
2003 HRESULT hr = cmdList->Close();
2004 if (FAILED(hr)) {
2005 qWarning("Failed to close command list: %s",
2006 qPrintable(QSystemError::windowsComString(hr)));
2007 return QRhi::FrameOpError;
2008 }
2009
2010 ID3D12CommandList *execList[] = { cmdList };
2011 cmdQueue->ExecuteCommandLists(1, execList);
2012
2013 releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
2014 }
2015
2016 // full blocking wait for everything, frame slots do not matter now
2017 waitGpu();
2018
2019 if (inFrame) {
2020 HRESULT hr = cmdAllocators[currentFrameSlot]->Reset();
2021 if (FAILED(hr)) {
2022 qWarning("Failed to reset command allocator: %s",
2023 qPrintable(QSystemError::windowsComString(hr)));
2024 return QRhi::FrameOpError;
2025 }
2026
2027 if (!startCommandListForCurrentFrameSlot(&cbD->cmdList))
2028 return QRhi::FrameOpError;
2029
2030 cbD->resetState();
2031
2032 shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
2033 smallStagingAreas[currentFrameSlot].head = 0;
2034
2035 bindShaderVisibleHeaps(cbD);
2036 }
2037
2038 releaseQueue.releaseAll();
2039 finishActiveReadbacks(true);
2040
2041 return QRhi::FrameOpSuccess;
2042}
2043
2044void QRhiD3D12::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2045{
2046 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2047 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
2048 enqueueResourceUpdates(cbD, resourceUpdates);
2049}
2050
2051void QRhiD3D12::beginPass(QRhiCommandBuffer *cb,
2052 QRhiRenderTarget *rt,
2053 const QColor &colorClearValue,
2054 const QRhiDepthStencilClearValue &depthStencilClearValue,
2055 QRhiResourceUpdateBatch *resourceUpdates,
2056 QRhiCommandBuffer::BeginPassFlags)
2057{
2058 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2059 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
2060
2061 if (resourceUpdates)
2062 enqueueResourceUpdates(cbD, resourceUpdates);
2063
2064 QD3D12RenderTargetData *rtD = rtData(rt);
2065 bool wantsColorClear = true;
2066 bool wantsDsClear = true;
2067 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
2068 QD3D12TextureRenderTarget *rtTex = QRHI_RES(QD3D12TextureRenderTarget, rt);
2069 wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
2070 wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
2071 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D12Texture, QD3D12RenderBuffer>(rtTex->description(), rtD->currentResIdList))
2072 rtTex->create();
2073
2074 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments(); it != itEnd; ++it) {
2075 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
2076 QD3D12Texture *resolveTexD = QRHI_RES(QD3D12Texture, it->resolveTexture());
2077 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
2078 if (texD)
2079 barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
2080 else if (rbD)
2081 barrierGen.addTransitionBarrier(rbD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
2082 if (resolveTexD)
2083 barrierGen.addTransitionBarrier(resolveTexD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
2084 }
2085 if (rtTex->m_desc.depthStencilBuffer()) {
2086 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, rtTex->m_desc.depthStencilBuffer());
2087 Q_ASSERT(rbD->m_type == QRhiRenderBuffer::DepthStencil);
2088 barrierGen.addTransitionBarrier(rbD->handle, D3D12_RESOURCE_STATE_DEPTH_WRITE);
2089 } else if (rtTex->m_desc.depthTexture()) {
2090 QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, rtTex->m_desc.depthTexture());
2091 barrierGen.addTransitionBarrier(depthTexD->handle, D3D12_RESOURCE_STATE_DEPTH_WRITE);
2092 }
2093 barrierGen.enqueueBufferedTransitionBarriers(cbD);
2094 } else {
2095 Q_ASSERT(currentSwapChain);
2096 barrierGen.addTransitionBarrier(currentSwapChain->sampleDesc.Count > 1
2097 ? currentSwapChain->msaaBuffers[currentSwapChain->currentBackBufferIndex]
2098 : currentSwapChain->colorBuffers[currentSwapChain->currentBackBufferIndex],
2099 D3D12_RESOURCE_STATE_RENDER_TARGET);
2100 barrierGen.enqueueBufferedTransitionBarriers(cbD);
2101 }
2102
2103 cbD->cmdList->OMSetRenderTargets(UINT(rtD->colorAttCount),
2104 rtD->rtv,
2105 TRUE,
2106 rtD->dsAttCount ? &rtD->dsv : nullptr);
2107
2108 if (rtD->colorAttCount && wantsColorClear) {
2109 float clearColor[4] = {
2110 colorClearValue.redF(),
2111 colorClearValue.greenF(),
2112 colorClearValue.blueF(),
2113 colorClearValue.alphaF()
2114 };
2115 for (int i = 0; i < rtD->colorAttCount; ++i)
2116 cbD->cmdList->ClearRenderTargetView(rtD->rtv[i], clearColor, 0, nullptr);
2117 }
2118 if (rtD->dsAttCount && wantsDsClear) {
2119 cbD->cmdList->ClearDepthStencilView(rtD->dsv,
2120 D3D12_CLEAR_FLAGS(D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL),
2121 depthStencilClearValue.depthClearValue(),
2122 UINT8(depthStencilClearValue.stencilClearValue()),
2123 0,
2124 nullptr);
2125 }
2126
2127 cbD->recordingPass = QD3D12CommandBuffer::RenderPass;
2128 cbD->currentTarget = rt;
2129
2130 bool hasShadingRateMapSet = false;
2131#ifdef QRHI_D3D12_CL5_AVAILABLE
2132 if (rtD->rp->hasShadingRateMap) {
2133 cbD->setShadingRate(QSize(1, 1));
2134 QD3D12ShadingRateMap *rateMapD = rt->resourceType() == QRhiRenderTarget::TextureRenderTarget
2135 ? QRHI_RES(QD3D12ShadingRateMap, QRHI_RES(QD3D12TextureRenderTarget, rt)->m_desc.shadingRateMap())
2136 : QRHI_RES(QD3D12ShadingRateMap, QRHI_RES(QD3D12SwapChainRenderTarget, rt)->swapChain()->shadingRateMap());
2137 if (QD3D12Resource *res = resourcePool.lookupRef(rateMapD->handle)) {
2138 barrierGen.addTransitionBarrier(rateMapD->handle, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE);
2139 barrierGen.enqueueBufferedTransitionBarriers(cbD);
2140 cbD->cmdList->RSSetShadingRateImage(res->resource);
2141 hasShadingRateMapSet = true;
2142 }
2143 } else if (cbD->hasShadingRateMapSet) {
2144 cbD->cmdList->RSSetShadingRateImage(nullptr);
2145 cbD->setShadingRate(QSize(1, 1));
2146 } else if (cbD->hasShadingRateSet) {
2147 cbD->setShadingRate(QSize(1, 1));
2148 }
2149#endif
2150
2151 cbD->resetPerPassState();
2152
2153 // shading rate tracking is reset in resetPerPassState(), sync what we did just above
2154 cbD->hasShadingRateMapSet = hasShadingRateMapSet;
2155}
2156
2157void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2158{
2159 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2160 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
2161
2162 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
2163 QD3D12TextureRenderTarget *rtTex = QRHI_RES(QD3D12TextureRenderTarget, cbD->currentTarget);
2164 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2165 it != itEnd; ++it)
2166 {
2167 const QRhiColorAttachment &colorAtt(*it);
2168 if (!colorAtt.resolveTexture())
2169 continue;
2170
2171 QD3D12Texture *dstTexD = QRHI_RES(QD3D12Texture, colorAtt.resolveTexture());
2172 QD3D12Resource *dstRes = resourcePool.lookupRef(dstTexD->handle);
2173 if (!dstRes)
2174 continue;
2175
2176 QD3D12Texture *srcTexD = QRHI_RES(QD3D12Texture, colorAtt.texture());
2177 QD3D12RenderBuffer *srcRbD = QRHI_RES(QD3D12RenderBuffer, colorAtt.renderBuffer());
2178 Q_ASSERT(srcTexD || srcRbD);
2179 QD3D12Resource *srcRes = resourcePool.lookupRef(srcTexD ? srcTexD->handle : srcRbD->handle);
2180 if (!srcRes)
2181 continue;
2182
2183 if (srcTexD) {
2184 if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
2185 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2186 int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
2187 continue;
2188 }
2189 if (srcTexD->sampleDesc.Count <= 1) {
2190 qWarning("Cannot resolve a non-multisample texture");
2191 continue;
2192 }
2193 if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
2194 qWarning("Resolve source and destination sizes do not match");
2195 continue;
2196 }
2197 } else {
2198 if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
2199 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2200 int(srcRbD->dxgiFormat), int(dstTexD->dxgiFormat));
2201 continue;
2202 }
2203 if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
2204 qWarning("Resolve source and destination sizes do not match");
2205 continue;
2206 }
2207 }
2208
2209 barrierGen.addTransitionBarrier(srcTexD ? srcTexD->handle : srcRbD->handle, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
2210 barrierGen.addTransitionBarrier(dstTexD->handle, D3D12_RESOURCE_STATE_RESOLVE_DEST);
2211 barrierGen.enqueueBufferedTransitionBarriers(cbD);
2212
2213 const UINT resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
2214 for (UINT resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
2215 const UINT srcSubresource = calcSubresource(0, UINT(colorAtt.layer()) + resolveIdx, 1);
2216 const UINT dstSubresource = calcSubresource(UINT(colorAtt.resolveLevel()),
2217 UINT(colorAtt.resolveLayer()) + resolveIdx,
2218 dstTexD->mipLevelCount);
2219 cbD->cmdList->ResolveSubresource(dstRes->resource, dstSubresource,
2220 srcRes->resource, srcSubresource,
2221 dstTexD->dxgiFormat);
2222 }
2223 }
2224 if (rtTex->m_desc.depthResolveTexture())
2225 qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
2226 }
2227
2228 cbD->recordingPass = QD3D12CommandBuffer::NoPass;
2229 cbD->currentTarget = nullptr;
2230
2231 if (resourceUpdates)
2232 enqueueResourceUpdates(cbD, resourceUpdates);
2233}
2234
2235void QRhiD3D12::beginComputePass(QRhiCommandBuffer *cb,
2236 QRhiResourceUpdateBatch *resourceUpdates,
2237 QRhiCommandBuffer::BeginPassFlags)
2238{
2239 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2240 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
2241
2242 if (resourceUpdates)
2243 enqueueResourceUpdates(cbD, resourceUpdates);
2244
2245 cbD->recordingPass = QD3D12CommandBuffer::ComputePass;
2246
2247 cbD->resetPerPassState();
2248}
2249
2250void QRhiD3D12::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2251{
2252 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2253 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
2254
2255 cbD->recordingPass = QD3D12CommandBuffer::NoPass;
2256
2257 if (resourceUpdates)
2258 enqueueResourceUpdates(cbD, resourceUpdates);
2259}
2260
2261void QRhiD3D12::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
2262{
2263 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2264 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
2265 QD3D12ComputePipeline *psD = QRHI_RES(QD3D12ComputePipeline, ps);
2266 const bool pipelineChanged = cbD->currentComputePipeline != psD || cbD->currentPipelineGeneration != psD->generation;
2267
2268 if (pipelineChanged) {
2269 cbD->currentGraphicsPipeline = nullptr;
2270 cbD->currentComputePipeline = psD;
2271 cbD->currentPipelineGeneration = psD->generation;
2272
2273 if (QD3D12Pipeline *pipeline = pipelinePool.lookupRef(psD->handle)) {
2274 Q_ASSERT(pipeline->type == QD3D12Pipeline::Compute);
2275 cbD->cmdList->SetPipelineState(pipeline->pso);
2276 if (QD3D12RootSignature *rs = rootSignaturePool.lookupRef(psD->rootSigHandle))
2277 cbD->cmdList->SetComputeRootSignature(rs->rootSig);
2278 }
2279 }
2280}
2281
2282void QRhiD3D12::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
2283{
2284 QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
2285 Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
2286 cbD->cmdList->Dispatch(UINT(x), UINT(y), UINT(z));
2287}
2288
2289bool QD3D12DescriptorHeap::create(ID3D12Device *device,
2290 quint32 descriptorCount,
2291 D3D12_DESCRIPTOR_HEAP_TYPE heapType,
2292 D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags)
2293{
2294 head = 0;
2295 capacity = descriptorCount;
2296 this->heapType = heapType;
2297 this->heapFlags = heapFlags;
2298
2299 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
2300 heapDesc.Type = heapType;
2301 heapDesc.NumDescriptors = capacity;
2302 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAGS(heapFlags);
2303
2304 HRESULT hr = device->CreateDescriptorHeap(&heapDesc, __uuidof(ID3D12DescriptorHeap), reinterpret_cast<void **>(&heap));
2305 if (FAILED(hr)) {
2306 qWarning("Failed to create descriptor heap: %s", qPrintable(QSystemError::windowsComString(hr)));
2307 heap = nullptr;
2308 capacity = descriptorByteSize = 0;
2309 return false;
2310 }
2311
2312 descriptorByteSize = device->GetDescriptorHandleIncrementSize(heapType);
2313 heapStart.cpuHandle = heap->GetCPUDescriptorHandleForHeapStart();
2314 if (heapFlags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
2315 heapStart.gpuHandle = heap->GetGPUDescriptorHandleForHeapStart();
2316
2317 return true;
2318}
2319
2320void QD3D12DescriptorHeap::createWithExisting(const QD3D12DescriptorHeap &other,
2321 quint32 offsetInDescriptors,
2322 quint32 descriptorCount)
2323{
2324 heap = nullptr;
2325 head = 0;
2326 capacity = descriptorCount;
2327 heapType = other.heapType;
2328 heapFlags = other.heapFlags;
2329 descriptorByteSize = other.descriptorByteSize;
2330 heapStart = incremented(other.heapStart, offsetInDescriptors);
2331}
2332
2333void QD3D12DescriptorHeap::destroy()
2334{
2335 if (heap) {
2336 heap->Release();
2337 heap = nullptr;
2338 }
2339 capacity = 0;
2340}
2341
2342void QD3D12DescriptorHeap::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
2343{
2344 if (heap) {
2345 releaseQueue->deferredReleaseDescriptorHeap(heap);
2346 heap = nullptr;
2347 }
2348 capacity = 0;
2349}
2350
2351QD3D12Descriptor QD3D12DescriptorHeap::get(quint32 count)
2352{
2353 Q_ASSERT(count > 0);
2354 if (head + count > capacity) {
2355 qWarning("Cannot get %u descriptors as that would exceed capacity %u", count, capacity);
2356 return {};
2357 }
2358 head += count;
2359 return at(head - count);
2360}
2361
2362QD3D12Descriptor QD3D12DescriptorHeap::at(quint32 index) const
2363{
2364 const quint32 startOffset = index * descriptorByteSize;
2365 QD3D12Descriptor result;
2366 result.cpuHandle.ptr = heapStart.cpuHandle.ptr + startOffset;
2367 if (heapStart.gpuHandle.ptr != 0)
2368 result.gpuHandle.ptr = heapStart.gpuHandle.ptr + startOffset;
2369 return result;
2370}
2371
2372bool QD3D12CpuDescriptorPool::create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE heapType, const char *debugName)
2373{
2374 QD3D12DescriptorHeap firstHeap;
2375 if (!firstHeap.create(device, DESCRIPTORS_PER_HEAP, heapType, D3D12_DESCRIPTOR_HEAP_FLAG_NONE))
2376 return false;
2377 heaps.append(HeapWithMap::init(firstHeap, DESCRIPTORS_PER_HEAP));
2378 descriptorByteSize = heaps[0].heap.descriptorByteSize;
2379 this->device = device;
2380 this->debugName = debugName;
2381 return true;
2382}
2383
2384void QD3D12CpuDescriptorPool::destroy()
2385{
2386#ifndef QT_NO_DEBUG
2387 // debug builds: just do it always
2388 static bool leakCheck = true;
2389#else
2390 // release builds: opt-in
2391 static bool leakCheck = qEnvironmentVariableIntValue("QT_RHI_LEAK_CHECK");
2392#endif
2393 if (leakCheck) {
2394 for (HeapWithMap &heap : heaps) {
2395 const int leakedDescriptorCount = heap.map.count(true);
2396 if (leakedDescriptorCount > 0) {
2397 qWarning("QD3D12CpuDescriptorPool::destroy(): "
2398 "Heap %p for descriptor pool %p '%s' has %d unreleased descriptors",
2399 &heap.heap, this, debugName, leakedDescriptorCount);
2400 }
2401 }
2402 }
2403 for (HeapWithMap &heap : heaps)
2404 heap.heap.destroy();
2405 heaps.clear();
2406}
2407
2408QD3D12Descriptor QD3D12CpuDescriptorPool::allocate(quint32 count)
2409{
2410 Q_ASSERT(count > 0 && count <= DESCRIPTORS_PER_HEAP);
2411
2412 HeapWithMap &last(heaps.last());
2413 if (last.heap.head + count <= last.heap.capacity) {
2414 quint32 firstIndex = last.heap.head;
2415 for (quint32 i = 0; i < count; ++i)
2416 last.map.setBit(firstIndex + i);
2417 return last.heap.get(count);
2418 }
2419
2420 for (HeapWithMap &heap : heaps) {
2421 quint32 freeCount = 0;
2422 for (quint32 i = 0; i < DESCRIPTORS_PER_HEAP; ++i) {
2423 if (heap.map.testBit(i)) {
2424 freeCount = 0;
2425 } else {
2426 freeCount += 1;
2427 if (freeCount == count) {
2428 quint32 firstIndex = i - (freeCount - 1);
2429 for (quint32 j = 0; j < count; ++j) {
2430 heap.map.setBit(firstIndex + j);
2431 return heap.heap.at(firstIndex);
2432 }
2433 }
2434 }
2435 }
2436 }
2437
2438 QD3D12DescriptorHeap newHeap;
2439 if (!newHeap.create(device, DESCRIPTORS_PER_HEAP, last.heap.heapType, last.heap.heapFlags))
2440 return {};
2441
2442 heaps.append(HeapWithMap::init(newHeap, DESCRIPTORS_PER_HEAP));
2443
2444 for (quint32 i = 0; i < count; ++i)
2445 heaps.last().map.setBit(i);
2446
2447 return heaps.last().heap.get(count);
2448}
2449
2450void QD3D12CpuDescriptorPool::release(const QD3D12Descriptor &descriptor, quint32 count)
2451{
2452 Q_ASSERT(count > 0 && count <= DESCRIPTORS_PER_HEAP);
2453 if (!descriptor.isValid())
2454 return;
2455
2456 const SIZE_T addr = descriptor.cpuHandle.ptr;
2457 for (HeapWithMap &heap : heaps) {
2458 const SIZE_T begin = heap.heap.heapStart.cpuHandle.ptr;
2459 const SIZE_T end = begin + heap.heap.descriptorByteSize * heap.heap.capacity;
2460 if (addr >= begin && addr < end) {
2461 quint32 firstIndex = (addr - begin) / heap.heap.descriptorByteSize;
2462 for (quint32 i = 0; i < count; ++i)
2463 heap.map.setBit(firstIndex + i, false);
2464 return;
2465 }
2466 }
2467
2468 qWarning("QD3D12CpuDescriptorPool::release: Descriptor with address %llu is not in any heap",
2469 quint64(descriptor.cpuHandle.ptr));
2470}
2471
2472bool QD3D12QueryHeap::create(ID3D12Device *device,
2473 quint32 queryCount,
2474 D3D12_QUERY_HEAP_TYPE heapType)
2475{
2476 capacity = queryCount;
2477
2478 D3D12_QUERY_HEAP_DESC heapDesc = {};
2479 heapDesc.Type = heapType;
2480 heapDesc.Count = capacity;
2481
2482 HRESULT hr = device->CreateQueryHeap(&heapDesc, __uuidof(ID3D12QueryHeap), reinterpret_cast<void **>(&heap));
2483 if (FAILED(hr)) {
2484 qWarning("Failed to create query heap: %s", qPrintable(QSystemError::windowsComString(hr)));
2485 heap = nullptr;
2486 capacity = 0;
2487 return false;
2488 }
2489
2490 return true;
2491}
2492
2493void QD3D12QueryHeap::destroy()
2494{
2495 if (heap) {
2496 heap->Release();
2497 heap = nullptr;
2498 }
2499 capacity = 0;
2500}
2501
2502bool QD3D12StagingArea::create(QRhiD3D12 *rhi, quint32 capacity, D3D12_HEAP_TYPE heapType)
2503{
2504 Q_ASSERT(heapType == D3D12_HEAP_TYPE_UPLOAD || heapType == D3D12_HEAP_TYPE_READBACK);
2505 D3D12_RESOURCE_DESC resourceDesc = {};
2506 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
2507 resourceDesc.Width = capacity;
2508 resourceDesc.Height = 1;
2509 resourceDesc.DepthOrArraySize = 1;
2510 resourceDesc.MipLevels = 1;
2511 resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
2512 resourceDesc.SampleDesc = { 1, 0 };
2513 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
2514 resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
2515 UINT state = heapType == D3D12_HEAP_TYPE_UPLOAD ? D3D12_RESOURCE_STATE_GENERIC_READ : D3D12_RESOURCE_STATE_COPY_DEST;
2516 HRESULT hr = rhi->vma.createResource(heapType,
2517 &resourceDesc,
2518 D3D12_RESOURCE_STATES(state),
2519 nullptr,
2520 &allocation,
2521 __uuidof(ID3D12Resource),
2522 reinterpret_cast<void **>(&resource));
2523 if (FAILED(hr)) {
2524 qWarning("Failed to create buffer for staging area: %s",
2525 qPrintable(QSystemError::windowsComString(hr)));
2526 return false;
2527 }
2528 void *p = nullptr;
2529 hr = resource->Map(0, nullptr, &p);
2530 if (FAILED(hr)) {
2531 qWarning("Failed to map buffer for staging area: %s",
2532 qPrintable(QSystemError::windowsComString(hr)));
2533 destroy();
2534 return false;
2535 }
2536
2537 mem.p = static_cast<quint8 *>(p);
2538 mem.gpuAddr = resource->GetGPUVirtualAddress();
2539 mem.buffer = resource;
2540 mem.bufferOffset = 0;
2541
2542 this->capacity = capacity;
2543 head = 0;
2544
2545 return true;
2546}
2547
2548void QD3D12StagingArea::destroy()
2549{
2550 if (resource) {
2551 resource->Release();
2552 resource = nullptr;
2553 }
2554 if (allocation) {
2555 allocation->Release();
2556 allocation = nullptr;
2557 }
2558 mem = {};
2559}
2560
2561void QD3D12StagingArea::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
2562{
2563 if (resource)
2564 releaseQueue->deferredReleaseResourceAndAllocation(resource, allocation);
2565 mem = {};
2566}
2567
2568QD3D12StagingArea::Allocation QD3D12StagingArea::get(quint32 byteSize)
2569{
2570 const quint32 allocSize = aligned(byteSize, ALIGNMENT);
2571 if (head + allocSize > capacity) {
2572 qWarning("Failed to allocate %u (%u) bytes from staging area of size %u with %u bytes left",
2573 allocSize, byteSize, capacity, remainingCapacity());
2574 return {};
2575 }
2576 const quint32 offset = head;
2577 head += allocSize;
2578 return {
2579 mem.p + offset,
2580 mem.gpuAddr + offset,
2581 mem.buffer,
2582 offset
2583 };
2584}
2585
2586// Can be called inside and outside of begin-endFrame. Removes from the pool
2587// and releases the underlying native resource only in the frames_in_flight'th
2588// beginFrame() counted starting from the next endFrame().
2589void QD3D12ReleaseQueue::deferredReleaseResource(const QD3D12ObjectHandle &handle)
2590{
2591 DeferredReleaseEntry e;
2592 e.handle = handle;
2593 queue.append(e);
2594}
2595
2596void QD3D12ReleaseQueue::deferredReleaseResourceWithViews(const QD3D12ObjectHandle &handle,
2597 QD3D12CpuDescriptorPool *pool,
2598 const QD3D12Descriptor &viewsStart,
2599 int viewCount)
2600{
2601 DeferredReleaseEntry e;
2602 e.type = DeferredReleaseEntry::Resource;
2603 e.handle = handle;
2604 e.poolForViews = pool;
2605 e.viewsStart = viewsStart;
2606 e.viewCount = viewCount;
2607 queue.append(e);
2608}
2609
2610void QD3D12ReleaseQueue::deferredReleasePipeline(const QD3D12ObjectHandle &handle)
2611{
2612 DeferredReleaseEntry e;
2613 e.type = DeferredReleaseEntry::Pipeline;
2614 e.handle = handle;
2615 queue.append(e);
2616}
2617
2618void QD3D12ReleaseQueue::deferredReleaseRootSignature(const QD3D12ObjectHandle &handle)
2619{
2620 DeferredReleaseEntry e;
2621 e.type = DeferredReleaseEntry::RootSignature;
2622 e.handle = handle;
2623 queue.append(e);
2624}
2625
2626void QD3D12ReleaseQueue::deferredReleaseCallback(std::function<void(void*)> callback, void *userData)
2627{
2628 DeferredReleaseEntry e;
2629 e.type = DeferredReleaseEntry::Callback;
2630 e.callback = callback;
2631 e.callbackUserData = userData;
2632 queue.append(e);
2633}
2634
2635void QD3D12ReleaseQueue::deferredReleaseResourceAndAllocation(ID3D12Resource *resource,
2636 D3D12MA::Allocation *allocation)
2637{
2638 DeferredReleaseEntry e;
2639 e.type = DeferredReleaseEntry::ResourceAndAllocation;
2640 e.resourceAndAllocation = { resource, allocation };
2641 queue.append(e);
2642}
2643
2644void QD3D12ReleaseQueue::deferredReleaseDescriptorHeap(ID3D12DescriptorHeap *heap)
2645{
2646 DeferredReleaseEntry e;
2647 e.type = DeferredReleaseEntry::DescriptorHeap;
2648 e.descriptorHeap = heap;
2649 queue.append(e);
2650}
2651
2652void QD3D12ReleaseQueue::deferredReleaseViews(QD3D12CpuDescriptorPool *pool,
2653 const QD3D12Descriptor &viewsStart,
2654 int viewCount)
2655{
2656 DeferredReleaseEntry e;
2657 e.type = DeferredReleaseEntry::Views;
2658 e.poolForViews = pool;
2659 e.viewsStart = viewsStart;
2660 e.viewCount = viewCount;
2661 queue.append(e);
2662}
2663
2664void QD3D12ReleaseQueue::activatePendingDeferredReleaseRequests(int frameSlot)
2665{
2666 for (DeferredReleaseEntry &e : queue) {
2667 if (!e.frameSlotToBeReleasedIn.has_value())
2668 e.frameSlotToBeReleasedIn = frameSlot;
2669 }
2670}
2671
2672void QD3D12ReleaseQueue::executeDeferredReleases(int frameSlot, bool forced)
2673{
2674 for (int i = queue.count() - 1; i >= 0; --i) {
2675 const DeferredReleaseEntry &e(queue[i]);
2676 if (forced || (e.frameSlotToBeReleasedIn.has_value() && e.frameSlotToBeReleasedIn.value() == frameSlot)) {
2677 switch (e.type) {
2678 case DeferredReleaseEntry::Resource:
2679 resourcePool->remove(e.handle);
2680 if (e.poolForViews && e.viewsStart.isValid() && e.viewCount > 0)
2681 e.poolForViews->release(e.viewsStart, e.viewCount);
2682 break;
2683 case DeferredReleaseEntry::Pipeline:
2684 pipelinePool->remove(e.handle);
2685 break;
2686 case DeferredReleaseEntry::RootSignature:
2687 rootSignaturePool->remove(e.handle);
2688 break;
2689 case DeferredReleaseEntry::Callback:
2690 e.callback(e.callbackUserData);
2691 break;
2692 case DeferredReleaseEntry::ResourceAndAllocation:
2693 // order matters: resource first, then the allocation (which
2694 // may be null)
2695 e.resourceAndAllocation.first->Release();
2696 if (e.resourceAndAllocation.second)
2697 e.resourceAndAllocation.second->Release();
2698 break;
2699 case DeferredReleaseEntry::DescriptorHeap:
2700 e.descriptorHeap->Release();
2701 break;
2702 case DeferredReleaseEntry::Views:
2703 e.poolForViews->release(e.viewsStart, e.viewCount);
2704 break;
2705 }
2706 queue.removeAt(i);
2707 }
2708 }
2709}
2710
2711void QD3D12ReleaseQueue::releaseAll()
2712{
2713 executeDeferredReleases(0, true);
2714}
2715
2716void QD3D12ResourceBarrierGenerator::addTransitionBarrier(const QD3D12ObjectHandle &resourceHandle,
2717 D3D12_RESOURCE_STATES stateAfter)
2718{
2719 if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
2720 if (stateAfter != res->state) {
2721 transitionResourceBarriers.append({ resourceHandle, res->state, stateAfter });
2722 res->state = stateAfter;
2723 }
2724 }
2725}
2726
2727void QD3D12ResourceBarrierGenerator::enqueueBufferedTransitionBarriers(QD3D12CommandBuffer *cbD)
2728{
2729 QVarLengthArray<D3D12_RESOURCE_BARRIER, PREALLOC> barriers;
2730 for (const TransitionResourceBarrier &trb : transitionResourceBarriers) {
2731 if (QD3D12Resource *res = resourcePool->lookupRef(trb.resourceHandle)) {
2732 D3D12_RESOURCE_BARRIER barrier = {};
2733 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
2734 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
2735 barrier.Transition.pResource = res->resource;
2736 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
2737 barrier.Transition.StateBefore = trb.stateBefore;
2738 barrier.Transition.StateAfter = trb.stateAfter;
2739 barriers.append(barrier);
2740 }
2741 }
2742 transitionResourceBarriers.clear();
2743 if (!barriers.isEmpty())
2744 cbD->cmdList->ResourceBarrier(barriers.count(), barriers.constData());
2745}
2746
2747void QD3D12ResourceBarrierGenerator::enqueueSubresourceTransitionBarrier(QD3D12CommandBuffer *cbD,
2748 const QD3D12ObjectHandle &resourceHandle,
2749 UINT subresource,
2750 D3D12_RESOURCE_STATES stateBefore,
2751 D3D12_RESOURCE_STATES stateAfter)
2752{
2753 if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
2754 D3D12_RESOURCE_BARRIER barrier = {};
2755 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
2756 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
2757 barrier.Transition.pResource = res->resource;
2758 barrier.Transition.Subresource = subresource;
2759 barrier.Transition.StateBefore = stateBefore;
2760 barrier.Transition.StateAfter = stateAfter;
2761 cbD->cmdList->ResourceBarrier(1, &barrier);
2762 }
2763}
2764
2765void QD3D12ResourceBarrierGenerator::enqueueUavBarrier(QD3D12CommandBuffer *cbD,
2766 const QD3D12ObjectHandle &resourceHandle)
2767{
2768 if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
2769 D3D12_RESOURCE_BARRIER barrier = {};
2770 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
2771 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
2772 barrier.UAV.pResource = res->resource;
2773 cbD->cmdList->ResourceBarrier(1, &barrier);
2774 }
2775}
2776
2777void QD3D12ShaderBytecodeCache::insertWithCapacityLimit(const QRhiShaderStage &key, const Shader &s)
2778{
2779 if (data.count() >= QRhiD3D12::MAX_SHADER_CACHE_ENTRIES)
2780 data.clear();
2781 data.insert(key, s);
2782}
2783
2784bool QD3D12ShaderVisibleDescriptorHeap::create(ID3D12Device *device,
2785 D3D12_DESCRIPTOR_HEAP_TYPE type,
2786 quint32 perFrameDescriptorCount)
2787{
2788 Q_ASSERT(type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
2789
2790 quint32 size = perFrameDescriptorCount * QD3D12_FRAMES_IN_FLIGHT;
2791
2792 // https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-support
2793 const quint32 CBV_SRV_UAV_MAX = 1000000;
2794 const quint32 SAMPLER_MAX = 2048;
2795 if (type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
2796 size = qMin(size, CBV_SRV_UAV_MAX);
2797 else if (type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
2798 size = qMin(size, SAMPLER_MAX);
2799
2800 if (!heap.create(device, size, type, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) {
2801 qWarning("Failed to create shader-visible descriptor heap of size %u", size);
2802 return false;
2803 }
2804
2805 perFrameDescriptorCount = size / QD3D12_FRAMES_IN_FLIGHT;
2806 quint32 currentOffsetInDescriptors = 0;
2807 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
2808 perFrameHeapSlice[i].createWithExisting(heap, currentOffsetInDescriptors, perFrameDescriptorCount);
2809 currentOffsetInDescriptors += perFrameDescriptorCount;
2810 }
2811
2812 return true;
2813}
2814
2815void QD3D12ShaderVisibleDescriptorHeap::destroy()
2816{
2817 heap.destroy();
2818}
2819
2820void QD3D12ShaderVisibleDescriptorHeap::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
2821{
2822 heap.destroyWithDeferredRelease(releaseQueue);
2823}
2824
2825static inline std::pair<int, int> mapBinding(int binding, const QShader::NativeResourceBindingMap &map)
2826{
2827 if (map.isEmpty())
2828 return { binding, binding }; // assume 1:1 mapping
2829
2830 auto it = map.constFind(binding);
2831 if (it != map.cend())
2832 return *it;
2833
2834 // Hitting this path is normal too. It is not given that the resource is
2835 // present in the shaders for all the stages specified by the visibility
2836 // mask in the QRhiShaderResourceBinding.
2837 return { -1, -1 };
2838}
2839
2840void QD3D12ShaderResourceVisitor::visit()
2841{
2842 for (int bindingIdx = 0, bindingCount = srb->m_bindings.count(); bindingIdx != bindingCount; ++bindingIdx) {
2843 const QRhiShaderResourceBinding &b(srb->m_bindings[bindingIdx]);
2844 const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
2845
2846 for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) {
2847 const QD3D12ShaderStageData *sd = &stageData[stageIdx];
2848 if (!sd->valid)
2849 continue;
2850
2851 if (!bd->stage.testFlag(qd3d12_stageToSrb(sd->stage)))
2852 continue;
2853
2854 switch (bd->type) {
2855 case QRhiShaderResourceBinding::UniformBuffer:
2856 {
2857 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2858 if (shaderRegister >= 0 && uniformBuffer)
2859 uniformBuffer(sd->stage, bd->u.ubuf, shaderRegister, bd->binding);
2860 }
2861 break;
2862 case QRhiShaderResourceBinding::SampledTexture:
2863 {
2864 Q_ASSERT(bd->u.stex.count > 0);
2865 const int textureBaseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2866 const int samplerBaseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).second;
2867 for (int i = 0; i < bd->u.stex.count; ++i) {
2868 if (textureBaseShaderRegister >= 0 && texture)
2869 texture(sd->stage, bd->u.stex.texSamplers[i], textureBaseShaderRegister + i);
2870 if (samplerBaseShaderRegister >= 0 && sampler)
2871 sampler(sd->stage, bd->u.stex.texSamplers[i], samplerBaseShaderRegister + i);
2872 }
2873 }
2874 break;
2875 case QRhiShaderResourceBinding::Texture:
2876 {
2877 Q_ASSERT(bd->u.stex.count > 0);
2878 const int baseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2879 if (baseShaderRegister >= 0 && texture) {
2880 for (int i = 0; i < bd->u.stex.count; ++i)
2881 texture(sd->stage, bd->u.stex.texSamplers[i], baseShaderRegister + i);
2882 }
2883 }
2884 break;
2885 case QRhiShaderResourceBinding::Sampler:
2886 {
2887 Q_ASSERT(bd->u.stex.count > 0);
2888 const int baseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2889 if (baseShaderRegister >= 0 && sampler) {
2890 for (int i = 0; i < bd->u.stex.count; ++i)
2891 sampler(sd->stage, bd->u.stex.texSamplers[i], baseShaderRegister + i);
2892 }
2893 }
2894 break;
2895 case QRhiShaderResourceBinding::ImageLoad:
2896 {
2897 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2898 if (shaderRegister >= 0 && storageImage)
2899 storageImage(sd->stage, bd->u.simage, Load, shaderRegister);
2900 }
2901 break;
2902 case QRhiShaderResourceBinding::ImageStore:
2903 {
2904 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2905 if (shaderRegister >= 0 && storageImage)
2906 storageImage(sd->stage, bd->u.simage, Store, shaderRegister);
2907 }
2908 break;
2909 case QRhiShaderResourceBinding::ImageLoadStore:
2910 {
2911 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2912 if (shaderRegister >= 0 && storageImage)
2913 storageImage(sd->stage, bd->u.simage, LoadStore, shaderRegister);
2914 }
2915 break;
2916 case QRhiShaderResourceBinding::BufferLoad:
2917 {
2918 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2919 if (shaderRegister >= 0 && storageBuffer)
2920 storageBuffer(sd->stage, bd->u.sbuf, Load, shaderRegister);
2921 }
2922 break;
2923 case QRhiShaderResourceBinding::BufferStore:
2924 {
2925 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2926 if (shaderRegister >= 0 && storageBuffer)
2927 storageBuffer(sd->stage, bd->u.sbuf, Store, shaderRegister);
2928 }
2929 break;
2930 case QRhiShaderResourceBinding::BufferLoadStore:
2931 {
2932 const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
2933 if (shaderRegister >= 0 && storageBuffer)
2934 storageBuffer(sd->stage, bd->u.sbuf, LoadStore, shaderRegister);
2935 }
2936 break;
2937 }
2938 }
2939 }
2940}
2941
2942bool QD3D12SamplerManager::create(ID3D12Device *device)
2943{
2944 // This does not need to be per-frame slot, just grab space for MAX_SAMPLERS samplers.
2945 if (!shaderVisibleSamplerHeap.create(device,
2946 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
2947 MAX_SAMPLERS / QD3D12_FRAMES_IN_FLIGHT))
2948 {
2949 qWarning("Could not create shader-visible SAMPLER heap");
2950 return false;
2951 }
2952
2953 this->device = device;
2954 return true;
2955}
2956
2957void QD3D12SamplerManager::destroy()
2958{
2959 if (device) {
2960 shaderVisibleSamplerHeap.destroy();
2961 device = nullptr;
2962 }
2963}
2964
2965QD3D12Descriptor QD3D12SamplerManager::getShaderVisibleDescriptor(const D3D12_SAMPLER_DESC &desc)
2966{
2967 auto it = gpuMap.constFind({desc});
2968 if (it != gpuMap.cend())
2969 return *it;
2970
2971 QD3D12Descriptor descriptor = shaderVisibleSamplerHeap.heap.get(1);
2972 if (descriptor.isValid()) {
2973 device->CreateSampler(&desc, descriptor.cpuHandle);
2974 gpuMap.insert({desc}, descriptor);
2975 } else {
2976 qWarning("Out of shader-visible SAMPLER descriptor heap space,"
2977 " this should not happen, maximum number of unique samplers is %u",
2978 shaderVisibleSamplerHeap.heap.capacity);
2979 }
2980
2981 return descriptor;
2982}
2983
2984bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD)
2985{
2986 this->rhiD = rhiD;
2987
2988 D3D12_ROOT_PARAMETER1 rootParams[3] = {};
2989 D3D12_DESCRIPTOR_RANGE1 descriptorRanges[2] = {};
2990
2991 // b0
2992 rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
2993 rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
2994 rootParams[0].Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
2995
2996 // t0
2997 descriptorRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
2998 descriptorRanges[0].NumDescriptors = 1;
2999 descriptorRanges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE;
3000 rootParams[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
3001 rootParams[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3002 rootParams[1].DescriptorTable.NumDescriptorRanges = 1;
3003 rootParams[1].DescriptorTable.pDescriptorRanges = &descriptorRanges[0];
3004
3005 // u0..3
3006 descriptorRanges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
3007 descriptorRanges[1].NumDescriptors = 4;
3008 rootParams[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
3009 rootParams[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3010 rootParams[2].DescriptorTable.NumDescriptorRanges = 1;
3011 rootParams[2].DescriptorTable.pDescriptorRanges = &descriptorRanges[1];
3012
3013 // s0
3014 D3D12_STATIC_SAMPLER_DESC samplerDesc = {};
3015 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
3016 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3017 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3018 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3019 samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3020
3021 D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
3022 rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
3023 rsDesc.Desc_1_1.NumParameters = 3;
3024 rsDesc.Desc_1_1.pParameters = rootParams;
3025 rsDesc.Desc_1_1.NumStaticSamplers = 1;
3026 rsDesc.Desc_1_1.pStaticSamplers = &samplerDesc;
3027
3028 ID3DBlob *signature = nullptr;
3029 HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
3030 if (FAILED(hr)) {
3031 qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
3032 return false;
3033 }
3034 ID3D12RootSignature *rootSig = nullptr;
3035 hr = rhiD->dev->CreateRootSignature(0,
3036 signature->GetBufferPointer(),
3037 signature->GetBufferSize(),
3038 __uuidof(ID3D12RootSignature),
3039 reinterpret_cast<void **>(&rootSig));
3040 signature->Release();
3041 if (FAILED(hr)) {
3042 qWarning("Failed to create root signature: %s",
3043 qPrintable(QSystemError::windowsComString(hr)));
3044 return false;
3045 }
3046
3047 rootSigHandle = QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
3048
3049 D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
3050 psoDesc.pRootSignature = rootSig;
3051 psoDesc.CS.pShaderBytecode = g_csMipmap;
3052 psoDesc.CS.BytecodeLength = sizeof(g_csMipmap);
3053 ID3D12PipelineState *pso = nullptr;
3054 hr = rhiD->dev->CreateComputePipelineState(&psoDesc,
3055 __uuidof(ID3D12PipelineState),
3056 reinterpret_cast<void **>(&pso));
3057 if (FAILED(hr)) {
3058 qWarning("Failed to create compute pipeline state: %s",
3059 qPrintable(QSystemError::windowsComString(hr)));
3060 rhiD->rootSignaturePool.remove(rootSigHandle);
3061 rootSigHandle = {};
3062 return false;
3063 }
3064
3065 pipelineHandle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
3066
3067 return true;
3068}
3069
3070void QD3D12MipmapGenerator::destroy()
3071{
3072 rhiD->pipelinePool.remove(pipelineHandle);
3073 pipelineHandle = {};
3074 rhiD->rootSignaturePool.remove(rootSigHandle);
3075 rootSigHandle = {};
3076}
3077
3078void QD3D12MipmapGenerator::generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle)
3079{
3080 QD3D12Pipeline *pipeline = rhiD->pipelinePool.lookupRef(pipelineHandle);
3081 if (!pipeline)
3082 return;
3083 QD3D12RootSignature *rootSig = rhiD->rootSignaturePool.lookupRef(rootSigHandle);
3084 if (!rootSig)
3085 return;
3086 QD3D12Resource *res = rhiD->resourcePool.lookupRef(textureHandle);
3087 if (!res)
3088 return;
3089
3090 const quint32 mipLevelCount = res->desc.MipLevels;
3091 if (mipLevelCount < 2)
3092 return;
3093
3094 if (res->desc.SampleDesc.Count > 1) {
3095 qWarning("Cannot generate mipmaps for MSAA texture");
3096 return;
3097 }
3098
3099 const bool is1D = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D;
3100 if (is1D) {
3101 qWarning("Cannot generate mipmaps for 1D texture");
3102 return;
3103 }
3104
3105 const bool is3D = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D;
3106 const bool isCubeOrArray = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
3107 && res->desc.DepthOrArraySize > 1;
3108 const quint32 layerCount = isCubeOrArray ? res->desc.DepthOrArraySize : 1;
3109
3110 if (is3D) {
3111 qWarning("2D mipmap generator invoked for 3D texture, this should not happen");
3112 return;
3113 }
3114
3115 rhiD->barrierGen.addTransitionBarrier(textureHandle, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
3116 rhiD->barrierGen.enqueueBufferedTransitionBarriers(cbD);
3117
3118 cbD->cmdList->SetPipelineState(pipeline->pso);
3119 cbD->cmdList->SetComputeRootSignature(rootSig->rootSig);
3120
3121 const quint32 descriptorByteSize = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].descriptorByteSize;
3122
3123 struct CBufData {
3124 quint32 srcMipLevel;
3125 quint32 numMipLevels;
3126 float texelWidth;
3127 float texelHeight;
3128 };
3129
3130 const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(sizeof(CBufData), mipLevelCount * layerCount);
3131 std::optional<QD3D12StagingArea> ownStagingArea;
3132 if (rhiD->smallStagingAreas[rhiD->currentFrameSlot].remainingCapacity() < allocSize) {
3133 ownStagingArea = QD3D12StagingArea();
3134 if (!ownStagingArea->create(rhiD, allocSize, D3D12_HEAP_TYPE_UPLOAD)) {
3135 qWarning("Could not create staging area for mipmap generation");
3136 return;
3137 }
3138 }
3139 QD3D12StagingArea *workArea = ownStagingArea.has_value()
3140 ? &ownStagingArea.value()
3141 : &rhiD->smallStagingAreas[rhiD->currentFrameSlot];
3142
3143 bool gotNewHeap = false;
3144 if (!rhiD->ensureShaderVisibleDescriptorHeapCapacity(&rhiD->shaderVisibleCbvSrvUavHeap,
3145 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
3146 rhiD->currentFrameSlot,
3147 (1 + 4) * mipLevelCount * layerCount,
3148 &gotNewHeap))
3149 {
3150 qWarning("Could not ensure enough space in descriptor heap for mipmap generation");
3151 return;
3152 }
3153 if (gotNewHeap)
3154 rhiD->bindShaderVisibleHeaps(cbD);
3155
3156 for (quint32 layer = 0; layer < layerCount; ++layer) {
3157 for (quint32 level = 0; level < mipLevelCount ;) {
3158 UINT subresource = calcSubresource(level, layer, res->desc.MipLevels);
3159 rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
3160 D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
3161 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
3162
3163 quint32 levelPlusOneMipWidth = res->desc.Width >> (level + 1);
3164 quint32 levelPlusOneMipHeight = res->desc.Height >> (level + 1);
3165 const quint32 dw = levelPlusOneMipWidth == 1 ? levelPlusOneMipHeight : levelPlusOneMipWidth;
3166 const quint32 dh = levelPlusOneMipHeight == 1 ? levelPlusOneMipWidth : levelPlusOneMipHeight;
3167 // number of times the size can be halved while still resulting in an even dimension
3168 const quint32 additionalMips = qCountTrailingZeroBits(dw | dh);
3169 const quint32 numGenMips = qMin(1u + qMin(3u, additionalMips), res->desc.MipLevels - level);
3170 levelPlusOneMipWidth = qMax(1u, levelPlusOneMipWidth);
3171 levelPlusOneMipHeight = qMax(1u, levelPlusOneMipHeight);
3172
3173 CBufData cbufData = {
3174 level,
3175 numGenMips,
3176 1.0f / float(levelPlusOneMipWidth),
3177 1.0f / float(levelPlusOneMipHeight)
3178 };
3179
3180 QD3D12StagingArea::Allocation cbuf = workArea->get(sizeof(cbufData));
3181 memcpy(cbuf.p, &cbufData, sizeof(cbufData));
3182 cbD->cmdList->SetComputeRootConstantBufferView(0, cbuf.gpuAddr);
3183
3184 QD3D12Descriptor srv = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(1);
3185 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
3186 srvDesc.Format = res->desc.Format;
3187 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
3188 if (isCubeOrArray) {
3189 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
3190 srvDesc.Texture2DArray.MipLevels = res->desc.MipLevels;
3191 srvDesc.Texture2DArray.FirstArraySlice = layer;
3192 srvDesc.Texture2DArray.ArraySize = 1;
3193 } else {
3194 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
3195 srvDesc.Texture2D.MipLevels = res->desc.MipLevels;
3196 }
3197 rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
3198 cbD->cmdList->SetComputeRootDescriptorTable(1, srv.gpuHandle);
3199
3200 QD3D12Descriptor uavStart = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(4);
3201 D3D12_CPU_DESCRIPTOR_HANDLE uavCpuHandle = uavStart.cpuHandle;
3202 // if level is N, then need UAVs for levels N+1, ..., N+4
3203 for (quint32 uavIdx = 0; uavIdx < 4; ++uavIdx) {
3204 const quint32 uavMipLevel = qMin(level + 1u + uavIdx, res->desc.MipLevels - 1u);
3205 D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
3206 uavDesc.Format = res->desc.Format;
3207 if (isCubeOrArray) {
3208 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
3209 uavDesc.Texture2DArray.MipSlice = uavMipLevel;
3210 uavDesc.Texture2DArray.FirstArraySlice = layer;
3211 uavDesc.Texture2DArray.ArraySize = 1;
3212 } else {
3213 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
3214 uavDesc.Texture2D.MipSlice = uavMipLevel;
3215 }
3216 rhiD->dev->CreateUnorderedAccessView(res->resource, nullptr, &uavDesc, uavCpuHandle);
3217 uavCpuHandle.ptr += descriptorByteSize;
3218 }
3219 cbD->cmdList->SetComputeRootDescriptorTable(2, uavStart.gpuHandle);
3220
3221 cbD->cmdList->Dispatch(levelPlusOneMipWidth, levelPlusOneMipHeight, 1);
3222
3223 rhiD->barrierGen.enqueueUavBarrier(cbD, textureHandle);
3224 rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
3225 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
3226 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
3227
3228 level += numGenMips;
3229 }
3230 }
3231
3232 if (ownStagingArea.has_value())
3233 ownStagingArea->destroyWithDeferredRelease(&rhiD->releaseQueue);
3234}
3235
3236bool QD3D12MipmapGenerator3D::create(QRhiD3D12 *rhiD)
3237{
3238 this->rhiD = rhiD;
3239
3240 D3D12_ROOT_PARAMETER1 rootParams[3] = {};
3241 D3D12_DESCRIPTOR_RANGE1 descriptorRanges[2] = {};
3242
3243 // b0
3244 rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
3245 rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3246 rootParams[0].Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
3247
3248 // t0
3249 descriptorRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
3250 descriptorRanges[0].NumDescriptors = 1;
3251 descriptorRanges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE;
3252 rootParams[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
3253 rootParams[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3254 rootParams[1].DescriptorTable.NumDescriptorRanges = 1;
3255 rootParams[1].DescriptorTable.pDescriptorRanges = &descriptorRanges[0];
3256
3257 // u0
3258 descriptorRanges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
3259 descriptorRanges[1].NumDescriptors = 1;
3260 rootParams[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
3261 rootParams[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3262 rootParams[2].DescriptorTable.NumDescriptorRanges = 1;
3263 rootParams[2].DescriptorTable.pDescriptorRanges = &descriptorRanges[1];
3264
3265 // s0
3266 D3D12_STATIC_SAMPLER_DESC samplerDesc = {};
3267 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
3268 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3269 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3270 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
3271 samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
3272
3273 D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
3274 rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
3275 rsDesc.Desc_1_1.NumParameters = 3;
3276 rsDesc.Desc_1_1.pParameters = rootParams;
3277 rsDesc.Desc_1_1.NumStaticSamplers = 1;
3278 rsDesc.Desc_1_1.pStaticSamplers = &samplerDesc;
3279
3280 ID3DBlob *signature = nullptr;
3281 HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
3282 if (FAILED(hr)) {
3283 qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
3284 return false;
3285 }
3286 ID3D12RootSignature *rootSig = nullptr;
3287 hr = rhiD->dev->CreateRootSignature(0,
3288 signature->GetBufferPointer(),
3289 signature->GetBufferSize(),
3290 __uuidof(ID3D12RootSignature),
3291 reinterpret_cast<void **>(&rootSig));
3292 signature->Release();
3293 if (FAILED(hr)) {
3294 qWarning("Failed to create root signature: %s",
3295 qPrintable(QSystemError::windowsComString(hr)));
3296 return false;
3297 }
3298
3299 rootSigHandle = QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
3300
3301 D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
3302 psoDesc.pRootSignature = rootSig;
3303 psoDesc.CS.pShaderBytecode = g_csMipmap3D;
3304 psoDesc.CS.BytecodeLength = sizeof(g_csMipmap3D);
3305 ID3D12PipelineState *pso = nullptr;
3306 hr = rhiD->dev->CreateComputePipelineState(&psoDesc,
3307 __uuidof(ID3D12PipelineState),
3308 reinterpret_cast<void **>(&pso));
3309 if (FAILED(hr)) {
3310 qWarning("Failed to create compute pipeline state: %s",
3311 qPrintable(QSystemError::windowsComString(hr)));
3312 rhiD->rootSignaturePool.remove(rootSigHandle);
3313 rootSigHandle = {};
3314 return false;
3315 }
3316
3317 pipelineHandle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
3318
3319 return true;
3320}
3321
3322void QD3D12MipmapGenerator3D::destroy()
3323{
3324 rhiD->pipelinePool.remove(pipelineHandle);
3325 pipelineHandle = {};
3326 rhiD->rootSignaturePool.remove(rootSigHandle);
3327 rootSigHandle = {};
3328}
3329
3330void QD3D12MipmapGenerator3D::generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle)
3331{
3332 QD3D12Pipeline *pipeline = rhiD->pipelinePool.lookupRef(pipelineHandle);
3333 if (!pipeline)
3334 return;
3335 QD3D12RootSignature *rootSig = rhiD->rootSignaturePool.lookupRef(rootSigHandle);
3336 if (!rootSig)
3337 return;
3338 QD3D12Resource *res = rhiD->resourcePool.lookupRef(textureHandle);
3339 if (!res)
3340 return;
3341
3342 const quint32 mipLevelCount = res->desc.MipLevels;
3343 if (mipLevelCount < 2)
3344 return;
3345
3346 const bool is3D = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D;
3347 if (!is3D) {
3348 qWarning("3D mipmap generator invoked for non-3D texture, this should not happen");
3349 return;
3350 }
3351
3352 rhiD->barrierGen.addTransitionBarrier(textureHandle, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
3353 rhiD->barrierGen.enqueueBufferedTransitionBarriers(cbD);
3354
3355 cbD->cmdList->SetPipelineState(pipeline->pso);
3356 cbD->cmdList->SetComputeRootSignature(rootSig->rootSig);
3357
3358 const quint32 descriptorByteSize = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].descriptorByteSize;
3359
3360 struct CBufData {
3361 float texelWidth;
3362 float texelHeight;
3363 float texelDepth;
3364 quint32 srcMipLevel;
3365 };
3366
3367 const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(sizeof(CBufData), mipLevelCount);
3368 std::optional<QD3D12StagingArea> ownStagingArea;
3369 if (rhiD->smallStagingAreas[rhiD->currentFrameSlot].remainingCapacity() < allocSize) {
3370 ownStagingArea = QD3D12StagingArea();
3371 if (!ownStagingArea->create(rhiD, allocSize, D3D12_HEAP_TYPE_UPLOAD)) {
3372 qWarning("Could not create staging area for mipmap generation");
3373 return;
3374 }
3375 }
3376 QD3D12StagingArea *workArea = ownStagingArea.has_value()
3377 ? &ownStagingArea.value()
3378 : &rhiD->smallStagingAreas[rhiD->currentFrameSlot];
3379
3380 bool gotNewHeap = false;
3381 if (!rhiD->ensureShaderVisibleDescriptorHeapCapacity(&rhiD->shaderVisibleCbvSrvUavHeap,
3382 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
3383 rhiD->currentFrameSlot,
3384 (1 + 1) * mipLevelCount, // 1 SRV + 1 UAV
3385 &gotNewHeap))
3386 {
3387 qWarning("Could not ensure enough space in descriptor heap for mipmap generation");
3388 return;
3389 }
3390 if (gotNewHeap)
3391 rhiD->bindShaderVisibleHeaps(cbD);
3392
3393 for (quint32 level = 0; level < mipLevelCount; ++level) {
3394 UINT subresource = calcSubresource(level, 0u, res->desc.MipLevels);
3395 rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
3396 D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
3397 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
3398
3399 quint32 levelPlusOneMipWidth = qMax<quint32>(1, res->desc.Width >> (level + 1));
3400 quint32 levelPlusOneMipHeight = qMax<quint32>(1, res->desc.Height >> (level + 1));
3401 quint32 levelPlusOneMipDepth = qMax<quint32>(1, res->desc.DepthOrArraySize >> (level + 1));
3402
3403 CBufData cbufData = {
3404 1.0f / float(levelPlusOneMipWidth),
3405 1.0f / float(levelPlusOneMipHeight),
3406 1.0f / float(levelPlusOneMipDepth),
3407 quint32(level)
3408 };
3409
3410 QD3D12StagingArea::Allocation cbuf = workArea->get(sizeof(cbufData));
3411 memcpy(cbuf.p, &cbufData, sizeof(cbufData));
3412 cbD->cmdList->SetComputeRootConstantBufferView(0, cbuf.gpuAddr);
3413
3414 QD3D12Descriptor srv = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(1);
3415 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
3416 srvDesc.Format = res->desc.Format;
3417 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
3418 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
3419 srvDesc.Texture3D.MipLevels = res->desc.MipLevels;
3420
3421 rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
3422 cbD->cmdList->SetComputeRootDescriptorTable(1, srv.gpuHandle);
3423
3424 QD3D12Descriptor uavStart = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(1);
3425 D3D12_CPU_DESCRIPTOR_HANDLE uavCpuHandle = uavStart.cpuHandle;
3426 const quint32 uavMipLevel = qMin(level + 1u, res->desc.MipLevels - 1u);
3427 D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
3428 uavDesc.Format = res->desc.Format;
3429 uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
3430 uavDesc.Texture3D.MipSlice = uavMipLevel;
3431 uavDesc.Texture3D.WSize = UINT(-1);
3432 rhiD->dev->CreateUnorderedAccessView(res->resource, nullptr, &uavDesc, uavCpuHandle);
3433 uavCpuHandle.ptr += descriptorByteSize;
3434 cbD->cmdList->SetComputeRootDescriptorTable(2, uavStart.gpuHandle);
3435
3436 cbD->cmdList->Dispatch(levelPlusOneMipWidth, levelPlusOneMipHeight, levelPlusOneMipDepth);
3437
3438 rhiD->barrierGen.enqueueUavBarrier(cbD, textureHandle);
3439 rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
3440 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
3441 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
3442 }
3443
3444 if (ownStagingArea.has_value())
3445 ownStagingArea->destroyWithDeferredRelease(&rhiD->releaseQueue);
3446}
3447
3448bool QD3D12MemoryAllocator::create(ID3D12Device *device, IDXGIAdapter1 *adapter)
3449{
3450 this->device = device;
3451
3452 // We can function with and without D3D12MA: CreateCommittedResource is
3453 // just fine for our purposes and not any complicated API-wise; the memory
3454 // allocator is interesting for efficiency mainly since it can suballocate
3455 // instead of making everything a committed resource allocation.
3456
3457 static bool disableMA = qEnvironmentVariableIntValue("QT_D3D_NO_SUBALLOC");
3458 if (disableMA)
3459 return true;
3460
3461 DXGI_ADAPTER_DESC1 desc;
3462 adapter->GetDesc1(&desc);
3463 if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
3464 return true;
3465
3466 D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
3467 allocatorDesc.pDevice = device;
3468 allocatorDesc.pAdapter = adapter;
3469 // A QRhi is supposed to be used from one single thread only. Disable
3470 // the allocator's own mutexes. This may give a performance boost.
3471 allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED;
3472 HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
3473 if (FAILED(hr)) {
3474 qWarning("Failed to initialize D3D12 Memory Allocator: %s",
3475 qPrintable(QSystemError::windowsComString(hr)));
3476 return false;
3477 }
3478 return true;
3479}
3480
3481void QD3D12MemoryAllocator::destroy()
3482{
3483 if (allocator) {
3484 allocator->Release();
3485 allocator = nullptr;
3486 }
3487}
3488
3489HRESULT QD3D12MemoryAllocator::createResource(D3D12_HEAP_TYPE heapType,
3490 const D3D12_RESOURCE_DESC *resourceDesc,
3491 D3D12_RESOURCE_STATES initialState,
3492 const D3D12_CLEAR_VALUE *optimizedClearValue,
3493 D3D12MA::Allocation **maybeAllocation,
3494 REFIID riidResource,
3495 void **ppvResource)
3496{
3497 if (allocator) {
3498 D3D12MA::ALLOCATION_DESC allocDesc = {};
3499 allocDesc.HeapType = heapType;
3500 return allocator->CreateResource(&allocDesc,
3501 resourceDesc,
3502 initialState,
3503 optimizedClearValue,
3504 maybeAllocation,
3505 riidResource,
3506 ppvResource);
3507 } else {
3508 *maybeAllocation = nullptr;
3509 D3D12_HEAP_PROPERTIES heapProps = {};
3510 heapProps.Type = heapType;
3511 return device->CreateCommittedResource(&heapProps,
3512 D3D12_HEAP_FLAG_NONE,
3513 resourceDesc,
3514 initialState,
3515 optimizedClearValue,
3516 riidResource,
3517 ppvResource);
3518 }
3519}
3520
3521void QD3D12MemoryAllocator::getBudget(D3D12MA::Budget *localBudget, D3D12MA::Budget *nonLocalBudget)
3522{
3523 if (allocator) {
3524 allocator->GetBudget(localBudget, nonLocalBudget);
3525 } else {
3526 *localBudget = {};
3527 *nonLocalBudget = {};
3528 }
3529}
3530
3531void QRhiD3D12::waitGpu()
3532{
3533 fullFenceCounter += 1u;
3534 if (SUCCEEDED(cmdQueue->Signal(fullFence, fullFenceCounter))) {
3535 if (SUCCEEDED(fullFence->SetEventOnCompletion(fullFenceCounter, fullFenceEvent)))
3536 WaitForSingleObject(fullFenceEvent, INFINITE);
3537 }
3538}
3539
3540DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const
3541{
3542 DXGI_SAMPLE_DESC desc;
3543 desc.Count = 1;
3544 desc.Quality = 0;
3545
3546 const int s = effectiveSampleCount(sampleCount);
3547
3548 if (s > 1) {
3549 D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
3550 msaaInfo.Format = format;
3551 msaaInfo.SampleCount = UINT(s);
3552 if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) {
3553 if (msaaInfo.NumQualityLevels > 0) {
3554 desc.Count = UINT(s);
3555 desc.Quality = msaaInfo.NumQualityLevels - 1;
3556 } else {
3557 qWarning("No quality levels for multisampling with sample count %d", s);
3558 }
3559 }
3560 }
3561
3562 return desc;
3563}
3564
3565bool QRhiD3D12::startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList)
3566{
3567 ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot];
3568 if (!*cmdList) {
3569 HRESULT hr = dev->CreateCommandList(0,
3570 D3D12_COMMAND_LIST_TYPE_DIRECT,
3571 cmdAlloc,
3572 nullptr,
3573 __uuidof(D3D12GraphicsCommandList),
3574 reinterpret_cast<void **>(cmdList));
3575 if (FAILED(hr)) {
3576 qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(hr)));
3577 return false;
3578 }
3579 } else {
3580 HRESULT hr = (*cmdList)->Reset(cmdAlloc, nullptr);
3581 if (FAILED(hr)) {
3582 qWarning("Failed to reset command list: %s", qPrintable(QSystemError::windowsComString(hr)));
3583 return false;
3584 }
3585 }
3586 return true;
3587}
3588
3589static inline QRhiTexture::Format swapchainReadbackTextureFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
3590{
3591 switch (format) {
3592 case DXGI_FORMAT_R8G8B8A8_UNORM:
3593 return QRhiTexture::RGBA8;
3594 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
3595 if (flags)
3596 (*flags) |= QRhiTexture::sRGB;
3597 return QRhiTexture::RGBA8;
3598 case DXGI_FORMAT_B8G8R8A8_UNORM:
3599 return QRhiTexture::BGRA8;
3600 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
3601 if (flags)
3602 (*flags) |= QRhiTexture::sRGB;
3603 return QRhiTexture::BGRA8;
3604 case DXGI_FORMAT_R16G16B16A16_FLOAT:
3605 return QRhiTexture::RGBA16F;
3606 case DXGI_FORMAT_R32G32B32A32_FLOAT:
3607 return QRhiTexture::RGBA32F;
3608 case DXGI_FORMAT_R10G10B10A2_UNORM:
3609 return QRhiTexture::RGB10A2;
3610 default:
3611 qWarning("DXGI_FORMAT %d cannot be read back", format);
3612 break;
3613 }
3614 return QRhiTexture::UnknownFormat;
3615}
3616
3617void QRhiD3D12::enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
3618{
3619 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
3620
3621 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
3622 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
3623 if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
3624 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
3625 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3626 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
3627 if (u.offset == 0 && u.data.size() == bufD->m_size)
3628 bufD->pendingHostWrites[i].clear();
3629 bufD->pendingHostWrites[i].append({ u.offset, u.data });
3630 }
3631 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
3632 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
3633 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3634 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3635
3636 // The general approach to staging upload data is to first try
3637 // using the per-frame "small" staging area, which is a very simple
3638 // linear allocator; if that's not big enough then create a
3639 // dedicated StagingArea and then deferred-release it to make sure
3640 // if stays alive while the frame is possibly still in flight.
3641
3642 QD3D12StagingArea::Allocation stagingAlloc;
3643 const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(bufD->m_size, 1);
3644 if (smallStagingAreas[currentFrameSlot].remainingCapacity() >= allocSize)
3645 stagingAlloc = smallStagingAreas[currentFrameSlot].get(bufD->m_size);
3646
3647 std::optional<QD3D12StagingArea> ownStagingArea;
3648 if (!stagingAlloc.isValid()) {
3649 ownStagingArea = QD3D12StagingArea();
3650 if (!ownStagingArea->create(this, allocSize, D3D12_HEAP_TYPE_UPLOAD))
3651 continue;
3652 stagingAlloc = ownStagingArea->get(allocSize);
3653 if (!stagingAlloc.isValid()) {
3654 ownStagingArea->destroy();
3655 continue;
3656 }
3657 }
3658
3659 memcpy(stagingAlloc.p + u.offset, u.data.constData(), u.data.size());
3660
3661 barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_COPY_DEST);
3662 barrierGen.enqueueBufferedTransitionBarriers(cbD);
3663
3664 if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
3665 cbD->cmdList->CopyBufferRegion(res->resource,
3666 u.offset,
3667 stagingAlloc.buffer,
3668 stagingAlloc.bufferOffset + u.offset,
3669 u.data.size());
3670 }
3671
3672 if (ownStagingArea.has_value())
3673 ownStagingArea->destroyWithDeferredRelease(&releaseQueue);
3674 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
3675 QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
3676 if (bufD->m_type == QRhiBuffer::Dynamic) {
3677 bufD->executeHostWritesForFrameSlot(currentFrameSlot);
3678 if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[currentFrameSlot])) {
3679 Q_ASSERT(res->cpuMapPtr);
3680 u.result->data.resize(u.readSize);
3681 memcpy(u.result->data.data(), reinterpret_cast<char *>(res->cpuMapPtr) + u.offset, u.readSize);
3682 }
3683 if (u.result->completed)
3684 u.result->completed();
3685 } else {
3686 QD3D12Readback readback;
3687 readback.frameSlot = currentFrameSlot;
3688 readback.result = u.result;
3689 readback.byteSize = u.readSize;
3690 const quint32 allocSize = aligned(u.readSize, QD3D12StagingArea::ALIGNMENT);
3691 if (!readback.staging.create(this, allocSize, D3D12_HEAP_TYPE_READBACK)) {
3692 if (u.result->completed)
3693 u.result->completed();
3694 continue;
3695 }
3696 QD3D12StagingArea::Allocation stagingAlloc = readback.staging.get(u.readSize);
3697 if (!stagingAlloc.isValid()) {
3698 readback.staging.destroy();
3699 if (u.result->completed)
3700 u.result->completed();
3701 continue;
3702 }
3703 Q_ASSERT(stagingAlloc.bufferOffset == 0);
3704 barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_COPY_SOURCE);
3705 barrierGen.enqueueBufferedTransitionBarriers(cbD);
3706 if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
3707 cbD->cmdList->CopyBufferRegion(stagingAlloc.buffer, 0, res->resource, u.offset, u.readSize);
3708 activeReadbacks.append(readback);
3709 } else {
3710 readback.staging.destroy();
3711 if (u.result->completed)
3712 u.result->completed();
3713 }
3714 }
3715 }
3716 }
3717
3718 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
3719 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
3720 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
3721 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.dst);
3722 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3723 QD3D12Resource *res = resourcePool.lookupRef(texD->handle);
3724 if (!res)
3725 continue;
3726 barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_COPY_DEST);
3727 barrierGen.enqueueBufferedTransitionBarriers(cbD);
3728 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
3729 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3730 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) {
3731 D3D12_SUBRESOURCE_FOOTPRINT footprint = {};
3732 footprint.Format = res->desc.Format;
3733 footprint.Depth = 1;
3734 quint32 totalBytes = 0;
3735
3736 const QSize subresSize = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
3737 : subresDesc.sourceSize();
3738 const QPoint srcPos = subresDesc.sourceTopLeft();
3739 QPoint dstPos = subresDesc.destinationTopLeft();
3740
3741 if (!subresDesc.image().isNull()) {
3742 const QImage img = subresDesc.image();
3743 const int bpl = img.bytesPerLine();
3744 footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
3745 totalBytes = footprint.RowPitch * img.height();
3746 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
3747 QSize blockDim;
3748 quint32 bpl = 0;
3749 compressedFormatInfo(texD->m_format, subresSize, &bpl, nullptr, &blockDim);
3750 footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
3751 const int rowCount = aligned(subresSize.height(), blockDim.height()) / blockDim.height();
3752 totalBytes = footprint.RowPitch * rowCount;
3753 } else if (!subresDesc.data().isEmpty()) {
3754 quint32 bpl = 0;
3755 if (subresDesc.dataStride())
3756 bpl = subresDesc.dataStride();
3757 else
3758 textureFormatInfo(texD->m_format, subresSize, &bpl, nullptr, nullptr);
3759 footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
3760 totalBytes = footprint.RowPitch * subresSize.height();
3761 } else {
3762 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3763 continue;
3764 }
3765
3766 const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(totalBytes, 1);
3767 QD3D12StagingArea::Allocation stagingAlloc;
3768 if (smallStagingAreas[currentFrameSlot].remainingCapacity() >= allocSize)
3769 stagingAlloc = smallStagingAreas[currentFrameSlot].get(allocSize);
3770
3771 std::optional<QD3D12StagingArea> ownStagingArea;
3772 if (!stagingAlloc.isValid()) {
3773 ownStagingArea = QD3D12StagingArea();
3774 if (!ownStagingArea->create(this, allocSize, D3D12_HEAP_TYPE_UPLOAD))
3775 continue;
3776 stagingAlloc = ownStagingArea->get(allocSize);
3777 if (!stagingAlloc.isValid()) {
3778 ownStagingArea->destroy();
3779 continue;
3780 }
3781 }
3782
3783 D3D12_TEXTURE_COPY_LOCATION dst;
3784 dst.pResource = res->resource;
3785 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3786 dst.SubresourceIndex = calcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount);
3787 D3D12_TEXTURE_COPY_LOCATION src;
3788 src.pResource = stagingAlloc.buffer;
3789 src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
3790 src.PlacedFootprint.Offset = stagingAlloc.bufferOffset;
3791
3792 D3D12_BOX srcBox; // back, right, bottom are exclusive
3793
3794 if (!subresDesc.image().isNull()) {
3795 const QImage img = subresDesc.image();
3796 const int bpc = qMax(1, img.depth() / 8);
3797 const int bpl = img.bytesPerLine();
3798
3799 QSize size = subresDesc.sourceSize().isEmpty() ? img.size() : subresDesc.sourceSize();
3800 size.setWidth(qMin(size.width(), img.width() - srcPos.x()));
3801 size.setHeight(qMin(size.height(), img.height() - srcPos.y()));
3802 size = clampedSubResourceUploadSize(size, dstPos, level, texD->m_pixelSize);
3803
3804 footprint.Width = size.width();
3805 footprint.Height = size.height();
3806
3807 srcBox.left = 0;
3808 srcBox.top = 0;
3809 srcBox.right = UINT(size.width());
3810 srcBox.bottom = UINT(size.height());
3811 srcBox.front = 0;
3812 srcBox.back = 1;
3813
3814 const uchar *imgPtr = img.constBits();
3815 const quint32 lineBytes = size.width() * bpc;
3816 for (int y = 0, h = size.height(); y < h; ++y) {
3817 memcpy(stagingAlloc.p + y * footprint.RowPitch,
3818 imgPtr + srcPos.x() * bpc + (y + srcPos.y()) * bpl,
3819 lineBytes);
3820 }
3821 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
3822 QSize blockDim;
3823 quint32 bpl = 0;
3824 compressedFormatInfo(texD->m_format, subresSize, &bpl, nullptr, &blockDim);
3825 // x and y must be multiples of the block width and height
3826 dstPos.setX(aligned(dstPos.x(), blockDim.width()));
3827 dstPos.setY(aligned(dstPos.y(), blockDim.height()));
3828
3829 srcBox.left = 0;
3830 srcBox.top = 0;
3831 // width and height must be multiples of the block width and height
3832 srcBox.right = aligned(subresSize.width(), blockDim.width());
3833 srcBox.bottom = aligned(subresSize.height(), blockDim.height());
3834
3835 srcBox.front = 0;
3836 srcBox.back = 1;
3837
3838 footprint.Width = aligned(subresSize.width(), blockDim.width());
3839 footprint.Height = aligned(subresSize.height(), blockDim.height());
3840
3841 const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
3842 const QByteArray imgData = subresDesc.data();
3843 const char *imgPtr = imgData.constData();
3844 const int rowCount = aligned(subresSize.height(), blockDim.height()) / blockDim.height();
3845 for (int y = 0; y < rowCount; ++y)
3846 memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
3847 } else if (!subresDesc.data().isEmpty()) {
3848 srcBox.left = 0;
3849 srcBox.top = 0;
3850 srcBox.right = subresSize.width();
3851 srcBox.bottom = subresSize.height();
3852 srcBox.front = 0;
3853 srcBox.back = 1;
3854
3855 footprint.Width = subresSize.width();
3856 footprint.Height = subresSize.height();
3857
3858 quint32 bpl = 0;
3859 if (subresDesc.dataStride())
3860 bpl = subresDesc.dataStride();
3861 else
3862 textureFormatInfo(texD->m_format, subresSize, &bpl, nullptr, nullptr);
3863
3864 const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
3865 const QByteArray data = subresDesc.data();
3866 const char *imgPtr = data.constData();
3867 for (int y = 0, h = subresSize.height(); y < h; ++y)
3868 memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
3869 }
3870
3871 src.PlacedFootprint.Footprint = footprint;
3872
3873 cbD->cmdList->CopyTextureRegion(&dst,
3874 UINT(dstPos.x()),
3875 UINT(dstPos.y()),
3876 is3D ? UINT(layer) : 0u,
3877 &src,
3878 &srcBox);
3879
3880 if (ownStagingArea.has_value())
3881 ownStagingArea->destroyWithDeferredRelease(&releaseQueue);
3882 }
3883 }
3884 }
3885 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
3886 Q_ASSERT(u.src && u.dst);
3887 QD3D12Texture *srcD = QRHI_RES(QD3D12Texture, u.src);
3888 QD3D12Texture *dstD = QRHI_RES(QD3D12Texture, u.dst);
3889 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3890 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3891 QD3D12Resource *srcRes = resourcePool.lookupRef(srcD->handle);
3892 QD3D12Resource *dstRes = resourcePool.lookupRef(dstD->handle);
3893 if (!srcRes || !dstRes)
3894 continue;
3895
3896 barrierGen.addTransitionBarrier(srcD->handle, D3D12_RESOURCE_STATE_COPY_SOURCE);
3897 barrierGen.addTransitionBarrier(dstD->handle, D3D12_RESOURCE_STATE_COPY_DEST);
3898 barrierGen.enqueueBufferedTransitionBarriers(cbD);
3899
3900 const UINT srcSubresource = calcSubresource(UINT(u.desc.sourceLevel()),
3901 srcIs3D ? 0u : UINT(u.desc.sourceLayer()),
3902 srcD->mipLevelCount);
3903 const UINT dstSubresource = calcSubresource(UINT(u.desc.destinationLevel()),
3904 dstIs3D ? 0u : UINT(u.desc.destinationLayer()),
3905 dstD->mipLevelCount);
3906 const QPoint dp = u.desc.destinationTopLeft();
3907 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3908 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3909 const QPoint sp = u.desc.sourceTopLeft();
3910
3911 D3D12_BOX srcBox;
3912 srcBox.left = UINT(sp.x());
3913 srcBox.top = UINT(sp.y());
3914 srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
3915 // back, right, bottom are exclusive
3916 srcBox.right = srcBox.left + UINT(copySize.width());
3917 srcBox.bottom = srcBox.top + UINT(copySize.height());
3918 srcBox.back = srcBox.front + 1;
3919
3920 D3D12_TEXTURE_COPY_LOCATION src;
3921 src.pResource = srcRes->resource;
3922 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3923 src.SubresourceIndex = srcSubresource;
3924 D3D12_TEXTURE_COPY_LOCATION dst;
3925 dst.pResource = dstRes->resource;
3926 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3927 dst.SubresourceIndex = dstSubresource;
3928
3929 cbD->cmdList->CopyTextureRegion(&dst,
3930 UINT(dp.x()),
3931 UINT(dp.y()),
3932 dstIs3D ? UINT(u.desc.destinationLayer()) : 0u,
3933 &src,
3934 &srcBox);
3935 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
3936 QD3D12Readback readback;
3937 readback.frameSlot = currentFrameSlot;
3938 readback.result = u.result;
3939
3940 QD3D12ObjectHandle srcHandle;
3941 QRect rect;
3942 bool is3D = false;
3943 if (u.rb.texture()) {
3944 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.rb.texture());
3945 if (texD->sampleDesc.Count > 1) {
3946 qWarning("Multisample texture cannot be read back");
3947 continue;
3948 }
3949 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3950 if (u.rb.rect().isValid())
3951 rect = u.rb.rect();
3952 else
3953 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3954 readback.format = texD->m_format;
3955 srcHandle = texD->handle;
3956 } else {
3957 Q_ASSERT(currentSwapChain);
3958 if (u.rb.rect().isValid())
3959 rect = u.rb.rect();
3960 else
3961 rect = QRect({0, 0}, currentSwapChain->pixelSize);
3962 readback.format = swapchainReadbackTextureFormat(currentSwapChain->colorFormat, nullptr);
3963 if (readback.format == QRhiTexture::UnknownFormat)
3964 continue;
3965 srcHandle = currentSwapChain->colorBuffers[currentSwapChain->currentBackBufferIndex];
3966 }
3967 readback.pixelSize = rect.size();
3968
3969 textureFormatInfo(readback.format,
3970 readback.pixelSize,
3971 &readback.bytesPerLine,
3972 &readback.byteSize,
3973 nullptr);
3974
3975 QD3D12Resource *srcRes = resourcePool.lookupRef(srcHandle);
3976 if (!srcRes)
3977 continue;
3978
3979 const UINT subresource = calcSubresource(UINT(u.rb.level()),
3980 is3D ? 0u : UINT(u.rb.layer()),
3981 srcRes->desc.MipLevels);
3982 D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
3983 // totalBytes is what we get from D3D, with the 256 aligned stride,
3984 // readback.byteSize is the final result that's not relevant here yet
3985 UINT64 totalBytes = 0;
3986 dev->GetCopyableFootprints(&srcRes->desc, subresource, 1, 0,
3987 &layout, nullptr, nullptr, &totalBytes);
3988 readback.stagingRowPitch = layout.Footprint.RowPitch;
3989
3990 const quint32 allocSize = aligned<quint32>(totalBytes, QD3D12StagingArea::ALIGNMENT);
3991 if (!readback.staging.create(this, allocSize, D3D12_HEAP_TYPE_READBACK)) {
3992 if (u.result->completed)
3993 u.result->completed();
3994 continue;
3995 }
3996 QD3D12StagingArea::Allocation stagingAlloc = readback.staging.get(totalBytes);
3997 if (!stagingAlloc.isValid()) {
3998 readback.staging.destroy();
3999 if (u.result->completed)
4000 u.result->completed();
4001 continue;
4002 }
4003 Q_ASSERT(stagingAlloc.bufferOffset == 0);
4004
4005 barrierGen.addTransitionBarrier(srcHandle, D3D12_RESOURCE_STATE_COPY_SOURCE);
4006 barrierGen.enqueueBufferedTransitionBarriers(cbD);
4007
4008 D3D12_TEXTURE_COPY_LOCATION dst;
4009 dst.pResource = stagingAlloc.buffer;
4010 dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
4011 dst.PlacedFootprint.Offset = 0;
4012 dst.PlacedFootprint.Footprint = layout.Footprint;
4013
4014 D3D12_TEXTURE_COPY_LOCATION src;
4015 src.pResource = srcRes->resource;
4016 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
4017 src.SubresourceIndex = subresource;
4018
4019 D3D12_BOX srcBox = {};
4020 srcBox.left = UINT(rect.left());
4021 srcBox.top = UINT(rect.top());
4022 srcBox.front = is3D ? UINT(u.rb.layer()) : 0u;
4023 // back, right, bottom are exclusive
4024 srcBox.right = srcBox.left + UINT(rect.width());
4025 srcBox.bottom = srcBox.top + UINT(rect.height());
4026 srcBox.back = srcBox.front + 1;
4027
4028 cbD->cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, &srcBox);
4029 activeReadbacks.append(readback);
4030 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
4031 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.dst);
4032 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
4033 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional))
4034 mipmapGen3D.generate(cbD, texD->handle);
4035 else
4036 mipmapGen.generate(cbD, texD->handle);
4037 }
4038 }
4039
4040 ud->free();
4041}
4042
4043void QRhiD3D12::finishActiveReadbacks(bool forced)
4044{
4045 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
4046
4047 for (int i = activeReadbacks.size() - 1; i >= 0; --i) {
4048 QD3D12Readback &readback(activeReadbacks[i]);
4049 if (forced || currentFrameSlot == readback.frameSlot || readback.frameSlot < 0) {
4050 readback.result->format = readback.format;
4051 readback.result->pixelSize = readback.pixelSize;
4052 readback.result->data.resize(int(readback.byteSize));
4053
4054 if (readback.format != QRhiTexture::UnknownFormat) {
4055 quint8 *dstPtr = reinterpret_cast<quint8 *>(readback.result->data.data());
4056 const quint8 *srcPtr = readback.staging.mem.p;
4057 const quint32 lineSize = qMin(readback.bytesPerLine, readback.stagingRowPitch);
4058 for (int y = 0, h = readback.pixelSize.height(); y < h; ++y)
4059 memcpy(dstPtr + y * readback.bytesPerLine, srcPtr + y * readback.stagingRowPitch, lineSize);
4060 } else {
4061 memcpy(readback.result->data.data(), readback.staging.mem.p, readback.byteSize);
4062 }
4063
4064 readback.staging.destroy();
4065
4066 if (readback.result->completed)
4067 completedCallbacks.append(readback.result->completed);
4068
4069 activeReadbacks.remove(i);
4070 }
4071 }
4072
4073 for (auto f : completedCallbacks)
4074 f();
4075}
4076
4077bool QRhiD3D12::ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
4078 D3D12_DESCRIPTOR_HEAP_TYPE type,
4079 int frameSlot,
4080 quint32 neededDescriptorCount,
4081 bool *gotNew)
4082{
4083 // Gets a new heap if needed. Note that the capacity we get is clamped
4084 // automatically (e.g. to 1 million, or 2048 for samplers), so * 2 does not
4085 // mean we can grow indefinitely, then again even using the same size would
4086 // work (because we what we are after here is a new heap for the rest of
4087 // the commands, not affecting what's already recorded).
4088 if (h->perFrameHeapSlice[frameSlot].remainingCapacity() < neededDescriptorCount) {
4089 const quint32 newPerFrameSize = qMax(h->perFrameHeapSlice[frameSlot].capacity * 2,
4090 neededDescriptorCount);
4091 QD3D12ShaderVisibleDescriptorHeap newHeap;
4092 if (!newHeap.create(dev, type, newPerFrameSize)) {
4093 qWarning("Could not create new shader-visible descriptor heap");
4094 return false;
4095 }
4096 h->destroyWithDeferredRelease(&releaseQueue);
4097 *h = newHeap;
4098 *gotNew = true;
4099 }
4100 return true;
4101}
4102
4103void QRhiD3D12::bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD)
4104{
4105 ID3D12DescriptorHeap *heaps[] = {
4106 shaderVisibleCbvSrvUavHeap.heap.heap,
4107 samplerMgr.shaderVisibleSamplerHeap.heap.heap
4108 };
4109 cbD->cmdList->SetDescriptorHeaps(2, heaps);
4110}
4111
4112QD3D12Buffer::QD3D12Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
4113 : QRhiBuffer(rhi, type, usage, size)
4114{
4115}
4116
4117QD3D12Buffer::~QD3D12Buffer()
4118{
4119 destroy();
4120}
4121
4122void QD3D12Buffer::destroy()
4123{
4124 if (handles[0].isNull())
4125 return;
4126
4127 QRHI_RES_RHI(QRhiD3D12);
4128
4129 // destroy() implementations, unlike other functions, are expected to test
4130 // for m_rhi (rhiD) being null, to allow surviving in case one attempts to
4131 // destroy a (leaked) resource after the QRhi.
4132 //
4133 // If there is no QRhi anymore, we do not deferred-release but that's fine
4134 // since the QRhi already released everything that was in the resourcePool.
4135
4136 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4137 if (rhiD)
4138 rhiD->releaseQueue.deferredReleaseResource(handles[i]);
4139 handles[i] = {};
4140 pendingHostWrites[i].clear();
4141 }
4142
4143 if (rhiD)
4144 rhiD->unregisterResource(this);
4145}
4146
4147bool QD3D12Buffer::create()
4148{
4149 if (!handles[0].isNull())
4150 destroy();
4151
4152 if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
4153 qWarning("UniformBuffer must always be Dynamic");
4154 return false;
4155 }
4156
4157 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
4158 qWarning("StorageBuffer cannot be combined with Dynamic");
4159 return false;
4160 }
4161
4162 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
4163 const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
4164
4165 UINT resourceFlags = D3D12_RESOURCE_FLAG_NONE;
4166 if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
4167 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4168
4169 QRHI_RES_RHI(QRhiD3D12);
4170 HRESULT hr = 0;
4171 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4172 if (i == 0 || m_type == Dynamic) {
4173 D3D12_RESOURCE_DESC resourceDesc = {};
4174 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
4175 resourceDesc.Width = roundedSize;
4176 resourceDesc.Height = 1;
4177 resourceDesc.DepthOrArraySize = 1;
4178 resourceDesc.MipLevels = 1;
4179 resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
4180 resourceDesc.SampleDesc = { 1, 0 };
4181 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
4182 resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
4183 ID3D12Resource *resource = nullptr;
4184 D3D12MA::Allocation *allocation = nullptr;
4185 // Dynamic == host (CPU) visible
4186 D3D12_HEAP_TYPE heapType = m_type == Dynamic
4187 ? D3D12_HEAP_TYPE_UPLOAD
4188 : D3D12_HEAP_TYPE_DEFAULT;
4189 D3D12_RESOURCE_STATES resourceState = m_type == Dynamic
4190 ? D3D12_RESOURCE_STATE_GENERIC_READ
4191 : D3D12_RESOURCE_STATE_COMMON;
4192 hr = rhiD->vma.createResource(heapType,
4193 &resourceDesc,
4194 resourceState,
4195 nullptr,
4196 &allocation,
4197 __uuidof(resource),
4198 reinterpret_cast<void **>(&resource));
4199 if (FAILED(hr))
4200 break;
4201 if (!m_objectName.isEmpty()) {
4202 QString decoratedName = QString::fromUtf8(m_objectName);
4203 if (m_type == Dynamic) {
4204 decoratedName += QLatin1Char('/');
4205 decoratedName += QString::number(i);
4206 }
4207 resource->SetName(reinterpret_cast<LPCWSTR>(decoratedName.utf16()));
4208 }
4209 void *cpuMemPtr = nullptr;
4210 if (m_type == Dynamic) {
4211 // will be mapped for ever on the CPU, this makes future host write operations very simple
4212 hr = resource->Map(0, nullptr, &cpuMemPtr);
4213 if (FAILED(hr)) {
4214 qWarning("Map() failed to dynamic buffer");
4215 resource->Release();
4216 if (allocation)
4217 allocation->Release();
4218 break;
4219 }
4220 }
4221 handles[i] = QD3D12Resource::addToPool(&rhiD->resourcePool,
4222 resource,
4223 resourceState,
4224 allocation,
4225 cpuMemPtr);
4226 }
4227 }
4228 if (FAILED(hr)) {
4229 qWarning("Failed to create buffer: '%s' Type was %d, size was %u, using D3D12MA was %d.",
4230 qPrintable(QSystemError::windowsComString(hr)),
4231 int(m_type),
4232 roundedSize,
4233 int(rhiD->vma.isUsingD3D12MA()));
4234 return false;
4235 }
4236
4237 rhiD->registerResource(this);
4238 return true;
4239}
4240
4241QRhiBuffer::NativeBuffer QD3D12Buffer::nativeBuffer()
4242{
4243 NativeBuffer b;
4244 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QD3D12_FRAMES_IN_FLIGHT));
4245 QRHI_RES_RHI(QRhiD3D12);
4246 if (m_type == Dynamic) {
4247 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4248 executeHostWritesForFrameSlot(i);
4249 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[i]))
4250 b.objects[i] = res->resource;
4251 else
4252 b.objects[i] = nullptr;
4253 }
4254 b.slotCount = QD3D12_FRAMES_IN_FLIGHT;
4255 return b;
4256 }
4257 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[0]))
4258 b.objects[0] = res->resource;
4259 else
4260 b.objects[0] = nullptr;
4261 b.slotCount = 1;
4262 return b;
4263}
4264
4265char *QD3D12Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
4266{
4267 // Shortcut the entire buffer update mechanism and allow the client to do
4268 // the host writes directly to the buffer. This will lead to unexpected
4269 // results when combined with QRhiResourceUpdateBatch-based updates for the
4270 // buffer, but provides a fast path for dynamic buffers that have all their
4271 // content changed in every frame.
4272
4273 Q_ASSERT(m_type == Dynamic);
4274 QRHI_RES_RHI(QRhiD3D12);
4275 Q_ASSERT(rhiD->inFrame);
4276 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[rhiD->currentFrameSlot]))
4277 return static_cast<char *>(res->cpuMapPtr);
4278
4279 return nullptr;
4280}
4281
4282void QD3D12Buffer::endFullDynamicBufferUpdateForCurrentFrame()
4283{
4284 // nothing to do here
4285}
4286
4287void QD3D12Buffer::executeHostWritesForFrameSlot(int frameSlot)
4288{
4289 if (pendingHostWrites[frameSlot].isEmpty())
4290 return;
4291
4292 Q_ASSERT(m_type == QRhiBuffer::Dynamic);
4293 QRHI_RES_RHI(QRhiD3D12);
4294 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[frameSlot])) {
4295 Q_ASSERT(res->cpuMapPtr);
4296 for (const QD3D12Buffer::HostWrite &u : std::as_const(pendingHostWrites[frameSlot]))
4297 memcpy(static_cast<char *>(res->cpuMapPtr) + u.offset, u.data.constData(), u.data.size());
4298 }
4299 pendingHostWrites[frameSlot].clear();
4300}
4301
4302static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
4303{
4304 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
4305 switch (format) {
4306 case QRhiTexture::RGBA8:
4307 return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
4308 case QRhiTexture::BGRA8:
4309 return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
4310 case QRhiTexture::R8:
4311 return DXGI_FORMAT_R8_UNORM;
4312 case QRhiTexture::R8SI:
4313 return DXGI_FORMAT_R8_SINT;
4314 case QRhiTexture::R8UI:
4315 return DXGI_FORMAT_R8_UINT;
4316 case QRhiTexture::RG8:
4317 return DXGI_FORMAT_R8G8_UNORM;
4318 case QRhiTexture::R16:
4319 return DXGI_FORMAT_R16_UNORM;
4320 case QRhiTexture::RG16:
4321 return DXGI_FORMAT_R16G16_UNORM;
4322 case QRhiTexture::RED_OR_ALPHA8:
4323 return DXGI_FORMAT_R8_UNORM;
4324
4325 case QRhiTexture::RGBA16F:
4326 return DXGI_FORMAT_R16G16B16A16_FLOAT;
4327 case QRhiTexture::RGBA32F:
4328 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4329 case QRhiTexture::R16F:
4330 return DXGI_FORMAT_R16_FLOAT;
4331 case QRhiTexture::R32F:
4332 return DXGI_FORMAT_R32_FLOAT;
4333
4334 case QRhiTexture::RGB10A2:
4335 return DXGI_FORMAT_R10G10B10A2_UNORM;
4336
4337 case QRhiTexture::R32SI:
4338 return DXGI_FORMAT_R32_SINT;
4339 case QRhiTexture::R32UI:
4340 return DXGI_FORMAT_R32_UINT;
4341 case QRhiTexture::RG32SI:
4342 return DXGI_FORMAT_R32G32_SINT;
4343 case QRhiTexture::RG32UI:
4344 return DXGI_FORMAT_R32G32_UINT;
4345 case QRhiTexture::RGBA32SI:
4346 return DXGI_FORMAT_R32G32B32A32_SINT;
4347 case QRhiTexture::RGBA32UI:
4348 return DXGI_FORMAT_R32G32B32A32_UINT;
4349
4350 case QRhiTexture::D16:
4351 return DXGI_FORMAT_R16_TYPELESS;
4352 case QRhiTexture::D24:
4353 return DXGI_FORMAT_R24G8_TYPELESS;
4354 case QRhiTexture::D24S8:
4355 return DXGI_FORMAT_R24G8_TYPELESS;
4356 case QRhiTexture::D32F:
4357 return DXGI_FORMAT_R32_TYPELESS;
4358 case QRhiTexture::Format::D32FS8:
4359 return DXGI_FORMAT_R32G8X24_TYPELESS;
4360
4361 case QRhiTexture::BC1:
4362 return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
4363 case QRhiTexture::BC2:
4364 return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
4365 case QRhiTexture::BC3:
4366 return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
4367 case QRhiTexture::BC4:
4368 return DXGI_FORMAT_BC4_UNORM;
4369 case QRhiTexture::BC5:
4370 return DXGI_FORMAT_BC5_UNORM;
4371 case QRhiTexture::BC6H:
4372 return DXGI_FORMAT_BC6H_UF16;
4373 case QRhiTexture::BC7:
4374 return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
4375
4376 case QRhiTexture::ETC2_RGB8:
4377 case QRhiTexture::ETC2_RGB8A1:
4378 case QRhiTexture::ETC2_RGBA8:
4379 qWarning("QRhiD3D12 does not support ETC2 textures");
4380 return DXGI_FORMAT_R8G8B8A8_UNORM;
4381
4382 case QRhiTexture::ASTC_4x4:
4383 case QRhiTexture::ASTC_5x4:
4384 case QRhiTexture::ASTC_5x5:
4385 case QRhiTexture::ASTC_6x5:
4386 case QRhiTexture::ASTC_6x6:
4387 case QRhiTexture::ASTC_8x5:
4388 case QRhiTexture::ASTC_8x6:
4389 case QRhiTexture::ASTC_8x8:
4390 case QRhiTexture::ASTC_10x5:
4391 case QRhiTexture::ASTC_10x6:
4392 case QRhiTexture::ASTC_10x8:
4393 case QRhiTexture::ASTC_10x10:
4394 case QRhiTexture::ASTC_12x10:
4395 case QRhiTexture::ASTC_12x12:
4396 qWarning("QRhiD3D12 does not support ASTC textures");
4397 return DXGI_FORMAT_R8G8B8A8_UNORM;
4398
4399 default:
4400 break;
4401 }
4402 return DXGI_FORMAT_R8G8B8A8_UNORM;
4403}
4404
4405QD3D12RenderBuffer::QD3D12RenderBuffer(QRhiImplementation *rhi,
4406 Type type,
4407 const QSize &pixelSize,
4408 int sampleCount,
4409 Flags flags,
4410 QRhiTexture::Format backingFormatHint)
4411 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
4412{
4413}
4414
4415QD3D12RenderBuffer::~QD3D12RenderBuffer()
4416{
4417 destroy();
4418}
4419
4420void QD3D12RenderBuffer::destroy()
4421{
4422 if (handle.isNull())
4423 return;
4424
4425 QRHI_RES_RHI(QRhiD3D12);
4426 if (rhiD) {
4427 if (rtv.isValid())
4428 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->rtvPool, rtv, 1);
4429 else if (dsv.isValid())
4430 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->dsvPool, dsv, 1);
4431 }
4432
4433 handle = {};
4434 rtv = {};
4435 dsv = {};
4436
4437 if (rhiD)
4438 rhiD->unregisterResource(this);
4439}
4440
4441bool QD3D12RenderBuffer::create()
4442{
4443 if (!handle.isNull())
4444 destroy();
4445
4446 if (m_pixelSize.isEmpty())
4447 return false;
4448
4449 QRHI_RES_RHI(QRhiD3D12);
4450
4451 switch (m_type) {
4452 case QRhiRenderBuffer::Color:
4453 {
4454 dxgiFormat = toD3DTextureFormat(backingFormat(), {});
4455 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4456 D3D12_RESOURCE_DESC resourceDesc = {};
4457 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
4458 resourceDesc.Width = UINT64(m_pixelSize.width());
4459 resourceDesc.Height = UINT(m_pixelSize.height());
4460 resourceDesc.DepthOrArraySize = 1;
4461 resourceDesc.MipLevels = 1;
4462 resourceDesc.Format = dxgiFormat;
4463 resourceDesc.SampleDesc = sampleDesc;
4464 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4465 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
4466 D3D12_CLEAR_VALUE clearValue = {};
4467 clearValue.Format = dxgiFormat;
4468 // have a separate allocation and resource object (meaning both will need its own Release())
4469 ID3D12Resource *resource = nullptr;
4470 D3D12MA::Allocation *allocation = nullptr;
4471 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4472 &resourceDesc,
4473 D3D12_RESOURCE_STATE_RENDER_TARGET,
4474 &clearValue,
4475 &allocation,
4476 __uuidof(ID3D12Resource),
4477 reinterpret_cast<void **>(&resource));
4478 if (FAILED(hr)) {
4479 qWarning("Failed to create color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
4480 return false;
4481 }
4482 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
4483 rtv = rhiD->rtvPool.allocate(1);
4484 if (!rtv.isValid())
4485 return false;
4486 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
4487 rtvDesc.Format = dxgiFormat;
4488 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
4489 : D3D12_RTV_DIMENSION_TEXTURE2D;
4490 rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, rtv.cpuHandle);
4491 }
4492 break;
4493 case QRhiRenderBuffer::DepthStencil:
4494 {
4495 dxgiFormat = DS_FORMAT;
4496 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4497 D3D12_RESOURCE_DESC resourceDesc = {};
4498 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
4499 resourceDesc.Width = UINT64(m_pixelSize.width());
4500 resourceDesc.Height = UINT(m_pixelSize.height());
4501 resourceDesc.DepthOrArraySize = 1;
4502 resourceDesc.MipLevels = 1;
4503 resourceDesc.Format = dxgiFormat;
4504 resourceDesc.SampleDesc = sampleDesc;
4505 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4506 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
4507 if (m_flags.testFlag(UsedWithSwapChainOnly))
4508 resourceDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
4509 D3D12_CLEAR_VALUE clearValue = {};
4510 clearValue.Format = dxgiFormat;
4511 clearValue.DepthStencil.Depth = 1.0f;
4512 clearValue.DepthStencil.Stencil = 0;
4513 ID3D12Resource *resource = nullptr;
4514 D3D12MA::Allocation *allocation = nullptr;
4515 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4516 &resourceDesc,
4517 D3D12_RESOURCE_STATE_DEPTH_WRITE,
4518 &clearValue,
4519 &allocation,
4520 __uuidof(ID3D12Resource),
4521 reinterpret_cast<void **>(&resource));
4522 if (FAILED(hr)) {
4523 qWarning("Failed to create depth-stencil buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
4524 return false;
4525 }
4526 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_DEPTH_WRITE, allocation);
4527 dsv = rhiD->dsvPool.allocate(1);
4528 if (!dsv.isValid())
4529 return false;
4530 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
4531 dsvDesc.Format = dxgiFormat;
4532 dsvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
4533 : D3D12_DSV_DIMENSION_TEXTURE2D;
4534 rhiD->dev->CreateDepthStencilView(resource, &dsvDesc, dsv.cpuHandle);
4535 }
4536 break;
4537 }
4538
4539 if (!m_objectName.isEmpty()) {
4540 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
4541 const QString name = QString::fromUtf8(m_objectName);
4542 res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
4543 }
4544 }
4545
4546 generation += 1;
4547 rhiD->registerResource(this);
4548 return true;
4549}
4550
4551QRhiTexture::Format QD3D12RenderBuffer::backingFormat() const
4552{
4553 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4554 return m_backingFormatHint;
4555 else
4556 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4557}
4558
4559QD3D12Texture::QD3D12Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
4560 int arraySize, int sampleCount, Flags flags)
4561 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
4562{
4563}
4564
4565QD3D12Texture::~QD3D12Texture()
4566{
4567 destroy();
4568}
4569
4570void QD3D12Texture::destroy()
4571{
4572 if (handle.isNull())
4573 return;
4574
4575 QRHI_RES_RHI(QRhiD3D12);
4576 if (rhiD)
4577 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->cbvSrvUavPool, srv, 1);
4578
4579 handle = {};
4580 srv = {};
4581
4582 if (rhiD)
4583 rhiD->unregisterResource(this);
4584}
4585
4586static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
4587{
4588 switch (format) {
4589 case QRhiTexture::Format::D16:
4590 return DXGI_FORMAT_R16_FLOAT;
4591 case QRhiTexture::Format::D24:
4592 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
4593 case QRhiTexture::Format::D24S8:
4594 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
4595 case QRhiTexture::Format::D32F:
4596 return DXGI_FORMAT_R32_FLOAT;
4597 case QRhiTexture::Format::D32FS8:
4598 return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
4599 default:
4600 break;
4601 }
4602 Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32_FLOAT);
4603}
4604
4605static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
4606{
4607 // here the result cannot be typeless
4608 switch (format) {
4609 case QRhiTexture::Format::D16:
4610 return DXGI_FORMAT_D16_UNORM;
4611 case QRhiTexture::Format::D24:
4612 return DXGI_FORMAT_D24_UNORM_S8_UINT;
4613 case QRhiTexture::Format::D24S8:
4614 return DXGI_FORMAT_D24_UNORM_S8_UINT;
4615 case QRhiTexture::Format::D32F:
4616 return DXGI_FORMAT_D32_FLOAT;
4617 case QRhiTexture::Format::D32FS8:
4618 return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
4619 default:
4620 break;
4621 }
4622 Q_UNREACHABLE_RETURN(DXGI_FORMAT_D32_FLOAT);
4623}
4624
4625static inline bool isDepthTextureFormat(QRhiTexture::Format format)
4626{
4627 switch (format) {
4628 case QRhiTexture::Format::D16:
4629 case QRhiTexture::Format::D24:
4630 case QRhiTexture::Format::D24S8:
4631 case QRhiTexture::Format::D32F:
4632 case QRhiTexture::Format::D32FS8:
4633 return true;
4634 default:
4635 return false;
4636 }
4637}
4638
4639bool QD3D12Texture::prepareCreate(QSize *adjustedSize)
4640{
4641 if (!handle.isNull())
4642 destroy();
4643
4644 QRHI_RES_RHI(QRhiD3D12);
4645 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
4646 return false;
4647
4648 const bool isDepth = isDepthTextureFormat(m_format);
4649 const bool isCube = m_flags.testFlag(CubeMap);
4650 const bool is3D = m_flags.testFlag(ThreeDimensional);
4651 const bool isArray = m_flags.testFlag(TextureArray);
4652 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4653 const bool is1D = m_flags.testFlag(OneDimensional);
4654
4655 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4656 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4657
4658 dxgiFormat = toD3DTextureFormat(m_format, m_flags);
4659 if (isDepth) {
4660 srvFormat = toD3DDepthTextureSRVFormat(m_format);
4661 rtFormat = toD3DDepthTextureDSVFormat(m_format);
4662 } else {
4663 srvFormat = dxgiFormat;
4664 rtFormat = dxgiFormat;
4665 }
4666 if (m_writeViewFormat.format != UnknownFormat) {
4667 if (isDepth)
4668 rtFormat = toD3DDepthTextureDSVFormat(m_writeViewFormat.format);
4669 else
4670 rtFormat = toD3DTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
4671 }
4672 if (m_readViewFormat.format != UnknownFormat) {
4673 if (isDepth)
4674 srvFormat = toD3DDepthTextureSRVFormat(m_readViewFormat.format);
4675 else
4676 srvFormat = toD3DTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
4677 }
4678
4679 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
4680 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4681 if (sampleDesc.Count > 1) {
4682 if (isCube) {
4683 qWarning("Cubemap texture cannot be multisample");
4684 return false;
4685 }
4686 if (is3D) {
4687 qWarning("3D texture cannot be multisample");
4688 return false;
4689 }
4690 if (hasMipMaps) {
4691 qWarning("Multisample texture cannot have mipmaps");
4692 return false;
4693 }
4694 }
4695 if (isDepth && hasMipMaps) {
4696 qWarning("Depth texture cannot have mipmaps");
4697 return false;
4698 }
4699 if (isCube && is3D) {
4700 qWarning("Texture cannot be both cube and 3D");
4701 return false;
4702 }
4703 if (isArray && is3D) {
4704 qWarning("Texture cannot be both array and 3D");
4705 return false;
4706 }
4707 if (isCube && is1D) {
4708 qWarning("Texture cannot be both cube and 1D");
4709 return false;
4710 }
4711 if (is1D && is3D) {
4712 qWarning("Texture cannot be both 1D and 3D");
4713 return false;
4714 }
4715 if (m_depth > 1 && !is3D) {
4716 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
4717 return false;
4718 }
4719 if (m_arraySize > 0 && !isArray) {
4720 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
4721 return false;
4722 }
4723 if (m_arraySize < 1 && isArray) {
4724 qWarning("Texture is an array but array size is %d", m_arraySize);
4725 return false;
4726 }
4727
4728 if (adjustedSize)
4729 *adjustedSize = size;
4730
4731 return true;
4732}
4733
4734bool QD3D12Texture::finishCreate()
4735{
4736 QRHI_RES_RHI(QRhiD3D12);
4737 const bool isCube = m_flags.testFlag(CubeMap);
4738 const bool is3D = m_flags.testFlag(ThreeDimensional);
4739 const bool isArray = m_flags.testFlag(TextureArray);
4740 const bool is1D = m_flags.testFlag(OneDimensional);
4741
4742 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
4743 srvDesc.Format = srvFormat;
4744 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
4745
4746 if (isCube) {
4747 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
4748 srvDesc.TextureCube.MipLevels = mipLevelCount;
4749 } else {
4750 if (is1D) {
4751 if (isArray) {
4752 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
4753 srvDesc.Texture1DArray.MipLevels = mipLevelCount;
4754 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4755 srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
4756 srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
4757 } else {
4758 srvDesc.Texture1DArray.FirstArraySlice = 0;
4759 srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
4760 }
4761 } else {
4762 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
4763 srvDesc.Texture1D.MipLevels = mipLevelCount;
4764 }
4765 } else if (isArray) {
4766 if (sampleDesc.Count > 1) {
4767 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
4768 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4769 srvDesc.Texture2DMSArray.FirstArraySlice = UINT(m_arrayRangeStart);
4770 srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
4771 } else {
4772 srvDesc.Texture2DMSArray.FirstArraySlice = 0;
4773 srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
4774 }
4775 } else {
4776 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
4777 srvDesc.Texture2DArray.MipLevels = mipLevelCount;
4778 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4779 srvDesc.Texture2DArray.FirstArraySlice = UINT(m_arrayRangeStart);
4780 srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
4781 } else {
4782 srvDesc.Texture2DArray.FirstArraySlice = 0;
4783 srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
4784 }
4785 }
4786 } else {
4787 if (sampleDesc.Count > 1) {
4788 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
4789 } else if (is3D) {
4790 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
4791 srvDesc.Texture3D.MipLevels = mipLevelCount;
4792 } else {
4793 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
4794 srvDesc.Texture2D.MipLevels = mipLevelCount;
4795 }
4796 }
4797 }
4798
4799 srv = rhiD->cbvSrvUavPool.allocate(1);
4800 if (!srv.isValid())
4801 return false;
4802
4803 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
4804 rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
4805 if (!m_objectName.isEmpty()) {
4806 const QString name = QString::fromUtf8(m_objectName);
4807 res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
4808 }
4809 } else {
4810 return false;
4811 }
4812
4813 generation += 1;
4814 return true;
4815}
4816
4817bool QD3D12Texture::create()
4818{
4819 QSize size;
4820 if (!prepareCreate(&size))
4821 return false;
4822
4823 const bool isDepth = isDepthTextureFormat(m_format);
4824 const bool isCube = m_flags.testFlag(CubeMap);
4825 const bool is3D = m_flags.testFlag(ThreeDimensional);
4826 const bool isArray = m_flags.testFlag(TextureArray);
4827 const bool is1D = m_flags.testFlag(OneDimensional);
4828
4829 QRHI_RES_RHI(QRhiD3D12);
4830
4831 bool needsOptimizedClearValueSpecified = false;
4832 UINT resourceFlags = 0;
4833 if (m_flags.testFlag(RenderTarget) || sampleDesc.Count > 1) {
4834 if (isDepth)
4835 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
4836 else
4837 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
4838 needsOptimizedClearValueSpecified = true;
4839 }
4840 if (m_flags.testFlag(UsedWithGenerateMips)) {
4841 if (isDepth) {
4842 qWarning("Depth texture cannot have mipmaps generated");
4843 return false;
4844 }
4845 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4846 }
4847 if (m_flags.testFlag(UsedWithLoadStore))
4848 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4849
4850 D3D12_RESOURCE_DESC resourceDesc = {};
4851 resourceDesc.Dimension = is1D ? D3D12_RESOURCE_DIMENSION_TEXTURE1D
4852 : (is3D ? D3D12_RESOURCE_DIMENSION_TEXTURE3D
4853 : D3D12_RESOURCE_DIMENSION_TEXTURE2D);
4854 resourceDesc.Width = UINT64(size.width());
4855 resourceDesc.Height = UINT(size.height());
4856 resourceDesc.DepthOrArraySize = isCube ? 6
4857 : (isArray ? UINT(qMax(0, m_arraySize))
4858 : (is3D ? qMax(1, m_depth)
4859 : 1));
4860 resourceDesc.MipLevels = mipLevelCount;
4861 resourceDesc.Format = dxgiFormat;
4862 resourceDesc.SampleDesc = sampleDesc;
4863 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4864 resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
4865 D3D12_CLEAR_VALUE clearValue = {};
4866 clearValue.Format = dxgiFormat;
4867 if (isDepth) {
4868 clearValue.Format = toD3DDepthTextureDSVFormat(m_format);
4869 clearValue.DepthStencil.Depth = 1.0f;
4870 clearValue.DepthStencil.Stencil = 0;
4871 }
4872 ID3D12Resource *resource = nullptr;
4873 D3D12MA::Allocation *allocation = nullptr;
4874 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4875 &resourceDesc,
4876 D3D12_RESOURCE_STATE_COMMON,
4877 needsOptimizedClearValueSpecified ? &clearValue : nullptr,
4878 &allocation,
4879 __uuidof(ID3D12Resource),
4880 reinterpret_cast<void **>(&resource));
4881 if (FAILED(hr)) {
4882 qWarning("Failed to create texture: '%s'"
4883 " Dim was %d Size was %ux%u Depth/ArraySize was %u MipLevels was %u Format was %d Sample count was %d",
4884 qPrintable(QSystemError::windowsComString(hr)),
4885 int(resourceDesc.Dimension),
4886 uint(resourceDesc.Width),
4887 uint(resourceDesc.Height),
4888 uint(resourceDesc.DepthOrArraySize),
4889 uint(resourceDesc.MipLevels),
4890 int(resourceDesc.Format),
4891 int(resourceDesc.SampleDesc.Count));
4892 return false;
4893 }
4894
4895 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_COMMON, allocation);
4896
4897 if (!finishCreate())
4898 return false;
4899
4900 rhiD->registerResource(this);
4901 return true;
4902}
4903
4904bool QD3D12Texture::createFrom(QRhiTexture::NativeTexture src)
4905{
4906 if (!src.object)
4907 return false;
4908
4909 if (!prepareCreate())
4910 return false;
4911
4912 ID3D12Resource *resource = reinterpret_cast<ID3D12Resource *>(src.object);
4913 D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATES(src.layout);
4914
4915 QRHI_RES_RHI(QRhiD3D12);
4916 handle = QD3D12Resource::addNonOwningToPool(&rhiD->resourcePool, resource, state);
4917
4918 if (!finishCreate())
4919 return false;
4920
4921 rhiD->registerResource(this);
4922 return true;
4923}
4924
4925QRhiTexture::NativeTexture QD3D12Texture::nativeTexture()
4926{
4927 QRHI_RES_RHI(QRhiD3D12);
4928 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
4929 return { quint64(res->resource), int(res->state) };
4930
4931 return {};
4932}
4933
4934void QD3D12Texture::setNativeLayout(int layout)
4935{
4936 QRHI_RES_RHI(QRhiD3D12);
4937 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
4938 res->state = D3D12_RESOURCE_STATES(layout);
4939}
4940
4941QD3D12Sampler::QD3D12Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
4942 AddressMode u, AddressMode v, AddressMode w)
4943 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
4944{
4945}
4946
4947QD3D12Sampler::~QD3D12Sampler()
4948{
4949 destroy();
4950}
4951
4952void QD3D12Sampler::destroy()
4953{
4954 shaderVisibleDescriptor = {};
4955
4956 QRHI_RES_RHI(QRhiD3D12);
4957 if (rhiD)
4958 rhiD->unregisterResource(this);
4959}
4960
4961static inline D3D12_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
4962{
4963 if (minFilter == QRhiSampler::Nearest) {
4964 if (magFilter == QRhiSampler::Nearest) {
4965 if (mipFilter == QRhiSampler::Linear)
4966 return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
4967 else
4968 return D3D12_FILTER_MIN_MAG_MIP_POINT;
4969 } else {
4970 if (mipFilter == QRhiSampler::Linear)
4971 return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
4972 else
4973 return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
4974 }
4975 } else {
4976 if (magFilter == QRhiSampler::Nearest) {
4977 if (mipFilter == QRhiSampler::Linear)
4978 return D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
4979 else
4980 return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
4981 } else {
4982 if (mipFilter == QRhiSampler::Linear)
4983 return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
4984 else
4985 return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
4986 }
4987 }
4988 Q_UNREACHABLE_RETURN(D3D12_FILTER_MIN_MAG_MIP_LINEAR);
4989}
4990
4991static inline D3D12_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
4992{
4993 switch (m) {
4994 case QRhiSampler::Repeat:
4995 return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
4996 case QRhiSampler::ClampToEdge:
4997 return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
4998 case QRhiSampler::Mirror:
4999 return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
5000 }
5001 Q_UNREACHABLE_RETURN(D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
5002}
5003
5004static inline D3D12_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
5005{
5006 switch (op) {
5007 case QRhiSampler::Never:
5008 return D3D12_COMPARISON_FUNC_NEVER;
5009 case QRhiSampler::Less:
5010 return D3D12_COMPARISON_FUNC_LESS;
5011 case QRhiSampler::Equal:
5012 return D3D12_COMPARISON_FUNC_EQUAL;
5013 case QRhiSampler::LessOrEqual:
5014 return D3D12_COMPARISON_FUNC_LESS_EQUAL;
5015 case QRhiSampler::Greater:
5016 return D3D12_COMPARISON_FUNC_GREATER;
5017 case QRhiSampler::NotEqual:
5018 return D3D12_COMPARISON_FUNC_NOT_EQUAL;
5019 case QRhiSampler::GreaterOrEqual:
5020 return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
5021 case QRhiSampler::Always:
5022 return D3D12_COMPARISON_FUNC_ALWAYS;
5023 }
5024 Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_NEVER);
5025}
5026
5027bool QD3D12Sampler::create()
5028{
5029 desc = {};
5030 desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
5031 if (m_compareOp != Never)
5032 desc.Filter = D3D12_FILTER(desc.Filter | 0x80);
5033 desc.AddressU = toD3DAddressMode(m_addressU);
5034 desc.AddressV = toD3DAddressMode(m_addressV);
5035 desc.AddressW = toD3DAddressMode(m_addressW);
5036 desc.MaxAnisotropy = 1.0f;
5037 desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
5038 desc.MaxLOD = m_mipmapMode == None ? 0.0f : 10000.0f;
5039
5040 QRHI_RES_RHI(QRhiD3D12);
5041 rhiD->registerResource(this, false);
5042 return true;
5043}
5044
5045QD3D12Descriptor QD3D12Sampler::lookupOrCreateShaderVisibleDescriptor()
5046{
5047 if (!shaderVisibleDescriptor.isValid()) {
5048 QRHI_RES_RHI(QRhiD3D12);
5049 shaderVisibleDescriptor = rhiD->samplerMgr.getShaderVisibleDescriptor(desc);
5050 }
5051 return shaderVisibleDescriptor;
5052}
5053
5054QD3D12ShadingRateMap::QD3D12ShadingRateMap(QRhiImplementation *rhi)
5055 : QRhiShadingRateMap(rhi)
5056{
5057}
5058
5059QD3D12ShadingRateMap::~QD3D12ShadingRateMap()
5060{
5061 destroy();
5062}
5063
5064void QD3D12ShadingRateMap::destroy()
5065{
5066 if (handle.isNull())
5067 return;
5068
5069 handle = {};
5070}
5071
5072bool QD3D12ShadingRateMap::createFrom(QRhiTexture *src)
5073{
5074 if (!handle.isNull())
5075 destroy();
5076
5077 handle = QRHI_RES(QD3D12Texture, src)->handle;
5078
5079 return true;
5080}
5081
5082QD3D12TextureRenderTarget::QD3D12TextureRenderTarget(QRhiImplementation *rhi,
5083 const QRhiTextureRenderTargetDescription &desc,
5084 Flags flags)
5085 : QRhiTextureRenderTarget(rhi, desc, flags),
5086 d(rhi)
5087{
5088}
5089
5090QD3D12TextureRenderTarget::~QD3D12TextureRenderTarget()
5091{
5092 destroy();
5093}
5094
5095void QD3D12TextureRenderTarget::destroy()
5096{
5097 if (!rtv[0].isValid() && !dsv.isValid())
5098 return;
5099
5100 QRHI_RES_RHI(QRhiD3D12);
5101 if (dsv.isValid()) {
5102 if (ownsDsv && rhiD)
5103 rhiD->releaseQueue.deferredReleaseViews(&rhiD->dsvPool, dsv, 1);
5104 dsv = {};
5105 }
5106
5107 for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
5108 if (rtv[i].isValid()) {
5109 if (ownsRtv[i] && rhiD)
5110 rhiD->releaseQueue.deferredReleaseViews(&rhiD->rtvPool, rtv[i], 1);
5111 rtv[i] = {};
5112 }
5113 }
5114
5115 if (rhiD)
5116 rhiD->unregisterResource(this);
5117}
5118
5119QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDescriptor()
5120{
5121 // not yet built so cannot rely on data computed in create()
5122
5123 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
5124
5125 rpD->colorAttachmentCount = 0;
5126 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it) {
5127 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
5128 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
5129 if (texD)
5130 rpD->colorFormat[rpD->colorAttachmentCount] = texD->rtFormat;
5131 else if (rbD)
5132 rpD->colorFormat[rpD->colorAttachmentCount] = rbD->dxgiFormat;
5133 rpD->colorAttachmentCount += 1;
5134 }
5135
5136 rpD->hasDepthStencil = false;
5137 if (m_desc.depthStencilBuffer()) {
5138 rpD->hasDepthStencil = true;
5139 rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
5140 } else if (m_desc.depthTexture()) {
5141 QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
5142 rpD->hasDepthStencil = true;
5143 rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
5144 }
5145
5146 rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
5147
5148 rpD->updateSerializedFormat();
5149
5150 QRHI_RES_RHI(QRhiD3D12);
5151 rhiD->registerResource(rpD);
5152 return rpD;
5153}
5154
5155bool QD3D12TextureRenderTarget::create()
5156{
5157 if (rtv[0].isValid() || dsv.isValid())
5158 destroy();
5159
5160 QRHI_RES_RHI(QRhiD3D12);
5161 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
5162 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
5163 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5164 d.colorAttCount = 0;
5165 int attIndex = 0;
5166
5167 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
5168 d.colorAttCount += 1;
5169 const QRhiColorAttachment &colorAtt(*it);
5170 QRhiTexture *texture = colorAtt.texture();
5171 QRhiRenderBuffer *rb = colorAtt.renderBuffer();
5172 Q_ASSERT(texture || rb);
5173 if (texture) {
5174 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, texture);
5175 QD3D12Resource *res = rhiD->resourcePool.lookupRef(texD->handle);
5176 if (!res) {
5177 qWarning("Could not look up texture handle for render target");
5178 return false;
5179 }
5180 const bool isMultiView = it->multiViewCount() >= 2;
5181 UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1;
5182 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5183 rtvDesc.Format = texD->rtFormat;
5184 if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
5185 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
5186 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
5187 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
5188 rtvDesc.Texture2DArray.ArraySize = layerCount;
5189 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
5190 if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
5191 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
5192 rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
5193 rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
5194 rtvDesc.Texture1DArray.ArraySize = layerCount;
5195 } else {
5196 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D;
5197 rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
5198 }
5199 } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
5200 if (texD->sampleDesc.Count > 1) {
5201 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
5202 rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
5203 rtvDesc.Texture2DMSArray.ArraySize = layerCount;
5204 } else {
5205 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
5206 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
5207 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
5208 rtvDesc.Texture2DArray.ArraySize = layerCount;
5209 }
5210 } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
5211 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
5212 rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
5213 rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
5214 rtvDesc.Texture3D.WSize = layerCount;
5215 } else {
5216 if (texD->sampleDesc.Count > 1) {
5217 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
5218 } else {
5219 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
5220 rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
5221 }
5222 }
5223 rtv[attIndex] = rhiD->rtvPool.allocate(1);
5224 if (!rtv[attIndex].isValid()) {
5225 qWarning("Failed to allocate RTV for texture render target");
5226 return false;
5227 }
5228 rhiD->dev->CreateRenderTargetView(res->resource, &rtvDesc, rtv[attIndex].cpuHandle);
5229 ownsRtv[attIndex] = true;
5230 if (attIndex == 0) {
5231 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
5232 d.sampleCount = int(texD->sampleDesc.Count);
5233 }
5234 } else if (rb) {
5235 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, rb);
5236 ownsRtv[attIndex] = false;
5237 rtv[attIndex] = rbD->rtv;
5238 if (attIndex == 0) {
5239 d.pixelSize = rbD->pixelSize();
5240 d.sampleCount = int(rbD->sampleDesc.Count);
5241 }
5242 }
5243 }
5244
5245 d.dpr = 1;
5246
5247 if (hasDepthStencil) {
5248 if (m_desc.depthTexture()) {
5249 ownsDsv = true;
5250 QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
5251 QD3D12Resource *res = rhiD->resourcePool.lookupRef(depthTexD->handle);
5252 if (!res) {
5253 qWarning("Could not look up depth texture handle");
5254 return false;
5255 }
5256 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
5257 dsvDesc.Format = depthTexD->rtFormat;
5258 dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
5259 : D3D12_DSV_DIMENSION_TEXTURE2D;
5260 if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
5261 if (depthTexD->sampleDesc.Count > 1) {
5262 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY;
5263 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
5264 dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
5265 dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
5266 } else {
5267 dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
5268 dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
5269 }
5270 } else {
5271 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
5272 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
5273 dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
5274 dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
5275 } else {
5276 dsvDesc.Texture2DArray.FirstArraySlice = 0;
5277 dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
5278 }
5279 }
5280 }
5281 dsv = rhiD->dsvPool.allocate(1);
5282 if (!dsv.isValid()) {
5283 qWarning("Failed to allocate DSV for texture render target");
5284 return false;
5285 }
5286 rhiD->dev->CreateDepthStencilView(res->resource, &dsvDesc, dsv.cpuHandle);
5287 if (d.colorAttCount == 0) {
5288 d.pixelSize = depthTexD->pixelSize();
5289 d.sampleCount = int(depthTexD->sampleDesc.Count);
5290 }
5291 } else {
5292 ownsDsv = false;
5293 QD3D12RenderBuffer *depthRbD = QRHI_RES(QD3D12RenderBuffer, m_desc.depthStencilBuffer());
5294 dsv = depthRbD->dsv;
5295 if (d.colorAttCount == 0) {
5296 d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
5297 d.sampleCount = int(depthRbD->sampleDesc.Count);
5298 }
5299 }
5300 d.dsAttCount = 1;
5301 } else {
5302 d.dsAttCount = 0;
5303 }
5304
5305 D3D12_CPU_DESCRIPTOR_HANDLE nullDescHandle = { 0 };
5306 for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
5307 d.rtv[i] = i < d.colorAttCount ? rtv[i].cpuHandle : nullDescHandle;
5308 d.dsv = dsv.cpuHandle;
5309 d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
5310
5311 QRhiRenderTargetAttachmentTracker::updateResIdList<QD3D12Texture, QD3D12RenderBuffer>(m_desc, &d.currentResIdList);
5312
5313 rhiD->registerResource(this);
5314 return true;
5315}
5316
5317QSize QD3D12TextureRenderTarget::pixelSize() const
5318{
5319 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D12Texture, QD3D12RenderBuffer>(m_desc, d.currentResIdList))
5320 const_cast<QD3D12TextureRenderTarget *>(this)->create();
5321
5322 return d.pixelSize;
5323}
5324
5325float QD3D12TextureRenderTarget::devicePixelRatio() const
5326{
5327 return d.dpr;
5328}
5329
5330int QD3D12TextureRenderTarget::sampleCount() const
5331{
5332 return d.sampleCount;
5333}
5334
5335QD3D12ShaderResourceBindings::QD3D12ShaderResourceBindings(QRhiImplementation *rhi)
5336 : QRhiShaderResourceBindings(rhi)
5337{
5338}
5339
5340QD3D12ShaderResourceBindings::~QD3D12ShaderResourceBindings()
5341{
5342 destroy();
5343}
5344
5345void QD3D12ShaderResourceBindings::destroy()
5346{
5347 QRHI_RES_RHI(QRhiD3D12);
5348 if (rhiD)
5349 rhiD->unregisterResource(this);
5350}
5351
5352bool QD3D12ShaderResourceBindings::create()
5353{
5354 QRHI_RES_RHI(QRhiD3D12);
5355 if (!rhiD->sanityCheckShaderResourceBindings(this))
5356 return false;
5357
5358 rhiD->updateLayoutDesc(this);
5359
5360 hasDynamicOffset = false;
5361 for (const QRhiShaderResourceBinding &b : std::as_const(m_bindings)) {
5362 const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
5363 if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
5364 hasDynamicOffset = true;
5365 break;
5366 }
5367 }
5368
5369 // The root signature is not part of the srb. Unintuitive, but the shader
5370 // translation pipeline ties our hands: as long as the per-shader (so per
5371 // stage!) nativeResourceBindingMap exist, meaning f.ex. that a SPIR-V
5372 // combined image sampler binding X passed in here may map to the tY and sY
5373 // HLSL registers, where Y is known only once the mapping table from the
5374 // shader is looked up. Creating a root parameters at this stage is
5375 // therefore impossible.
5376
5377 generation += 1;
5378 rhiD->registerResource(this, false);
5379 return true;
5380}
5381
5382void QD3D12ShaderResourceBindings::updateResources(UpdateFlags flags)
5383{
5384 Q_UNUSED(flags);
5385 generation += 1;
5386}
5387
5388// Accessing the QRhiBuffer/Texture/Sampler resources must be avoided in the
5389// callbacks; that would only be possible if the srb had those specified, and
5390// that's not required at the time of srb and pipeline create() time, and
5391// createRootSignature is called from the pipeline create().
5392
5393void QD3D12ShaderResourceBindings::visitUniformBuffer(QD3D12Stage s,
5394 const QRhiShaderResourceBinding::Data::UniformBufferData &,
5395 int shaderRegister,
5396 int)
5397{
5398 D3D12_ROOT_PARAMETER1 rootParam = {};
5399 rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
5400 rootParam.ShaderVisibility = qd3d12_stageToVisibility(s);
5401 rootParam.Descriptor.ShaderRegister = shaderRegister;
5402 rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
5403 visitorData.cbParams[s].append(rootParam);
5404}
5405
5406void QD3D12ShaderResourceBindings::visitTexture(QD3D12Stage s,
5407 const QRhiShaderResourceBinding::TextureAndSampler &,
5408 int shaderRegister)
5409{
5410 D3D12_DESCRIPTOR_RANGE1 range = {};
5411 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
5412 range.NumDescriptors = 1;
5413 range.BaseShaderRegister = shaderRegister;
5414 range.OffsetInDescriptorsFromTableStart = visitorData.currentSrvRangeOffset[s];
5415 visitorData.currentSrvRangeOffset[s] += 1;
5416 visitorData.srvRanges[s].append(range);
5417 if (visitorData.srvRanges[s].count() == 1) {
5418 visitorData.srvTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5419 visitorData.srvTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5420 }
5421}
5422
5423void QD3D12ShaderResourceBindings::visitSampler(QD3D12Stage s,
5424 const QRhiShaderResourceBinding::TextureAndSampler &,
5425 int shaderRegister)
5426{
5427 // Unlike SRVs and UAVs, samplers are handled so that each sampler becomes
5428 // a root parameter with its own descriptor table.
5429
5430 int &rangeStoreIdx(visitorData.samplerRangeHeads[s]);
5431 if (rangeStoreIdx == 16) {
5432 qWarning("Sampler count in QD3D12Stage %d exceeds the limit of 16, this is disallowed by QRhi", s);
5433 return;
5434 }
5435 D3D12_DESCRIPTOR_RANGE1 range = {};
5436 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
5437 range.NumDescriptors = 1;
5438 range.BaseShaderRegister = shaderRegister;
5439 visitorData.samplerRanges[s][rangeStoreIdx] = range;
5440 D3D12_ROOT_PARAMETER1 param = {};
5441 param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5442 param.ShaderVisibility = qd3d12_stageToVisibility(s);
5443 param.DescriptorTable.NumDescriptorRanges = 1;
5444 param.DescriptorTable.pDescriptorRanges = &visitorData.samplerRanges[s][rangeStoreIdx];
5445 rangeStoreIdx += 1;
5446 visitorData.samplerTables[s].append(param);
5447}
5448
5449void QD3D12ShaderResourceBindings::visitStorageBuffer(QD3D12Stage s,
5450 const QRhiShaderResourceBinding::Data::StorageBufferData &,
5451 QD3D12ShaderResourceVisitor::StorageOp,
5452 int shaderRegister)
5453{
5454 D3D12_DESCRIPTOR_RANGE1 range = {};
5455 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
5456 range.NumDescriptors = 1;
5457 range.BaseShaderRegister = shaderRegister;
5458 range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
5459 visitorData.currentUavRangeOffset[s] += 1;
5460 visitorData.uavRanges[s].append(range);
5461 if (visitorData.uavRanges[s].count() == 1) {
5462 visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5463 visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5464 }
5465}
5466
5467void QD3D12ShaderResourceBindings::visitStorageImage(QD3D12Stage s,
5468 const QRhiShaderResourceBinding::Data::StorageImageData &,
5469 QD3D12ShaderResourceVisitor::StorageOp,
5470 int shaderRegister)
5471{
5472 D3D12_DESCRIPTOR_RANGE1 range = {};
5473 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
5474 range.NumDescriptors = 1;
5475 range.BaseShaderRegister = shaderRegister;
5476 range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
5477 visitorData.currentUavRangeOffset[s] += 1;
5478 visitorData.uavRanges[s].append(range);
5479 if (visitorData.uavRanges[s].count() == 1) {
5480 visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5481 visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5482 }
5483}
5484
5485QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D12ShaderStageData *stageData,
5486 int stageCount)
5487{
5488 QRHI_RES_RHI(QRhiD3D12);
5489
5490 // It's not just that the root signature has to be tied to the pipeline
5491 // (cannot just freely create it like e.g. with Vulkan where one just
5492 // creates a descriptor layout 1:1 with the QRhiShaderResourceBindings'
5493 // data), due to not knowing the shader-specific resource binding mapping
5494 // tables at the point of srb creation, but each shader stage may have a
5495 // different mapping table. (ugh!)
5496 //
5497 // Hence we set up everything per-stage, even if it means the root
5498 // signature gets unnecessarily big. (note that the magic is in the
5499 // ShaderVisibility: even though the register range is the same in the
5500 // descriptor tables, the visibility is different)
5501
5502 QD3D12ShaderResourceVisitor visitor(this, stageData, stageCount);
5503
5504 visitorData = {};
5505
5506 using namespace std::placeholders;
5507 visitor.uniformBuffer = std::bind(&QD3D12ShaderResourceBindings::visitUniformBuffer, this, _1, _2, _3, _4);
5508 visitor.texture = std::bind(&QD3D12ShaderResourceBindings::visitTexture, this, _1, _2, _3);
5509 visitor.sampler = std::bind(&QD3D12ShaderResourceBindings::visitSampler, this, _1, _2, _3);
5510 visitor.storageBuffer = std::bind(&QD3D12ShaderResourceBindings::visitStorageBuffer, this, _1, _2, _3, _4);
5511 visitor.storageImage = std::bind(&QD3D12ShaderResourceBindings::visitStorageImage, this, _1, _2, _3, _4);
5512
5513 visitor.visit();
5514
5515 // The maximum size of a root signature is 256 bytes, where a descriptor
5516 // table is 4, a root descriptor (e.g. CBV) is 8. We have 5 stages at most
5517 // (or 1 with compute) and a separate descriptor table for SRVs (->
5518 // textures) and UAVs (-> storage buffers and images) per stage, plus each
5519 // uniform buffer counts as a CBV in the stages it is visible.
5520 //
5521 // Due to the limited maximum size of a shader-visible sampler heap (2048)
5522 // and the potential costly switching of descriptor heaps, each sampler is
5523 // declared as a separate root parameter / descriptor table (meaning that
5524 // two samplers in the same stage are two parameters and two tables, not
5525 // just one). QRhi documents a hard limit of 16 on texture/sampler bindings
5526 // in a shader (matching D3D11), so we can hopefully get away with this.
5527 //
5528 // This means that e.g. a vertex+fragment shader with a uniform buffer
5529 // visible in both and one texture+sampler in the fragment shader would
5530 // consume 2*8 + 4 + 4 = 24 bytes. This also implies that clients
5531 // specifying the minimal stage bit mask for each entry in
5532 // QRhiShaderResourceBindings are ideal for this backend since it helps
5533 // reducing the chance of hitting the size limit.
5534
5535 QVarLengthArray<D3D12_ROOT_PARAMETER1, 4> rootParams;
5536 for (int s = 0; s < 6; ++s) {
5537 if (!visitorData.cbParams[s].isEmpty())
5538 rootParams.append(visitorData.cbParams[s].constData(), visitorData.cbParams[s].count());
5539 }
5540 for (int s = 0; s < 6; ++s) {
5541 if (!visitorData.srvRanges[s].isEmpty()) {
5542 visitorData.srvTables[s].DescriptorTable.NumDescriptorRanges = visitorData.srvRanges[s].count();
5543 visitorData.srvTables[s].DescriptorTable.pDescriptorRanges = visitorData.srvRanges[s].constData();
5544 rootParams.append(visitorData.srvTables[s]);
5545 }
5546 }
5547 for (int s = 0; s < 6; ++s) {
5548 if (!visitorData.samplerTables[s].isEmpty())
5549 rootParams.append(visitorData.samplerTables[s].constData(), visitorData.samplerTables[s].count());
5550 }
5551 for (int s = 0; s < 6; ++s) {
5552 if (!visitorData.uavRanges[s].isEmpty()) {
5553 visitorData.uavTables[s].DescriptorTable.NumDescriptorRanges = visitorData.uavRanges[s].count();
5554 visitorData.uavTables[s].DescriptorTable.pDescriptorRanges = visitorData.uavRanges[s].constData();
5555 rootParams.append(visitorData.uavTables[s]);
5556 }
5557 }
5558
5559 D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
5560 rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
5561 if (!rootParams.isEmpty()) {
5562 rsDesc.Desc_1_1.NumParameters = rootParams.count();
5563 rsDesc.Desc_1_1.pParameters = rootParams.constData();
5564 }
5565
5566 UINT rsFlags = 0;
5567 for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) {
5568 if (stageData[stageIdx].valid && stageData[stageIdx].stage == VS)
5569 rsFlags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
5570 }
5571 rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags);
5572
5573 ID3DBlob *signature = nullptr;
5574 HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
5575 if (FAILED(hr)) {
5576 qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
5577 return {};
5578 }
5579 ID3D12RootSignature *rootSig = nullptr;
5580 hr = rhiD->dev->CreateRootSignature(0,
5581 signature->GetBufferPointer(),
5582 signature->GetBufferSize(),
5583 __uuidof(ID3D12RootSignature),
5584 reinterpret_cast<void **>(&rootSig));
5585 signature->Release();
5586 if (FAILED(hr)) {
5587 qWarning("Failed to create root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
5588 return {};
5589 }
5590
5591 return QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
5592}
5593
5594// For shader model < 6.0 we do the same as the D3D11 backend: use the old
5595// compiler (D3DCompile) to generate DXBC, just as qsb does (when -c is passed)
5596// by invoking fxc, not dxc. For SM >= 6.0 we have to use the new compiler and
5597// work with DXIL. And that involves IDxcCompiler and needs the presence of
5598// dxcompiler.dll and dxil.dll at runtime. Plus there's a chance we have
5599// ancient SDK headers when not using MSVC. So this is heavily optional,
5600// meaning support for dxc can be disabled both at build time (no dxcapi.h) and
5601// at run time (no DLLs).
5602
5603static inline void makeHlslTargetString(char target[7], const char stage[3], int version)
5604{
5605 const int smMajor = version / 10;
5606 const int smMinor = version % 10;
5607 target[0] = stage[0];
5608 target[1] = stage[1];
5609 target[2] = '_';
5610 target[3] = '0' + smMajor;
5611 target[4] = '_';
5612 target[5] = '0' + smMinor;
5613 target[6] = '\0';
5614}
5615
5616enum class HlslCompileFlag
5617{
5618 WithDebugInfo = 0x01
5619};
5620
5621static QByteArray legacyCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
5622{
5623 static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
5624 if (!d3dCompile) {
5625 qWarning("Unable to resolve function D3DCompile()");
5626 return QByteArray();
5627 }
5628
5629 ID3DBlob *bytecode = nullptr;
5630 ID3DBlob *errors = nullptr;
5631 UINT d3dCompileFlags = 0;
5632 if (flags & int(HlslCompileFlag::WithDebugInfo))
5633 d3dCompileFlags |= D3DCOMPILE_DEBUG;
5634
5635 HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
5636 nullptr, nullptr, nullptr,
5637 hlslSource.entryPoint().constData(), target, d3dCompileFlags, 0, &bytecode, &errors);
5638 if (FAILED(hr) || !bytecode) {
5639 qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
5640 if (errors) {
5641 *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
5642 int(errors->GetBufferSize()));
5643 errors->Release();
5644 }
5645 return QByteArray();
5646 }
5647
5648 QByteArray result;
5649 result.resize(int(bytecode->GetBufferSize()));
5650 memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
5651 bytecode->Release();
5652 return result;
5653}
5654
5655#ifdef QRHI_D3D12_HAS_DXC
5656
5657#ifndef DXC_CP_UTF8
5658#define DXC_CP_UTF8 65001
5659#endif
5660
5661#ifndef DXC_ARG_DEBUG
5662#define DXC_ARG_DEBUG L"-Zi"
5663#endif
5664
5665static QByteArray dxcCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
5666{
5667 static std::pair<IDxcCompiler *, IDxcLibrary *> dxc = QRhiD3D::createDxcCompiler();
5668 IDxcCompiler *compiler = dxc.first;
5669 if (!compiler) {
5670 qWarning("Unable to instantiate IDxcCompiler. Likely no dxcompiler.dll and dxil.dll present. "
5671 "Use windeployqt or try https://github.com/microsoft/DirectXShaderCompiler/releases");
5672 return QByteArray();
5673 }
5674 IDxcLibrary *library = dxc.second;
5675 if (!library)
5676 return QByteArray();
5677
5678 IDxcBlobEncoding *sourceBlob = nullptr;
5679 HRESULT hr = library->CreateBlobWithEncodingOnHeapCopy(hlslSource.shader().constData(),
5680 UINT32(hlslSource.shader().size()),
5681 DXC_CP_UTF8,
5682 &sourceBlob);
5683 if (FAILED(hr)) {
5684 qWarning("Failed to create source blob for dxc: 0x%x (%s)",
5685 uint(hr),
5686 qPrintable(QSystemError::windowsComString(hr)));
5687 return QByteArray();
5688 }
5689
5690 const QString entryPointStr = QString::fromLatin1(hlslSource.entryPoint());
5691 const QString targetStr = QString::fromLatin1(target);
5692
5693 QVarLengthArray<LPCWSTR, 4> argPtrs;
5694 QString debugArg;
5695 if (flags & int(HlslCompileFlag::WithDebugInfo)) {
5696 debugArg = QString::fromUtf16(reinterpret_cast<const char16_t *>(DXC_ARG_DEBUG));
5697 argPtrs.append(reinterpret_cast<LPCWSTR>(debugArg.utf16()));
5698 }
5699
5700 IDxcOperationResult *result = nullptr;
5701 hr = compiler->Compile(sourceBlob,
5702 nullptr,
5703 reinterpret_cast<LPCWSTR>(entryPointStr.utf16()),
5704 reinterpret_cast<LPCWSTR>(targetStr.utf16()),
5705 argPtrs.data(), argPtrs.count(),
5706 nullptr, 0,
5707 nullptr,
5708 &result);
5709 sourceBlob->Release();
5710 if (SUCCEEDED(hr))
5711 result->GetStatus(&hr);
5712 if (FAILED(hr)) {
5713 qWarning("HLSL shader compilation failed: 0x%x (%s)",
5714 uint(hr),
5715 qPrintable(QSystemError::windowsComString(hr)));
5716 if (result) {
5717 IDxcBlobEncoding *errorsBlob = nullptr;
5718 if (SUCCEEDED(result->GetErrorBuffer(&errorsBlob))) {
5719 if (errorsBlob) {
5720 *error = QString::fromUtf8(static_cast<const char *>(errorsBlob->GetBufferPointer()),
5721 int(errorsBlob->GetBufferSize()));
5722 errorsBlob->Release();
5723 }
5724 }
5725 }
5726 return QByteArray();
5727 }
5728
5729 IDxcBlob *bytecode = nullptr;
5730 if FAILED(result->GetResult(&bytecode)) {
5731 qWarning("No result from IDxcCompiler: 0x%x (%s)",
5732 uint(hr),
5733 qPrintable(QSystemError::windowsComString(hr)));
5734 return QByteArray();
5735 }
5736
5737 QByteArray ba;
5738 ba.resize(int(bytecode->GetBufferSize()));
5739 memcpy(ba.data(), bytecode->GetBufferPointer(), size_t(ba.size()));
5740 bytecode->Release();
5741 return ba;
5742}
5743
5744#endif // QRHI_D3D12_HAS_DXC
5745
5746static QByteArray compileHlslShaderSource(const QShader &shader,
5747 QShader::Variant shaderVariant,
5748 int flags,
5749 QString *error,
5750 QShaderKey *usedShaderKey)
5751{
5752 // look for SM 6.7, 6.6, .., 5.0
5753 const int shaderModelMax = 67;
5754 for (int sm = shaderModelMax; sm >= 50; --sm) {
5755 for (QShader::Source type : { QShader::DxilShader, QShader::DxbcShader }) {
5756 QShaderKey key = { type, sm, shaderVariant };
5757 QShaderCode intermediateBytecodeShader = shader.shader(key);
5758 if (!intermediateBytecodeShader.shader().isEmpty()) {
5759 if (usedShaderKey)
5760 *usedShaderKey = key;
5761 return intermediateBytecodeShader.shader();
5762 }
5763 }
5764 }
5765
5766 QShaderCode hlslSource;
5767 QShaderKey key;
5768 for (int sm = shaderModelMax; sm >= 50; --sm) {
5769 key = { QShader::HlslShader, sm, shaderVariant };
5770 hlslSource = shader.shader(key);
5771 if (!hlslSource.shader().isEmpty())
5772 break;
5773 }
5774
5775 if (hlslSource.shader().isEmpty()) {
5776 qWarning() << "No HLSL (shader model 6.7..5.0) code found in baked shader" << shader;
5777 return QByteArray();
5778 }
5779
5780 if (usedShaderKey)
5781 *usedShaderKey = key;
5782
5783 char target[7];
5784 switch (shader.stage()) {
5785 case QShader::VertexStage:
5786 makeHlslTargetString(target, "vs", key.sourceVersion().version());
5787 break;
5788 case QShader::TessellationControlStage:
5789 makeHlslTargetString(target, "hs", key.sourceVersion().version());
5790 break;
5791 case QShader::TessellationEvaluationStage:
5792 makeHlslTargetString(target, "ds", key.sourceVersion().version());
5793 break;
5794 case QShader::GeometryStage:
5795 makeHlslTargetString(target, "gs", key.sourceVersion().version());
5796 break;
5797 case QShader::FragmentStage:
5798 makeHlslTargetString(target, "ps", key.sourceVersion().version());
5799 break;
5800 case QShader::ComputeStage:
5801 makeHlslTargetString(target, "cs", key.sourceVersion().version());
5802 break;
5803 }
5804
5805 if (key.sourceVersion().version() >= 60) {
5806#ifdef QRHI_D3D12_HAS_DXC
5807 return dxcCompile(hlslSource, target, flags, error);
5808#else
5809 qWarning("Attempted to runtime-compile HLSL source code for shader model >= 6.0 "
5810 "but the Qt build has no support for DXC. "
5811 "Rebuild Qt with a recent Windows SDK or switch to an MSVC build.");
5812#endif
5813 }
5814
5815 return legacyCompile(hlslSource, target, flags, error);
5816}
5817
5818static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
5819{
5820 UINT8 f = 0;
5821 if (c.testFlag(QRhiGraphicsPipeline::R))
5822 f |= D3D12_COLOR_WRITE_ENABLE_RED;
5823 if (c.testFlag(QRhiGraphicsPipeline::G))
5824 f |= D3D12_COLOR_WRITE_ENABLE_GREEN;
5825 if (c.testFlag(QRhiGraphicsPipeline::B))
5826 f |= D3D12_COLOR_WRITE_ENABLE_BLUE;
5827 if (c.testFlag(QRhiGraphicsPipeline::A))
5828 f |= D3D12_COLOR_WRITE_ENABLE_ALPHA;
5829 return f;
5830}
5831
5832static inline D3D12_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
5833{
5834 // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
5835 // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
5836 // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
5837 // set as an alpha src/dest factor), but for D3D we have to take care of it
5838 // ourselves. Hence the rgb argument.
5839
5840 switch (f) {
5841 case QRhiGraphicsPipeline::Zero:
5842 return D3D12_BLEND_ZERO;
5843 case QRhiGraphicsPipeline::One:
5844 return D3D12_BLEND_ONE;
5845 case QRhiGraphicsPipeline::SrcColor:
5846 return rgb ? D3D12_BLEND_SRC_COLOR : D3D12_BLEND_SRC_ALPHA;
5847 case QRhiGraphicsPipeline::OneMinusSrcColor:
5848 return rgb ? D3D12_BLEND_INV_SRC_COLOR : D3D12_BLEND_INV_SRC_ALPHA;
5849 case QRhiGraphicsPipeline::DstColor:
5850 return rgb ? D3D12_BLEND_DEST_COLOR : D3D12_BLEND_DEST_ALPHA;
5851 case QRhiGraphicsPipeline::OneMinusDstColor:
5852 return rgb ? D3D12_BLEND_INV_DEST_COLOR : D3D12_BLEND_INV_DEST_ALPHA;
5853 case QRhiGraphicsPipeline::SrcAlpha:
5854 return D3D12_BLEND_SRC_ALPHA;
5855 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5856 return D3D12_BLEND_INV_SRC_ALPHA;
5857 case QRhiGraphicsPipeline::DstAlpha:
5858 return D3D12_BLEND_DEST_ALPHA;
5859 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5860 return D3D12_BLEND_INV_DEST_ALPHA;
5861 case QRhiGraphicsPipeline::ConstantColor:
5862 case QRhiGraphicsPipeline::ConstantAlpha:
5863 return D3D12_BLEND_BLEND_FACTOR;
5864 case QRhiGraphicsPipeline::OneMinusConstantColor:
5865 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5866 return D3D12_BLEND_INV_BLEND_FACTOR;
5867 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5868 return D3D12_BLEND_SRC_ALPHA_SAT;
5869 case QRhiGraphicsPipeline::Src1Color:
5870 return rgb ? D3D12_BLEND_SRC1_COLOR : D3D12_BLEND_SRC1_ALPHA;
5871 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5872 return rgb ? D3D12_BLEND_INV_SRC1_COLOR : D3D12_BLEND_INV_SRC1_ALPHA;
5873 case QRhiGraphicsPipeline::Src1Alpha:
5874 return D3D12_BLEND_SRC1_ALPHA;
5875 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5876 return D3D12_BLEND_INV_SRC1_ALPHA;
5877 }
5878 Q_UNREACHABLE_RETURN(D3D12_BLEND_ZERO);
5879}
5880
5881static inline D3D12_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
5882{
5883 switch (op) {
5884 case QRhiGraphicsPipeline::Add:
5885 return D3D12_BLEND_OP_ADD;
5886 case QRhiGraphicsPipeline::Subtract:
5887 return D3D12_BLEND_OP_SUBTRACT;
5888 case QRhiGraphicsPipeline::ReverseSubtract:
5889 return D3D12_BLEND_OP_REV_SUBTRACT;
5890 case QRhiGraphicsPipeline::Min:
5891 return D3D12_BLEND_OP_MIN;
5892 case QRhiGraphicsPipeline::Max:
5893 return D3D12_BLEND_OP_MAX;
5894 }
5895 Q_UNREACHABLE_RETURN(D3D12_BLEND_OP_ADD);
5896}
5897
5898static inline D3D12_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
5899{
5900 switch (c) {
5901 case QRhiGraphicsPipeline::None:
5902 return D3D12_CULL_MODE_NONE;
5903 case QRhiGraphicsPipeline::Front:
5904 return D3D12_CULL_MODE_FRONT;
5905 case QRhiGraphicsPipeline::Back:
5906 return D3D12_CULL_MODE_BACK;
5907 }
5908 Q_UNREACHABLE_RETURN(D3D12_CULL_MODE_NONE);
5909}
5910
5911static inline D3D12_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
5912{
5913 switch (mode) {
5914 case QRhiGraphicsPipeline::Fill:
5915 return D3D12_FILL_MODE_SOLID;
5916 case QRhiGraphicsPipeline::Line:
5917 return D3D12_FILL_MODE_WIREFRAME;
5918 }
5919 Q_UNREACHABLE_RETURN(D3D12_FILL_MODE_SOLID);
5920}
5921
5922static inline D3D12_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
5923{
5924 switch (op) {
5925 case QRhiGraphicsPipeline::Never:
5926 return D3D12_COMPARISON_FUNC_NEVER;
5927 case QRhiGraphicsPipeline::Less:
5928 return D3D12_COMPARISON_FUNC_LESS;
5929 case QRhiGraphicsPipeline::Equal:
5930 return D3D12_COMPARISON_FUNC_EQUAL;
5931 case QRhiGraphicsPipeline::LessOrEqual:
5932 return D3D12_COMPARISON_FUNC_LESS_EQUAL;
5933 case QRhiGraphicsPipeline::Greater:
5934 return D3D12_COMPARISON_FUNC_GREATER;
5935 case QRhiGraphicsPipeline::NotEqual:
5936 return D3D12_COMPARISON_FUNC_NOT_EQUAL;
5937 case QRhiGraphicsPipeline::GreaterOrEqual:
5938 return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
5939 case QRhiGraphicsPipeline::Always:
5940 return D3D12_COMPARISON_FUNC_ALWAYS;
5941 }
5942 Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_ALWAYS);
5943}
5944
5945static inline D3D12_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
5946{
5947 switch (op) {
5948 case QRhiGraphicsPipeline::StencilZero:
5949 return D3D12_STENCIL_OP_ZERO;
5950 case QRhiGraphicsPipeline::Keep:
5951 return D3D12_STENCIL_OP_KEEP;
5952 case QRhiGraphicsPipeline::Replace:
5953 return D3D12_STENCIL_OP_REPLACE;
5954 case QRhiGraphicsPipeline::IncrementAndClamp:
5955 return D3D12_STENCIL_OP_INCR_SAT;
5956 case QRhiGraphicsPipeline::DecrementAndClamp:
5957 return D3D12_STENCIL_OP_DECR_SAT;
5958 case QRhiGraphicsPipeline::Invert:
5959 return D3D12_STENCIL_OP_INVERT;
5960 case QRhiGraphicsPipeline::IncrementAndWrap:
5961 return D3D12_STENCIL_OP_INCR;
5962 case QRhiGraphicsPipeline::DecrementAndWrap:
5963 return D3D12_STENCIL_OP_DECR;
5964 }
5965 Q_UNREACHABLE_RETURN(D3D12_STENCIL_OP_KEEP);
5966}
5967
5968static inline D3D12_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
5969{
5970 switch (t) {
5971 case QRhiGraphicsPipeline::Triangles:
5972 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
5973 case QRhiGraphicsPipeline::TriangleStrip:
5974 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
5975 case QRhiGraphicsPipeline::TriangleFan:
5976 qWarning("Triangle fans are not supported with D3D");
5977 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
5978 case QRhiGraphicsPipeline::Lines:
5979 return D3D_PRIMITIVE_TOPOLOGY_LINELIST;
5980 case QRhiGraphicsPipeline::LineStrip:
5981 return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
5982 case QRhiGraphicsPipeline::Points:
5983 return D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
5984 case QRhiGraphicsPipeline::Patches:
5985 Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
5986 return D3D_PRIMITIVE_TOPOLOGY(D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
5987 }
5988 Q_UNREACHABLE_RETURN(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
5989}
5990
5991static inline D3D12_PRIMITIVE_TOPOLOGY_TYPE toD3DTopologyType(QRhiGraphicsPipeline::Topology t)
5992{
5993 switch (t) {
5994 case QRhiGraphicsPipeline::Triangles:
5995 case QRhiGraphicsPipeline::TriangleStrip:
5996 case QRhiGraphicsPipeline::TriangleFan:
5997 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
5998 case QRhiGraphicsPipeline::Lines:
5999 case QRhiGraphicsPipeline::LineStrip:
6000 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
6001 case QRhiGraphicsPipeline::Points:
6002 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
6003 case QRhiGraphicsPipeline::Patches:
6004 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
6005 }
6006 Q_UNREACHABLE_RETURN(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
6007}
6008
6009static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
6010{
6011 switch (format) {
6012 case QRhiVertexInputAttribute::Float4:
6013 return DXGI_FORMAT_R32G32B32A32_FLOAT;
6014 case QRhiVertexInputAttribute::Float3:
6015 return DXGI_FORMAT_R32G32B32_FLOAT;
6016 case QRhiVertexInputAttribute::Float2:
6017 return DXGI_FORMAT_R32G32_FLOAT;
6018 case QRhiVertexInputAttribute::Float:
6019 return DXGI_FORMAT_R32_FLOAT;
6020 case QRhiVertexInputAttribute::UNormByte4:
6021 return DXGI_FORMAT_R8G8B8A8_UNORM;
6022 case QRhiVertexInputAttribute::UNormByte2:
6023 return DXGI_FORMAT_R8G8_UNORM;
6024 case QRhiVertexInputAttribute::UNormByte:
6025 return DXGI_FORMAT_R8_UNORM;
6026 case QRhiVertexInputAttribute::UInt4:
6027 return DXGI_FORMAT_R32G32B32A32_UINT;
6028 case QRhiVertexInputAttribute::UInt3:
6029 return DXGI_FORMAT_R32G32B32_UINT;
6030 case QRhiVertexInputAttribute::UInt2:
6031 return DXGI_FORMAT_R32G32_UINT;
6032 case QRhiVertexInputAttribute::UInt:
6033 return DXGI_FORMAT_R32_UINT;
6034 case QRhiVertexInputAttribute::SInt4:
6035 return DXGI_FORMAT_R32G32B32A32_SINT;
6036 case QRhiVertexInputAttribute::SInt3:
6037 return DXGI_FORMAT_R32G32B32_SINT;
6038 case QRhiVertexInputAttribute::SInt2:
6039 return DXGI_FORMAT_R32G32_SINT;
6040 case QRhiVertexInputAttribute::SInt:
6041 return DXGI_FORMAT_R32_SINT;
6042 case QRhiVertexInputAttribute::Half4:
6043 // Note: D3D does not support half3. Pass through half3 as half4.
6044 case QRhiVertexInputAttribute::Half3:
6045 return DXGI_FORMAT_R16G16B16A16_FLOAT;
6046 case QRhiVertexInputAttribute::Half2:
6047 return DXGI_FORMAT_R16G16_FLOAT;
6048 case QRhiVertexInputAttribute::Half:
6049 return DXGI_FORMAT_R16_FLOAT;
6050 case QRhiVertexInputAttribute::UShort4:
6051 // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
6052 case QRhiVertexInputAttribute::UShort3:
6053 return DXGI_FORMAT_R16G16B16A16_UINT;
6054 case QRhiVertexInputAttribute::UShort2:
6055 return DXGI_FORMAT_R16G16_UINT;
6056 case QRhiVertexInputAttribute::UShort:
6057 return DXGI_FORMAT_R16_UINT;
6058 case QRhiVertexInputAttribute::SShort4:
6059 // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
6060 case QRhiVertexInputAttribute::SShort3:
6061 return DXGI_FORMAT_R16G16B16A16_SINT;
6062 case QRhiVertexInputAttribute::SShort2:
6063 return DXGI_FORMAT_R16G16_SINT;
6064 case QRhiVertexInputAttribute::SShort:
6065 return DXGI_FORMAT_R16_SINT;
6066 }
6067 Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32G32B32A32_FLOAT);
6068}
6069
6070QD3D12GraphicsPipeline::QD3D12GraphicsPipeline(QRhiImplementation *rhi)
6071 : QRhiGraphicsPipeline(rhi)
6072{
6073}
6074
6075QD3D12GraphicsPipeline::~QD3D12GraphicsPipeline()
6076{
6077 destroy();
6078}
6079
6080void QD3D12GraphicsPipeline::destroy()
6081{
6082 if (handle.isNull())
6083 return;
6084
6085 QRHI_RES_RHI(QRhiD3D12);
6086 if (rhiD) {
6087 rhiD->releaseQueue.deferredReleasePipeline(handle);
6088 rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
6089 }
6090
6091 handle = {};
6092 stageData = {};
6093
6094 if (rhiD)
6095 rhiD->unregisterResource(this);
6096}
6097
6098bool QD3D12GraphicsPipeline::create()
6099{
6100 if (!handle.isNull())
6101 destroy();
6102
6103 QRHI_RES_RHI(QRhiD3D12);
6104 if (!rhiD->sanityCheckGraphicsPipeline(this))
6105 return false;
6106
6107 rhiD->pipelineCreationStart();
6108
6109 QByteArray shaderBytecode[5];
6110 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6111 const QD3D12Stage d3dStage = qd3d12_stage(shaderStage.type());
6112 stageData[d3dStage].valid = true;
6113 stageData[d3dStage].stage = d3dStage;
6114 auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(shaderStage);
6115 if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
6116 shaderBytecode[d3dStage] = cacheIt->bytecode;
6117 stageData[d3dStage].nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
6118 } else {
6119 QString error;
6120 QShaderKey shaderKey;
6121 int compileFlags = 0;
6122 if (m_flags.testFlag(CompileShadersWithDebugInfo))
6123 compileFlags |= int(HlslCompileFlag::WithDebugInfo);
6124 const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(),
6125 shaderStage.shaderVariant(),
6126 compileFlags,
6127 &error,
6128 &shaderKey);
6129 if (bytecode.isEmpty()) {
6130 qWarning("HLSL graphics shader compilation failed: %s", qPrintable(error));
6131 return false;
6132 }
6133
6134 shaderBytecode[d3dStage] = bytecode;
6135 stageData[d3dStage].nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
6136 rhiD->shaderBytecodeCache.insertWithCapacityLimit(shaderStage,
6137 { bytecode, stageData[d3dStage].nativeResourceBindingMap });
6138 }
6139 }
6140
6141 QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
6142 if (srbD) {
6143 rootSigHandle = srbD->createRootSignature(stageData.data(), 5);
6144 if (rootSigHandle.isNull()) {
6145 qWarning("Failed to create root signature");
6146 return false;
6147 }
6148 }
6149 ID3D12RootSignature *rootSig = nullptr;
6150 if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
6151 rootSig = rs->rootSig;
6152 if (!rootSig) {
6153 qWarning("Cannot create graphics pipeline state without root signature");
6154 return false;
6155 }
6156
6157 QD3D12RenderPassDescriptor *rpD = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
6158 DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
6159 if (rpD->colorAttachmentCount > 0) {
6160 format = DXGI_FORMAT(rpD->colorFormat[0]);
6161 } else if (rpD->hasDepthStencil) {
6162 format = DXGI_FORMAT(rpD->dsFormat);
6163 } else {
6164 qWarning("Cannot create graphics pipeline state without color or depthStencil format");
6165 return false;
6166 }
6167 const DXGI_SAMPLE_DESC sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, format);
6168
6169 struct {
6170 QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
6171 QD3D12PipelineStateSubObject<D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> inputLayout;
6172 QD3D12PipelineStateSubObject<D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE> primitiveRestartValue;
6173 QD3D12PipelineStateSubObject<D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> primitiveTopology;
6174 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> VS;
6175 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> HS;
6176 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> DS;
6177 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> GS;
6178 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> PS;
6179 QD3D12PipelineStateSubObject<D3D12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER> rasterizerState;
6180 QD3D12PipelineStateSubObject<D3D12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL> depthStencilState;
6181 QD3D12PipelineStateSubObject<D3D12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND> blendState;
6182 QD3D12PipelineStateSubObject<D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> rtFormats;
6183 QD3D12PipelineStateSubObject<DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> dsFormat;
6184 QD3D12PipelineStateSubObject<DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC> sampleDesc;
6185 QD3D12PipelineStateSubObject<UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK> sampleMask;
6186 QD3D12PipelineStateSubObject<D3D12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING> viewInstancingDesc;
6187 } stream;
6188
6189 stream.rootSig.object = rootSig;
6190
6191 QVarLengthArray<D3D12_INPUT_ELEMENT_DESC, 4> inputDescs;
6192 QByteArrayList matrixSliceSemantics;
6193 if (!shaderBytecode[VS].isEmpty()) {
6194 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
6195 it != itEnd; ++it)
6196 {
6197 D3D12_INPUT_ELEMENT_DESC desc = {};
6198 // The output from SPIRV-Cross uses TEXCOORD<location> as the
6199 // semantic, except for matrices that are unrolled into consecutive
6200 // vec2/3/4s attributes and need TEXCOORD<location>_ as
6201 // SemanticName and row/column index as SemanticIndex.
6202 const int matrixSlice = it->matrixSlice();
6203 if (matrixSlice < 0) {
6204 desc.SemanticName = "TEXCOORD";
6205 desc.SemanticIndex = UINT(it->location());
6206 } else {
6207 QByteArray sem;
6208 sem.resize(16);
6209 std::snprintf(sem.data(), sem.size(), "TEXCOORD%d_", it->location() - matrixSlice);
6210 matrixSliceSemantics.append(sem);
6211 desc.SemanticName = matrixSliceSemantics.last().constData();
6212 desc.SemanticIndex = UINT(matrixSlice);
6213 }
6214 desc.Format = toD3DAttributeFormat(it->format());
6215 desc.InputSlot = UINT(it->binding());
6216 desc.AlignedByteOffset = it->offset();
6217 const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
6218 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
6219 desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA;
6220 desc.InstanceDataStepRate = inputBinding->instanceStepRate();
6221 } else {
6222 desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
6223 }
6224 inputDescs.append(desc);
6225 }
6226 }
6227
6228 stream.inputLayout.object.NumElements = inputDescs.count();
6229 stream.inputLayout.object.pInputElementDescs = inputDescs.isEmpty() ? nullptr : inputDescs.constData();
6230
6231 stream.primitiveRestartValue.object = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF;
6232
6233 stream.primitiveTopology.object = toD3DTopologyType(m_topology);
6234 topology = toD3DTopology(m_topology, m_patchControlPointCount);
6235
6236 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6237 const int d3dStage = qd3d12_stage(shaderStage.type());
6238 switch (d3dStage) {
6239 case VS:
6240 stream.VS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6241 stream.VS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6242 break;
6243 case HS:
6244 stream.HS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6245 stream.HS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6246 break;
6247 case DS:
6248 stream.DS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6249 stream.DS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6250 break;
6251 case GS:
6252 stream.GS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6253 stream.GS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6254 break;
6255 case PS:
6256 stream.PS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6257 stream.PS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6258 break;
6259 default:
6260 Q_UNREACHABLE();
6261 break;
6262 }
6263 }
6264
6265 stream.rasterizerState.object.FillMode = toD3DFillMode(m_polygonMode);
6266 stream.rasterizerState.object.CullMode = toD3DCullMode(m_cullMode);
6267 stream.rasterizerState.object.FrontCounterClockwise = m_frontFace == CCW;
6268 stream.rasterizerState.object.DepthBias = m_depthBias;
6269 stream.rasterizerState.object.SlopeScaledDepthBias = m_slopeScaledDepthBias;
6270 stream.rasterizerState.object.DepthClipEnable = m_depthClamp ? FALSE : TRUE;
6271 stream.rasterizerState.object.MultisampleEnable = sampleDesc.Count > 1;
6272
6273 stream.depthStencilState.object.DepthEnable = m_depthTest;
6274 stream.depthStencilState.object.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
6275 stream.depthStencilState.object.DepthFunc = toD3DCompareOp(m_depthOp);
6276 stream.depthStencilState.object.StencilEnable = m_stencilTest;
6277 if (m_stencilTest) {
6278 stream.depthStencilState.object.StencilReadMask = UINT8(m_stencilReadMask);
6279 stream.depthStencilState.object.StencilWriteMask = UINT8(m_stencilWriteMask);
6280 stream.depthStencilState.object.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
6281 stream.depthStencilState.object.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
6282 stream.depthStencilState.object.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
6283 stream.depthStencilState.object.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
6284 stream.depthStencilState.object.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
6285 stream.depthStencilState.object.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
6286 stream.depthStencilState.object.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
6287 stream.depthStencilState.object.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
6288 }
6289
6290 stream.blendState.object.IndependentBlendEnable = m_targetBlends.count() > 1;
6291 for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
6292 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
6293 D3D12_RENDER_TARGET_BLEND_DESC blend = {};
6294 blend.BlendEnable = b.enable;
6295 blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
6296 blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
6297 blend.BlendOp = toD3DBlendOp(b.opColor);
6298 blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
6299 blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
6300 blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
6301 blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
6302 stream.blendState.object.RenderTarget[i] = blend;
6303 }
6304 if (m_targetBlends.isEmpty()) {
6305 D3D12_RENDER_TARGET_BLEND_DESC blend = {};
6306 blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
6307 stream.blendState.object.RenderTarget[0] = blend;
6308 }
6309
6310 stream.rtFormats.object.NumRenderTargets = rpD->colorAttachmentCount;
6311 for (int i = 0; i < rpD->colorAttachmentCount; ++i)
6312 stream.rtFormats.object.RTFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]);
6313
6314 stream.dsFormat.object = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN;
6315
6316 stream.sampleDesc.object = sampleDesc;
6317
6318 stream.sampleMask.object = 0xFFFFFFFF;
6319
6320 viewInstanceMask = 0;
6321 const bool isMultiView = m_multiViewCount >= 2;
6322 stream.viewInstancingDesc.object.ViewInstanceCount = isMultiView ? m_multiViewCount : 0;
6323 QVarLengthArray<D3D12_VIEW_INSTANCE_LOCATION, 4> viewInstanceLocations;
6324 if (isMultiView) {
6325 for (int i = 0; i < m_multiViewCount; ++i) {
6326 viewInstanceMask |= (1 << i);
6327 viewInstanceLocations.append({ 0, UINT(i) });
6328 }
6329 stream.viewInstancingDesc.object.pViewInstanceLocations = viewInstanceLocations.constData();
6330 }
6331
6332 const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
6333
6334 ID3D12PipelineState *pso = nullptr;
6335 HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
6336 if (FAILED(hr)) {
6337 qWarning("Failed to create graphics pipeline state: %s",
6338 qPrintable(QSystemError::windowsComString(hr)));
6339 rhiD->rootSignaturePool.remove(rootSigHandle);
6340 rootSigHandle = {};
6341 return false;
6342 }
6343
6344 handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Graphics, pso);
6345
6346 rhiD->pipelineCreationEnd();
6347 generation += 1;
6348 rhiD->registerResource(this);
6349 return true;
6350}
6351
6352QD3D12ComputePipeline::QD3D12ComputePipeline(QRhiImplementation *rhi)
6353 : QRhiComputePipeline(rhi)
6354{
6355}
6356
6357QD3D12ComputePipeline::~QD3D12ComputePipeline()
6358{
6359 destroy();
6360}
6361
6362void QD3D12ComputePipeline::destroy()
6363{
6364 if (handle.isNull())
6365 return;
6366
6367 QRHI_RES_RHI(QRhiD3D12);
6368 if (rhiD) {
6369 rhiD->releaseQueue.deferredReleasePipeline(handle);
6370 rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
6371 }
6372
6373 handle = {};
6374 stageData = {};
6375
6376 if (rhiD)
6377 rhiD->unregisterResource(this);
6378}
6379
6380bool QD3D12ComputePipeline::create()
6381{
6382 if (!handle.isNull())
6383 destroy();
6384
6385 QRHI_RES_RHI(QRhiD3D12);
6386 rhiD->pipelineCreationStart();
6387
6388 stageData.valid = true;
6389 stageData.stage = CS;
6390
6391 QByteArray shaderBytecode;
6392 auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(m_shaderStage);
6393 if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
6394 shaderBytecode = cacheIt->bytecode;
6395 stageData.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
6396 } else {
6397 QString error;
6398 QShaderKey shaderKey;
6399 int compileFlags = 0;
6400 if (m_flags.testFlag(CompileShadersWithDebugInfo))
6401 compileFlags |= int(HlslCompileFlag::WithDebugInfo);
6402 const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(),
6403 m_shaderStage.shaderVariant(),
6404 compileFlags,
6405 &error,
6406 &shaderKey);
6407 if (bytecode.isEmpty()) {
6408 qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
6409 return false;
6410 }
6411
6412 shaderBytecode = bytecode;
6413 stageData.nativeResourceBindingMap = m_shaderStage.shader().nativeResourceBindingMap(shaderKey);
6414 rhiD->shaderBytecodeCache.insertWithCapacityLimit(m_shaderStage, { bytecode,
6415 stageData.nativeResourceBindingMap });
6416 }
6417
6418 QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
6419 if (srbD) {
6420 rootSigHandle = srbD->createRootSignature(&stageData, 1);
6421 if (rootSigHandle.isNull()) {
6422 qWarning("Failed to create root signature");
6423 return false;
6424 }
6425 }
6426 ID3D12RootSignature *rootSig = nullptr;
6427 if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
6428 rootSig = rs->rootSig;
6429 if (!rootSig) {
6430 qWarning("Cannot create compute pipeline state without root signature");
6431 return false;
6432 }
6433
6434 struct {
6435 QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
6436 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CS;
6437 } stream;
6438 stream.rootSig.object = rootSig;
6439 stream.CS.object.pShaderBytecode = shaderBytecode.constData();
6440 stream.CS.object.BytecodeLength = shaderBytecode.size();
6441 const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
6442 ID3D12PipelineState *pso = nullptr;
6443 HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
6444 if (FAILED(hr)) {
6445 qWarning("Failed to create compute pipeline state: %s",
6446 qPrintable(QSystemError::windowsComString(hr)));
6447 rhiD->rootSignaturePool.remove(rootSigHandle);
6448 rootSigHandle = {};
6449 return false;
6450 }
6451
6452 handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
6453
6454 rhiD->pipelineCreationEnd();
6455 generation += 1;
6456 rhiD->registerResource(this);
6457 return true;
6458}
6459
6460// This is a lot like in the Metal backend: we need to now the rtv and dsv
6461// formats to create a graphics pipeline, and that's exactly what our
6462// "renderpass descriptor" is going to hold.
6463QD3D12RenderPassDescriptor::QD3D12RenderPassDescriptor(QRhiImplementation *rhi)
6464 : QRhiRenderPassDescriptor(rhi)
6465{
6466 serializedFormatData.reserve(16);
6467}
6468
6469QD3D12RenderPassDescriptor::~QD3D12RenderPassDescriptor()
6470{
6471 destroy();
6472}
6473
6474void QD3D12RenderPassDescriptor::destroy()
6475{
6476 QRHI_RES_RHI(QRhiD3D12);
6477 if (rhiD)
6478 rhiD->unregisterResource(this);
6479}
6480
6481bool QD3D12RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
6482{
6483 if (!other)
6484 return false;
6485
6486 const QD3D12RenderPassDescriptor *o = QRHI_RES(const QD3D12RenderPassDescriptor, other);
6487
6488 if (colorAttachmentCount != o->colorAttachmentCount)
6489 return false;
6490
6491 if (hasDepthStencil != o->hasDepthStencil)
6492 return false;
6493
6494 for (int i = 0; i < colorAttachmentCount; ++i) {
6495 if (colorFormat[i] != o->colorFormat[i])
6496 return false;
6497 }
6498
6499 if (hasDepthStencil) {
6500 if (dsFormat != o->dsFormat)
6501 return false;
6502 }
6503
6504 if (hasShadingRateMap != o->hasShadingRateMap)
6505 return false;
6506
6507 return true;
6508}
6509
6510void QD3D12RenderPassDescriptor::updateSerializedFormat()
6511{
6512 serializedFormatData.clear();
6513 auto p = std::back_inserter(serializedFormatData);
6514
6515 *p++ = colorAttachmentCount;
6516 *p++ = hasDepthStencil;
6517 for (int i = 0; i < colorAttachmentCount; ++i)
6518 *p++ = colorFormat[i];
6519 *p++ = hasDepthStencil ? dsFormat : 0;
6520}
6521
6522QRhiRenderPassDescriptor *QD3D12RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
6523{
6524 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
6525 rpD->colorAttachmentCount = colorAttachmentCount;
6526 rpD->hasDepthStencil = hasDepthStencil;
6527 memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
6528 rpD->dsFormat = dsFormat;
6529 rpD->hasShadingRateMap = hasShadingRateMap;
6530
6531 rpD->updateSerializedFormat();
6532
6533 QRHI_RES_RHI(QRhiD3D12);
6534 rhiD->registerResource(rpD);
6535 return rpD;
6536}
6537
6538QVector<quint32> QD3D12RenderPassDescriptor::serializedFormat() const
6539{
6540 return serializedFormatData;
6541}
6542
6543QD3D12CommandBuffer::QD3D12CommandBuffer(QRhiImplementation *rhi)
6544 : QRhiCommandBuffer(rhi)
6545{
6546 resetState();
6547}
6548
6549QD3D12CommandBuffer::~QD3D12CommandBuffer()
6550{
6551 destroy();
6552}
6553
6554void QD3D12CommandBuffer::destroy()
6555{
6556 // nothing to do here, the command list is not owned by us
6557}
6558
6559const QRhiNativeHandles *QD3D12CommandBuffer::nativeHandles()
6560{
6561 nativeHandlesStruct.commandList = cmdList;
6562 return &nativeHandlesStruct;
6563}
6564
6565QD3D12SwapChainRenderTarget::QD3D12SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
6566 : QRhiSwapChainRenderTarget(rhi, swapchain),
6567 d(rhi)
6568{
6569}
6570
6571QD3D12SwapChainRenderTarget::~QD3D12SwapChainRenderTarget()
6572{
6573 destroy();
6574}
6575
6576void QD3D12SwapChainRenderTarget::destroy()
6577{
6578 // nothing to do here
6579}
6580
6581QSize QD3D12SwapChainRenderTarget::pixelSize() const
6582{
6583 return d.pixelSize;
6584}
6585
6586float QD3D12SwapChainRenderTarget::devicePixelRatio() const
6587{
6588 return d.dpr;
6589}
6590
6591int QD3D12SwapChainRenderTarget::sampleCount() const
6592{
6593 return d.sampleCount;
6594}
6595
6596QD3D12SwapChain::QD3D12SwapChain(QRhiImplementation *rhi)
6597 : QRhiSwapChain(rhi),
6598 rtWrapper(rhi, this),
6599 rtWrapperRight(rhi, this),
6600 cbWrapper(rhi)
6601{
6602}
6603
6604QD3D12SwapChain::~QD3D12SwapChain()
6605{
6606 destroy();
6607}
6608
6609void QD3D12SwapChain::destroy()
6610{
6611 if (!swapChain)
6612 return;
6613
6614 releaseBuffers();
6615
6616 swapChain->Release();
6617 swapChain = nullptr;
6618 sourceSwapChain1->Release();
6619 sourceSwapChain1 = nullptr;
6620
6621 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
6622 FrameResources &fr(frameRes[i]);
6623 if (fr.fence)
6624 fr.fence->Release();
6625 if (fr.fenceEvent)
6626 CloseHandle(fr.fenceEvent);
6627 if (fr.cmdList)
6628 fr.cmdList->Release();
6629 fr = {};
6630 }
6631
6632 if (dcompVisual) {
6633 dcompVisual->Release();
6634 dcompVisual = nullptr;
6635 }
6636
6637 if (dcompTarget) {
6638 dcompTarget->Release();
6639 dcompTarget = nullptr;
6640 }
6641
6642 if (frameLatencyWaitableObject) {
6643 CloseHandle(frameLatencyWaitableObject);
6644 frameLatencyWaitableObject = nullptr;
6645 }
6646
6647 QDxgiVSyncService::instance()->unregisterWindow(window);
6648
6649 QRHI_RES_RHI(QRhiD3D12);
6650 if (rhiD) {
6651 rhiD->swapchains.remove(this);
6652 rhiD->unregisterResource(this);
6653 }
6654}
6655
6656void QD3D12SwapChain::releaseBuffers()
6657{
6658 QRHI_RES_RHI(QRhiD3D12);
6659 rhiD->waitGpu();
6660 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
6661 rhiD->resourcePool.remove(colorBuffers[i]);
6662 rhiD->rtvPool.release(rtvs[i], 1);
6663 if (stereo)
6664 rhiD->rtvPool.release(rtvsRight[i], 1);
6665 if (!msaaBuffers[i].isNull())
6666 rhiD->resourcePool.remove(msaaBuffers[i]);
6667 if (msaaRtvs[i].isValid())
6668 rhiD->rtvPool.release(msaaRtvs[i], 1);
6669 }
6670}
6671
6672void QD3D12SwapChain::waitCommandCompletionForFrameSlot(int frameSlot)
6673{
6674 FrameResources &fr(frameRes[frameSlot]);
6675 if (fr.fence->GetCompletedValue() < fr.fenceCounter) {
6676 fr.fence->SetEventOnCompletion(fr.fenceCounter, fr.fenceEvent);
6677 WaitForSingleObject(fr.fenceEvent, INFINITE);
6678 }
6679}
6680
6681void QD3D12SwapChain::addCommandCompletionSignalForCurrentFrameSlot()
6682{
6683 QRHI_RES_RHI(QRhiD3D12);
6684 FrameResources &fr(frameRes[currentFrameSlot]);
6685 fr.fenceCounter += 1u;
6686 rhiD->cmdQueue->Signal(fr.fence, fr.fenceCounter);
6687}
6688
6689QRhiCommandBuffer *QD3D12SwapChain::currentFrameCommandBuffer()
6690{
6691 return &cbWrapper;
6692}
6693
6694QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget()
6695{
6696 return &rtWrapper;
6697}
6698
6699QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
6700{
6701 return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
6702}
6703
6704QSize QD3D12SwapChain::surfacePixelSize()
6705{
6706 Q_ASSERT(m_window);
6707 return m_window->size() * m_window->devicePixelRatio();
6708}
6709
6710bool QD3D12SwapChain::isFormatSupported(Format f)
6711{
6712 if (f == SDR)
6713 return true;
6714
6715 if (!m_window) {
6716 qWarning("Attempted to call isFormatSupported() without a window set");
6717 return false;
6718 }
6719
6720 QRHI_RES_RHI(QRhiD3D12);
6721 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window))
6722 return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
6723
6724 return false;
6725}
6726
6727QRhiSwapChainHdrInfo QD3D12SwapChain::hdrInfo()
6728{
6729 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
6730 // Must use m_window, not window, given this may be called before createOrResize().
6731 if (m_window) {
6732 QRHI_RES_RHI(QRhiD3D12);
6733 info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window);
6734 }
6735 return info;
6736}
6737
6738QRhiRenderPassDescriptor *QD3D12SwapChain::newCompatibleRenderPassDescriptor()
6739{
6740 // not yet built so cannot rely on data computed in createOrResize()
6741 chooseFormats();
6742
6743 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
6744 rpD->colorAttachmentCount = 1;
6745 rpD->hasDepthStencil = m_depthStencil != nullptr;
6746 rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
6747 rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
6748
6749 rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
6750
6751 rpD->updateSerializedFormat();
6752
6753 QRHI_RES_RHI(QRhiD3D12);
6754 rhiD->registerResource(rpD);
6755 return rpD;
6756}
6757
6758bool QRhiD3D12::ensureDirectCompositionDevice()
6759{
6760 if (dcompDevice)
6761 return true;
6762
6763 qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
6764 dcompDevice = QRhiD3D::createDirectCompositionDevice();
6765 return dcompDevice ? true : false;
6766}
6767
6768static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
6769static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
6770
6771void QD3D12SwapChain::chooseFormats()
6772{
6773 colorFormat = DEFAULT_FORMAT;
6774 srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
6775 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
6776 QRHI_RES_RHI(QRhiD3D12);
6777 if (m_format != SDR) {
6778 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) {
6779 // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
6780 switch (m_format) {
6781 case HDRExtendedSrgbLinear:
6782 colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
6783 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
6784 srgbAdjustedColorFormat = colorFormat;
6785 break;
6786 case HDR10:
6787 colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
6788 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
6789 srgbAdjustedColorFormat = colorFormat;
6790 break;
6791 default:
6792 break;
6793 }
6794 } else {
6795 // This happens also when Use HDR is set to Off in the Windows
6796 // Display settings. Show a helpful warning, but continue with the
6797 // default non-HDR format.
6798 qWarning("The output associated with the window is not HDR capable "
6799 "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
6800 }
6801 }
6802 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, colorFormat);
6803}
6804
6805bool QD3D12SwapChain::createOrResize()
6806{
6807 // Can be called multiple times due to window resizes - that is not the
6808 // same as a simple destroy+create (as with other resources). Just need to
6809 // resize the buffers then.
6810
6811 const bool needsRegistration = !window || window != m_window;
6812
6813 // except if the window actually changes
6814 if (window && window != m_window)
6815 destroy();
6816
6817 window = m_window;
6818 m_currentPixelSize = surfacePixelSize();
6819 pixelSize = m_currentPixelSize;
6820
6821 if (pixelSize.isEmpty())
6822 return false;
6823
6824 HWND hwnd = reinterpret_cast<HWND>(window->winId());
6825 HRESULT hr;
6826 QRHI_RES_RHI(QRhiD3D12);
6827 stereo = m_window->format().stereo() && rhiD->dxgiFactory->IsWindowedStereoEnabled();
6828
6829 if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6830 if (rhiD->ensureDirectCompositionDevice()) {
6831 if (!dcompTarget) {
6832 hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
6833 if (FAILED(hr)) {
6834 qWarning("Failed to create Direct Composition target for the window: %s",
6835 qPrintable(QSystemError::windowsComString(hr)));
6836 }
6837 }
6838 if (dcompTarget && !dcompVisual) {
6839 hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
6840 if (FAILED(hr)) {
6841 qWarning("Failed to create DirectComposition visual: %s",
6842 qPrintable(QSystemError::windowsComString(hr)));
6843 }
6844 }
6845 }
6846 // simple consistency check
6847 if (window->requestedFormat().alphaBufferSize() <= 0)
6848 qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
6849 "This may lead to problems.");
6850 }
6851
6852 swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
6853 swapChainFlags = 0;
6854 if (swapInterval == 0 && rhiD->supportsAllowTearing)
6855 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
6856
6857 // maxFrameLatency 0 means no waitable object usage.
6858 // Ignore it also when NoVSync is on, and when using WARP.
6859 const bool useFrameLatencyWaitableObject = rhiD->maxFrameLatency != 0
6860 && swapInterval != 0
6861 && rhiD->driverInfoStruct.deviceType != QRhiDriverInfo::CpuDevice;
6862 if (useFrameLatencyWaitableObject)
6863 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
6864
6865 if (!swapChain) {
6866 chooseFormats();
6867
6868 DXGI_SWAP_CHAIN_DESC1 desc = {};
6869 desc.Width = UINT(pixelSize.width());
6870 desc.Height = UINT(pixelSize.height());
6871 desc.Format = colorFormat;
6872 desc.SampleDesc.Count = 1;
6873 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6874 desc.BufferCount = BUFFER_COUNT;
6875 desc.Flags = swapChainFlags;
6876 desc.Scaling = DXGI_SCALING_NONE;
6877 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
6878 desc.Stereo = stereo;
6879
6880 if (dcompVisual) {
6881 // With DirectComposition setting AlphaMode to STRAIGHT fails the
6882 // swapchain creation, whereas the result seems to be identical
6883 // with any of the other values, including IGNORE. (?)
6884 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
6885
6886 // DirectComposition has its own limitations, cannot use
6887 // SCALING_NONE. So with semi-transparency requested we are forced
6888 // to SCALING_STRETCH.
6889 desc.Scaling = DXGI_SCALING_STRETCH;
6890 }
6891
6892 if (dcompVisual)
6893 hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
6894 else
6895 hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
6896
6897 // If failed and we tried a HDR format, then try with SDR. This
6898 // matches other backends, such as Vulkan where if the format is
6899 // not supported, the default one is used instead.
6900 if (FAILED(hr) && m_format != SDR) {
6901 colorFormat = DEFAULT_FORMAT;
6902 desc.Format = DEFAULT_FORMAT;
6903 if (dcompVisual)
6904 hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
6905 else
6906 hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
6907 }
6908
6909 if (SUCCEEDED(hr)) {
6910 if (FAILED(sourceSwapChain1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&swapChain)))) {
6911 qWarning("IDXGISwapChain3 not available");
6912 return false;
6913 }
6914 if (m_format != SDR) {
6915 hr = swapChain->SetColorSpace1(hdrColorSpace);
6916 if (FAILED(hr)) {
6917 qWarning("Failed to set color space on swapchain: %s",
6918 qPrintable(QSystemError::windowsComString(hr)));
6919 }
6920 }
6921 if (useFrameLatencyWaitableObject) {
6922 swapChain->SetMaximumFrameLatency(rhiD->maxFrameLatency);
6923 frameLatencyWaitableObject = swapChain->GetFrameLatencyWaitableObject();
6924 }
6925 if (dcompVisual) {
6926 hr = dcompVisual->SetContent(swapChain);
6927 if (SUCCEEDED(hr)) {
6928 hr = dcompTarget->SetRoot(dcompVisual);
6929 if (FAILED(hr)) {
6930 qWarning("Failed to associate Direct Composition visual with the target: %s",
6931 qPrintable(QSystemError::windowsComString(hr)));
6932 }
6933 } else {
6934 qWarning("Failed to set content for Direct Composition visual: %s",
6935 qPrintable(QSystemError::windowsComString(hr)));
6936 }
6937 } else {
6938 // disable Alt+Enter; not relevant when using DirectComposition
6939 rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
6940 }
6941 }
6942 if (FAILED(hr)) {
6943 qWarning("Failed to create D3D12 swapchain: %s"
6944 " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
6945 qPrintable(QSystemError::windowsComString(hr)),
6946 desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
6947 desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
6948 return false;
6949 }
6950
6951 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
6952 hr = rhiD->dev->CreateFence(0,
6953 D3D12_FENCE_FLAG_NONE,
6954 __uuidof(ID3D12Fence),
6955 reinterpret_cast<void **>(&frameRes[i].fence));
6956 if (FAILED(hr)) {
6957 qWarning("Failed to create fence for swapchain: %s",
6958 qPrintable(QSystemError::windowsComString(hr)));
6959 return false;
6960 }
6961 frameRes[i].fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
6962
6963 frameRes[i].fenceCounter = 0;
6964 }
6965 } else {
6966 releaseBuffers();
6967 hr = swapChain->ResizeBuffers(BUFFER_COUNT,
6968 UINT(pixelSize.width()),
6969 UINT(pixelSize.height()),
6970 colorFormat,
6971 swapChainFlags);
6972 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
6973 qWarning("Device loss detected in ResizeBuffers()");
6974 rhiD->deviceLost = true;
6975 return false;
6976 } else if (FAILED(hr)) {
6977 qWarning("Failed to resize D3D12 swapchain: %s", qPrintable(QSystemError::windowsComString(hr)));
6978 return false;
6979 }
6980 }
6981
6982 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
6983 ID3D12Resource *colorBuffer;
6984 hr = swapChain->GetBuffer(i, __uuidof(ID3D12Resource), reinterpret_cast<void **>(&colorBuffer));
6985 if (FAILED(hr)) {
6986 qWarning("Failed to get buffer %u for D3D12 swapchain: %s",
6987 i, qPrintable(QSystemError::windowsComString(hr)));
6988 return false;
6989 }
6990 colorBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, colorBuffer, D3D12_RESOURCE_STATE_PRESENT);
6991 rtvs[i] = rhiD->rtvPool.allocate(1);
6992 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
6993 rtvDesc.Format = srgbAdjustedColorFormat;
6994 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
6995 rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvs[i].cpuHandle);
6996
6997 if (stereo) {
6998 rtvsRight[i] = rhiD->rtvPool.allocate(1);
6999 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
7000 rtvDesc.Format = srgbAdjustedColorFormat;
7001 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
7002 rtvDesc.Texture2DArray.ArraySize = 1;
7003 rtvDesc.Texture2DArray.FirstArraySlice = 1;
7004 rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvsRight[i].cpuHandle);
7005 }
7006 }
7007
7008 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
7009 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
7010 m_depthStencil->sampleCount(), m_sampleCount);
7011 }
7012 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
7013 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
7014 m_depthStencil->setPixelSize(pixelSize);
7015 if (!m_depthStencil->create())
7016 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
7017 pixelSize.width(), pixelSize.height());
7018 } else {
7019 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
7020 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
7021 pixelSize.width(), pixelSize.height());
7022 }
7023 }
7024
7025 ds = m_depthStencil ? QRHI_RES(QD3D12RenderBuffer, m_depthStencil) : nullptr;
7026
7027 if (sampleDesc.Count > 1) {
7028 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
7029 D3D12_RESOURCE_DESC resourceDesc = {};
7030 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
7031 resourceDesc.Width = UINT64(pixelSize.width());
7032 resourceDesc.Height = UINT(pixelSize.height());
7033 resourceDesc.DepthOrArraySize = 1;
7034 resourceDesc.MipLevels = 1;
7035 resourceDesc.Format = srgbAdjustedColorFormat;
7036 resourceDesc.SampleDesc = sampleDesc;
7037 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
7038 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
7039 D3D12_CLEAR_VALUE clearValue = {};
7040 clearValue.Format = colorFormat;
7041 ID3D12Resource *resource = nullptr;
7042 D3D12MA::Allocation *allocation = nullptr;
7043 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
7044 &resourceDesc,
7045 D3D12_RESOURCE_STATE_RENDER_TARGET,
7046 &clearValue,
7047 &allocation,
7048 __uuidof(ID3D12Resource),
7049 reinterpret_cast<void **>(&resource));
7050 if (FAILED(hr)) {
7051 qWarning("Failed to create MSAA color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
7052 return false;
7053 }
7054 msaaBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
7055 msaaRtvs[i] = rhiD->rtvPool.allocate(1);
7056 if (!msaaRtvs[i].isValid())
7057 return false;
7058 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
7059 rtvDesc.Format = srgbAdjustedColorFormat;
7060 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
7061 : D3D12_RTV_DIMENSION_TEXTURE2D;
7062 rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, msaaRtvs[i].cpuHandle);
7063 }
7064 }
7065
7066 currentBackBufferIndex = swapChain->GetCurrentBackBufferIndex();
7067 currentFrameSlot = 0;
7068 lastFrameLatencyWaitSlot = -1; // wait already in the first frame, as instructed in the dxgi docs
7069
7070 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
7071 QD3D12SwapChainRenderTarget *rtD = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapper);
7072 rtD->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
7073 rtD->d.pixelSize = pixelSize;
7074 rtD->d.dpr = float(window->devicePixelRatio());
7075 rtD->d.sampleCount = int(sampleDesc.Count);
7076 rtD->d.colorAttCount = 1;
7077 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
7078
7079 rtWrapperRight.setRenderPassDescriptor(m_renderPassDesc);
7080 QD3D12SwapChainRenderTarget *rtDr = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapperRight);
7081 rtDr->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
7082 rtDr->d.pixelSize = pixelSize;
7083 rtDr->d.dpr = float(window->devicePixelRatio());
7084 rtDr->d.sampleCount = int(sampleDesc.Count);
7085 rtDr->d.colorAttCount = 1;
7086 rtDr->d.dsAttCount = m_depthStencil ? 1 : 0;
7087
7088 QDxgiVSyncService::instance()->registerWindow(window);
7089
7090 if (needsRegistration || !rhiD->swapchains.contains(this))
7091 rhiD->swapchains.insert(this);
7092
7093 rhiD->registerResource(this);
7094
7095 return true;
7096}
7097
7098QT_END_NAMESPACE
7099
7100#endif // __ID3D12Device2_INTERFACE_DEFINED__
#define __has_include(x)