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