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
3800 footprint.Width = size.width();
3801 footprint.Height = size.height();
3802
3803 srcBox.left = 0;
3804 srcBox.top = 0;
3805 srcBox.right = UINT(size.width());
3806 srcBox.bottom = UINT(size.height());
3807 srcBox.front = 0;
3808 srcBox.back = 1;
3809
3810 const uchar *imgPtr = img.constBits();
3811 const quint32 lineBytes = size.width() * bpc;
3812 for (int y = 0, h = size.height(); y < h; ++y) {
3813 memcpy(stagingAlloc.p + y * footprint.RowPitch,
3814 imgPtr + srcPos.x() * bpc + (y + srcPos.y()) * bpl,
3815 lineBytes);
3816 }
3817 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
3818 QSize blockDim;
3819 quint32 bpl = 0;
3820 compressedFormatInfo(texD->m_format, subresSize, &bpl, nullptr, &blockDim);
3821 // x and y must be multiples of the block width and height
3822 dstPos.setX(aligned(dstPos.x(), blockDim.width()));
3823 dstPos.setY(aligned(dstPos.y(), blockDim.height()));
3824
3825 srcBox.left = 0;
3826 srcBox.top = 0;
3827 // width and height must be multiples of the block width and height
3828 srcBox.right = aligned(subresSize.width(), blockDim.width());
3829 srcBox.bottom = aligned(subresSize.height(), blockDim.height());
3830
3831 srcBox.front = 0;
3832 srcBox.back = 1;
3833
3834 footprint.Width = aligned(subresSize.width(), blockDim.width());
3835 footprint.Height = aligned(subresSize.height(), blockDim.height());
3836
3837 const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
3838 const QByteArray imgData = subresDesc.data();
3839 const char *imgPtr = imgData.constData();
3840 const int rowCount = aligned(subresSize.height(), blockDim.height()) / blockDim.height();
3841 for (int y = 0; y < rowCount; ++y)
3842 memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
3843 } else if (!subresDesc.data().isEmpty()) {
3844 srcBox.left = 0;
3845 srcBox.top = 0;
3846 srcBox.right = subresSize.width();
3847 srcBox.bottom = subresSize.height();
3848 srcBox.front = 0;
3849 srcBox.back = 1;
3850
3851 footprint.Width = subresSize.width();
3852 footprint.Height = subresSize.height();
3853
3854 quint32 bpl = 0;
3855 if (subresDesc.dataStride())
3856 bpl = subresDesc.dataStride();
3857 else
3858 textureFormatInfo(texD->m_format, subresSize, &bpl, nullptr, nullptr);
3859
3860 const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
3861 const QByteArray data = subresDesc.data();
3862 const char *imgPtr = data.constData();
3863 for (int y = 0, h = subresSize.height(); y < h; ++y)
3864 memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
3865 }
3866
3867 src.PlacedFootprint.Footprint = footprint;
3868
3869 cbD->cmdList->CopyTextureRegion(&dst,
3870 UINT(dstPos.x()),
3871 UINT(dstPos.y()),
3872 is3D ? UINT(layer) : 0u,
3873 &src,
3874 &srcBox);
3875
3876 if (ownStagingArea.has_value())
3877 ownStagingArea->destroyWithDeferredRelease(&releaseQueue);
3878 }
3879 }
3880 }
3881 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
3882 Q_ASSERT(u.src && u.dst);
3883 QD3D12Texture *srcD = QRHI_RES(QD3D12Texture, u.src);
3884 QD3D12Texture *dstD = QRHI_RES(QD3D12Texture, u.dst);
3885 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3886 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3887 QD3D12Resource *srcRes = resourcePool.lookupRef(srcD->handle);
3888 QD3D12Resource *dstRes = resourcePool.lookupRef(dstD->handle);
3889 if (!srcRes || !dstRes)
3890 continue;
3891
3892 barrierGen.addTransitionBarrier(srcD->handle, D3D12_RESOURCE_STATE_COPY_SOURCE);
3893 barrierGen.addTransitionBarrier(dstD->handle, D3D12_RESOURCE_STATE_COPY_DEST);
3894 barrierGen.enqueueBufferedTransitionBarriers(cbD);
3895
3896 const UINT srcSubresource = calcSubresource(UINT(u.desc.sourceLevel()),
3897 srcIs3D ? 0u : UINT(u.desc.sourceLayer()),
3898 srcD->mipLevelCount);
3899 const UINT dstSubresource = calcSubresource(UINT(u.desc.destinationLevel()),
3900 dstIs3D ? 0u : UINT(u.desc.destinationLayer()),
3901 dstD->mipLevelCount);
3902 const QPoint dp = u.desc.destinationTopLeft();
3903 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3904 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3905 const QPoint sp = u.desc.sourceTopLeft();
3906
3907 D3D12_BOX srcBox;
3908 srcBox.left = UINT(sp.x());
3909 srcBox.top = UINT(sp.y());
3910 srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
3911 // back, right, bottom are exclusive
3912 srcBox.right = srcBox.left + UINT(copySize.width());
3913 srcBox.bottom = srcBox.top + UINT(copySize.height());
3914 srcBox.back = srcBox.front + 1;
3915
3916 D3D12_TEXTURE_COPY_LOCATION src;
3917 src.pResource = srcRes->resource;
3918 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3919 src.SubresourceIndex = srcSubresource;
3920 D3D12_TEXTURE_COPY_LOCATION dst;
3921 dst.pResource = dstRes->resource;
3922 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
3923 dst.SubresourceIndex = dstSubresource;
3924
3925 cbD->cmdList->CopyTextureRegion(&dst,
3926 UINT(dp.x()),
3927 UINT(dp.y()),
3928 dstIs3D ? UINT(u.desc.destinationLayer()) : 0u,
3929 &src,
3930 &srcBox);
3931 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
3932 QD3D12Readback readback;
3933 readback.frameSlot = currentFrameSlot;
3934 readback.result = u.result;
3935
3936 QD3D12ObjectHandle srcHandle;
3937 QRect rect;
3938 bool is3D = false;
3939 if (u.rb.texture()) {
3940 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.rb.texture());
3941 if (texD->sampleDesc.Count > 1) {
3942 qWarning("Multisample texture cannot be read back");
3943 continue;
3944 }
3945 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3946 if (u.rb.rect().isValid())
3947 rect = u.rb.rect();
3948 else
3949 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3950 readback.format = texD->m_format;
3951 srcHandle = texD->handle;
3952 } else {
3953 Q_ASSERT(currentSwapChain);
3954 if (u.rb.rect().isValid())
3955 rect = u.rb.rect();
3956 else
3957 rect = QRect({0, 0}, currentSwapChain->pixelSize);
3958 readback.format = swapchainReadbackTextureFormat(currentSwapChain->colorFormat, nullptr);
3959 if (readback.format == QRhiTexture::UnknownFormat)
3960 continue;
3961 srcHandle = currentSwapChain->colorBuffers[currentSwapChain->currentBackBufferIndex];
3962 }
3963 readback.pixelSize = rect.size();
3964
3965 textureFormatInfo(readback.format,
3966 readback.pixelSize,
3967 &readback.bytesPerLine,
3968 &readback.byteSize,
3969 nullptr);
3970
3971 QD3D12Resource *srcRes = resourcePool.lookupRef(srcHandle);
3972 if (!srcRes)
3973 continue;
3974
3975 const UINT subresource = calcSubresource(UINT(u.rb.level()),
3976 is3D ? 0u : UINT(u.rb.layer()),
3977 srcRes->desc.MipLevels);
3978 D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
3979 // totalBytes is what we get from D3D, with the 256 aligned stride,
3980 // readback.byteSize is the final result that's not relevant here yet
3981 UINT64 totalBytes = 0;
3982 dev->GetCopyableFootprints(&srcRes->desc, subresource, 1, 0,
3983 &layout, nullptr, nullptr, &totalBytes);
3984 readback.stagingRowPitch = layout.Footprint.RowPitch;
3985
3986 const quint32 allocSize = aligned<quint32>(totalBytes, QD3D12StagingArea::ALIGNMENT);
3987 if (!readback.staging.create(this, allocSize, D3D12_HEAP_TYPE_READBACK)) {
3988 if (u.result->completed)
3989 u.result->completed();
3990 continue;
3991 }
3992 QD3D12StagingArea::Allocation stagingAlloc = readback.staging.get(totalBytes);
3993 if (!stagingAlloc.isValid()) {
3994 readback.staging.destroy();
3995 if (u.result->completed)
3996 u.result->completed();
3997 continue;
3998 }
3999 Q_ASSERT(stagingAlloc.bufferOffset == 0);
4000
4001 barrierGen.addTransitionBarrier(srcHandle, D3D12_RESOURCE_STATE_COPY_SOURCE);
4002 barrierGen.enqueueBufferedTransitionBarriers(cbD);
4003
4004 D3D12_TEXTURE_COPY_LOCATION dst;
4005 dst.pResource = stagingAlloc.buffer;
4006 dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
4007 dst.PlacedFootprint.Offset = 0;
4008 dst.PlacedFootprint.Footprint = layout.Footprint;
4009
4010 D3D12_TEXTURE_COPY_LOCATION src;
4011 src.pResource = srcRes->resource;
4012 src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
4013 src.SubresourceIndex = subresource;
4014
4015 D3D12_BOX srcBox = {};
4016 srcBox.left = UINT(rect.left());
4017 srcBox.top = UINT(rect.top());
4018 srcBox.front = is3D ? UINT(u.rb.layer()) : 0u;
4019 // back, right, bottom are exclusive
4020 srcBox.right = srcBox.left + UINT(rect.width());
4021 srcBox.bottom = srcBox.top + UINT(rect.height());
4022 srcBox.back = srcBox.front + 1;
4023
4024 cbD->cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, &srcBox);
4025 activeReadbacks.append(readback);
4026 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
4027 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.dst);
4028 Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
4029 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional))
4030 mipmapGen3D.generate(cbD, texD->handle);
4031 else
4032 mipmapGen.generate(cbD, texD->handle);
4033 }
4034 }
4035
4036 ud->free();
4037}
4038
4039void QRhiD3D12::finishActiveReadbacks(bool forced)
4040{
4041 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
4042
4043 for (int i = activeReadbacks.size() - 1; i >= 0; --i) {
4044 QD3D12Readback &readback(activeReadbacks[i]);
4045 if (forced || currentFrameSlot == readback.frameSlot || readback.frameSlot < 0) {
4046 readback.result->format = readback.format;
4047 readback.result->pixelSize = readback.pixelSize;
4048 readback.result->data.resize(int(readback.byteSize));
4049
4050 if (readback.format != QRhiTexture::UnknownFormat) {
4051 quint8 *dstPtr = reinterpret_cast<quint8 *>(readback.result->data.data());
4052 const quint8 *srcPtr = readback.staging.mem.p;
4053 const quint32 lineSize = qMin(readback.bytesPerLine, readback.stagingRowPitch);
4054 for (int y = 0, h = readback.pixelSize.height(); y < h; ++y)
4055 memcpy(dstPtr + y * readback.bytesPerLine, srcPtr + y * readback.stagingRowPitch, lineSize);
4056 } else {
4057 memcpy(readback.result->data.data(), readback.staging.mem.p, readback.byteSize);
4058 }
4059
4060 readback.staging.destroy();
4061
4062 if (readback.result->completed)
4063 completedCallbacks.append(readback.result->completed);
4064
4065 activeReadbacks.remove(i);
4066 }
4067 }
4068
4069 for (auto f : completedCallbacks)
4070 f();
4071}
4072
4073bool QRhiD3D12::ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
4074 D3D12_DESCRIPTOR_HEAP_TYPE type,
4075 int frameSlot,
4076 quint32 neededDescriptorCount,
4077 bool *gotNew)
4078{
4079 // Gets a new heap if needed. Note that the capacity we get is clamped
4080 // automatically (e.g. to 1 million, or 2048 for samplers), so * 2 does not
4081 // mean we can grow indefinitely, then again even using the same size would
4082 // work (because we what we are after here is a new heap for the rest of
4083 // the commands, not affecting what's already recorded).
4084 if (h->perFrameHeapSlice[frameSlot].remainingCapacity() < neededDescriptorCount) {
4085 const quint32 newPerFrameSize = qMax(h->perFrameHeapSlice[frameSlot].capacity * 2,
4086 neededDescriptorCount);
4087 QD3D12ShaderVisibleDescriptorHeap newHeap;
4088 if (!newHeap.create(dev, type, newPerFrameSize)) {
4089 qWarning("Could not create new shader-visible descriptor heap");
4090 return false;
4091 }
4092 h->destroyWithDeferredRelease(&releaseQueue);
4093 *h = newHeap;
4094 *gotNew = true;
4095 }
4096 return true;
4097}
4098
4099void QRhiD3D12::bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD)
4100{
4101 ID3D12DescriptorHeap *heaps[] = {
4102 shaderVisibleCbvSrvUavHeap.heap.heap,
4103 samplerMgr.shaderVisibleSamplerHeap.heap.heap
4104 };
4105 cbD->cmdList->SetDescriptorHeaps(2, heaps);
4106}
4107
4108QD3D12Buffer::QD3D12Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
4109 : QRhiBuffer(rhi, type, usage, size)
4110{
4111}
4112
4113QD3D12Buffer::~QD3D12Buffer()
4114{
4115 destroy();
4116}
4117
4118void QD3D12Buffer::destroy()
4119{
4120 if (handles[0].isNull())
4121 return;
4122
4123 QRHI_RES_RHI(QRhiD3D12);
4124
4125 // destroy() implementations, unlike other functions, are expected to test
4126 // for m_rhi (rhiD) being null, to allow surviving in case one attempts to
4127 // destroy a (leaked) resource after the QRhi.
4128 //
4129 // If there is no QRhi anymore, we do not deferred-release but that's fine
4130 // since the QRhi already released everything that was in the resourcePool.
4131
4132 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4133 if (rhiD)
4134 rhiD->releaseQueue.deferredReleaseResource(handles[i]);
4135 handles[i] = {};
4136 pendingHostWrites[i].clear();
4137 }
4138
4139 if (rhiD)
4140 rhiD->unregisterResource(this);
4141}
4142
4143bool QD3D12Buffer::create()
4144{
4145 if (!handles[0].isNull())
4146 destroy();
4147
4148 if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
4149 qWarning("UniformBuffer must always be Dynamic");
4150 return false;
4151 }
4152
4153 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
4154 qWarning("StorageBuffer cannot be combined with Dynamic");
4155 return false;
4156 }
4157
4158 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
4159 const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
4160
4161 UINT resourceFlags = D3D12_RESOURCE_FLAG_NONE;
4162 if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
4163 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4164
4165 QRHI_RES_RHI(QRhiD3D12);
4166 HRESULT hr = 0;
4167 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4168 if (i == 0 || m_type == Dynamic) {
4169 D3D12_RESOURCE_DESC resourceDesc = {};
4170 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
4171 resourceDesc.Width = roundedSize;
4172 resourceDesc.Height = 1;
4173 resourceDesc.DepthOrArraySize = 1;
4174 resourceDesc.MipLevels = 1;
4175 resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
4176 resourceDesc.SampleDesc = { 1, 0 };
4177 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
4178 resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
4179 ID3D12Resource *resource = nullptr;
4180 D3D12MA::Allocation *allocation = nullptr;
4181 // Dynamic == host (CPU) visible
4182 D3D12_HEAP_TYPE heapType = m_type == Dynamic
4183 ? D3D12_HEAP_TYPE_UPLOAD
4184 : D3D12_HEAP_TYPE_DEFAULT;
4185 D3D12_RESOURCE_STATES resourceState = m_type == Dynamic
4186 ? D3D12_RESOURCE_STATE_GENERIC_READ
4187 : D3D12_RESOURCE_STATE_COMMON;
4188 hr = rhiD->vma.createResource(heapType,
4189 &resourceDesc,
4190 resourceState,
4191 nullptr,
4192 &allocation,
4193 __uuidof(resource),
4194 reinterpret_cast<void **>(&resource));
4195 if (FAILED(hr))
4196 break;
4197 if (!m_objectName.isEmpty()) {
4198 QString decoratedName = QString::fromUtf8(m_objectName);
4199 if (m_type == Dynamic) {
4200 decoratedName += QLatin1Char('/');
4201 decoratedName += QString::number(i);
4202 }
4203 resource->SetName(reinterpret_cast<LPCWSTR>(decoratedName.utf16()));
4204 }
4205 void *cpuMemPtr = nullptr;
4206 if (m_type == Dynamic) {
4207 // will be mapped for ever on the CPU, this makes future host write operations very simple
4208 hr = resource->Map(0, nullptr, &cpuMemPtr);
4209 if (FAILED(hr)) {
4210 qWarning("Map() failed to dynamic buffer");
4211 resource->Release();
4212 if (allocation)
4213 allocation->Release();
4214 break;
4215 }
4216 }
4217 handles[i] = QD3D12Resource::addToPool(&rhiD->resourcePool,
4218 resource,
4219 resourceState,
4220 allocation,
4221 cpuMemPtr);
4222 }
4223 }
4224 if (FAILED(hr)) {
4225 qWarning("Failed to create buffer: '%s' Type was %d, size was %u, using D3D12MA was %d.",
4226 qPrintable(QSystemError::windowsComString(hr)),
4227 int(m_type),
4228 roundedSize,
4229 int(rhiD->vma.isUsingD3D12MA()));
4230 return false;
4231 }
4232
4233 rhiD->registerResource(this);
4234 return true;
4235}
4236
4237QRhiBuffer::NativeBuffer QD3D12Buffer::nativeBuffer()
4238{
4239 NativeBuffer b;
4240 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QD3D12_FRAMES_IN_FLIGHT));
4241 QRHI_RES_RHI(QRhiD3D12);
4242 if (m_type == Dynamic) {
4243 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
4244 executeHostWritesForFrameSlot(i);
4245 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[i]))
4246 b.objects[i] = res->resource;
4247 else
4248 b.objects[i] = nullptr;
4249 }
4250 b.slotCount = QD3D12_FRAMES_IN_FLIGHT;
4251 return b;
4252 }
4253 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[0]))
4254 b.objects[0] = res->resource;
4255 else
4256 b.objects[0] = nullptr;
4257 b.slotCount = 1;
4258 return b;
4259}
4260
4261char *QD3D12Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
4262{
4263 // Shortcut the entire buffer update mechanism and allow the client to do
4264 // the host writes directly to the buffer. This will lead to unexpected
4265 // results when combined with QRhiResourceUpdateBatch-based updates for the
4266 // buffer, but provides a fast path for dynamic buffers that have all their
4267 // content changed in every frame.
4268
4269 Q_ASSERT(m_type == Dynamic);
4270 QRHI_RES_RHI(QRhiD3D12);
4271 Q_ASSERT(rhiD->inFrame);
4272 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[rhiD->currentFrameSlot]))
4273 return static_cast<char *>(res->cpuMapPtr);
4274
4275 return nullptr;
4276}
4277
4278void QD3D12Buffer::endFullDynamicBufferUpdateForCurrentFrame()
4279{
4280 // nothing to do here
4281}
4282
4283void QD3D12Buffer::executeHostWritesForFrameSlot(int frameSlot)
4284{
4285 if (pendingHostWrites[frameSlot].isEmpty())
4286 return;
4287
4288 Q_ASSERT(m_type == QRhiBuffer::Dynamic);
4289 QRHI_RES_RHI(QRhiD3D12);
4290 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[frameSlot])) {
4291 Q_ASSERT(res->cpuMapPtr);
4292 for (const QD3D12Buffer::HostWrite &u : std::as_const(pendingHostWrites[frameSlot]))
4293 memcpy(static_cast<char *>(res->cpuMapPtr) + u.offset, u.data.constData(), u.data.size());
4294 }
4295 pendingHostWrites[frameSlot].clear();
4296}
4297
4298static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
4299{
4300 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
4301 switch (format) {
4302 case QRhiTexture::RGBA8:
4303 return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
4304 case QRhiTexture::BGRA8:
4305 return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
4306 case QRhiTexture::R8:
4307 return DXGI_FORMAT_R8_UNORM;
4308 case QRhiTexture::R8SI:
4309 return DXGI_FORMAT_R8_SINT;
4310 case QRhiTexture::R8UI:
4311 return DXGI_FORMAT_R8_UINT;
4312 case QRhiTexture::RG8:
4313 return DXGI_FORMAT_R8G8_UNORM;
4314 case QRhiTexture::R16:
4315 return DXGI_FORMAT_R16_UNORM;
4316 case QRhiTexture::RG16:
4317 return DXGI_FORMAT_R16G16_UNORM;
4318 case QRhiTexture::RED_OR_ALPHA8:
4319 return DXGI_FORMAT_R8_UNORM;
4320
4321 case QRhiTexture::RGBA16F:
4322 return DXGI_FORMAT_R16G16B16A16_FLOAT;
4323 case QRhiTexture::RGBA32F:
4324 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4325 case QRhiTexture::R16F:
4326 return DXGI_FORMAT_R16_FLOAT;
4327 case QRhiTexture::R32F:
4328 return DXGI_FORMAT_R32_FLOAT;
4329
4330 case QRhiTexture::RGB10A2:
4331 return DXGI_FORMAT_R10G10B10A2_UNORM;
4332
4333 case QRhiTexture::R32SI:
4334 return DXGI_FORMAT_R32_SINT;
4335 case QRhiTexture::R32UI:
4336 return DXGI_FORMAT_R32_UINT;
4337 case QRhiTexture::RG32SI:
4338 return DXGI_FORMAT_R32G32_SINT;
4339 case QRhiTexture::RG32UI:
4340 return DXGI_FORMAT_R32G32_UINT;
4341 case QRhiTexture::RGBA32SI:
4342 return DXGI_FORMAT_R32G32B32A32_SINT;
4343 case QRhiTexture::RGBA32UI:
4344 return DXGI_FORMAT_R32G32B32A32_UINT;
4345
4346 case QRhiTexture::D16:
4347 return DXGI_FORMAT_R16_TYPELESS;
4348 case QRhiTexture::D24:
4349 return DXGI_FORMAT_R24G8_TYPELESS;
4350 case QRhiTexture::D24S8:
4351 return DXGI_FORMAT_R24G8_TYPELESS;
4352 case QRhiTexture::D32F:
4353 return DXGI_FORMAT_R32_TYPELESS;
4354 case QRhiTexture::Format::D32FS8:
4355 return DXGI_FORMAT_R32G8X24_TYPELESS;
4356
4357 case QRhiTexture::BC1:
4358 return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
4359 case QRhiTexture::BC2:
4360 return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
4361 case QRhiTexture::BC3:
4362 return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
4363 case QRhiTexture::BC4:
4364 return DXGI_FORMAT_BC4_UNORM;
4365 case QRhiTexture::BC5:
4366 return DXGI_FORMAT_BC5_UNORM;
4367 case QRhiTexture::BC6H:
4368 return DXGI_FORMAT_BC6H_UF16;
4369 case QRhiTexture::BC7:
4370 return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
4371
4372 case QRhiTexture::ETC2_RGB8:
4373 case QRhiTexture::ETC2_RGB8A1:
4374 case QRhiTexture::ETC2_RGBA8:
4375 qWarning("QRhiD3D12 does not support ETC2 textures");
4376 return DXGI_FORMAT_R8G8B8A8_UNORM;
4377
4378 case QRhiTexture::ASTC_4x4:
4379 case QRhiTexture::ASTC_5x4:
4380 case QRhiTexture::ASTC_5x5:
4381 case QRhiTexture::ASTC_6x5:
4382 case QRhiTexture::ASTC_6x6:
4383 case QRhiTexture::ASTC_8x5:
4384 case QRhiTexture::ASTC_8x6:
4385 case QRhiTexture::ASTC_8x8:
4386 case QRhiTexture::ASTC_10x5:
4387 case QRhiTexture::ASTC_10x6:
4388 case QRhiTexture::ASTC_10x8:
4389 case QRhiTexture::ASTC_10x10:
4390 case QRhiTexture::ASTC_12x10:
4391 case QRhiTexture::ASTC_12x12:
4392 qWarning("QRhiD3D12 does not support ASTC textures");
4393 return DXGI_FORMAT_R8G8B8A8_UNORM;
4394
4395 default:
4396 break;
4397 }
4398 return DXGI_FORMAT_R8G8B8A8_UNORM;
4399}
4400
4401QD3D12RenderBuffer::QD3D12RenderBuffer(QRhiImplementation *rhi,
4402 Type type,
4403 const QSize &pixelSize,
4404 int sampleCount,
4405 Flags flags,
4406 QRhiTexture::Format backingFormatHint)
4407 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
4408{
4409}
4410
4411QD3D12RenderBuffer::~QD3D12RenderBuffer()
4412{
4413 destroy();
4414}
4415
4416void QD3D12RenderBuffer::destroy()
4417{
4418 if (handle.isNull())
4419 return;
4420
4421 QRHI_RES_RHI(QRhiD3D12);
4422 if (rhiD) {
4423 if (rtv.isValid())
4424 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->rtvPool, rtv, 1);
4425 else if (dsv.isValid())
4426 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->dsvPool, dsv, 1);
4427 }
4428
4429 handle = {};
4430 rtv = {};
4431 dsv = {};
4432
4433 if (rhiD)
4434 rhiD->unregisterResource(this);
4435}
4436
4437bool QD3D12RenderBuffer::create()
4438{
4439 if (!handle.isNull())
4440 destroy();
4441
4442 if (m_pixelSize.isEmpty())
4443 return false;
4444
4445 QRHI_RES_RHI(QRhiD3D12);
4446
4447 switch (m_type) {
4448 case QRhiRenderBuffer::Color:
4449 {
4450 dxgiFormat = toD3DTextureFormat(backingFormat(), {});
4451 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4452 D3D12_RESOURCE_DESC resourceDesc = {};
4453 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
4454 resourceDesc.Width = UINT64(m_pixelSize.width());
4455 resourceDesc.Height = UINT(m_pixelSize.height());
4456 resourceDesc.DepthOrArraySize = 1;
4457 resourceDesc.MipLevels = 1;
4458 resourceDesc.Format = dxgiFormat;
4459 resourceDesc.SampleDesc = sampleDesc;
4460 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4461 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
4462 D3D12_CLEAR_VALUE clearValue = {};
4463 clearValue.Format = dxgiFormat;
4464 // have a separate allocation and resource object (meaning both will need its own Release())
4465 ID3D12Resource *resource = nullptr;
4466 D3D12MA::Allocation *allocation = nullptr;
4467 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4468 &resourceDesc,
4469 D3D12_RESOURCE_STATE_RENDER_TARGET,
4470 &clearValue,
4471 &allocation,
4472 __uuidof(ID3D12Resource),
4473 reinterpret_cast<void **>(&resource));
4474 if (FAILED(hr)) {
4475 qWarning("Failed to create color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
4476 return false;
4477 }
4478 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
4479 rtv = rhiD->rtvPool.allocate(1);
4480 if (!rtv.isValid())
4481 return false;
4482 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
4483 rtvDesc.Format = dxgiFormat;
4484 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
4485 : D3D12_RTV_DIMENSION_TEXTURE2D;
4486 rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, rtv.cpuHandle);
4487 }
4488 break;
4489 case QRhiRenderBuffer::DepthStencil:
4490 {
4491 dxgiFormat = DS_FORMAT;
4492 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4493 D3D12_RESOURCE_DESC resourceDesc = {};
4494 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
4495 resourceDesc.Width = UINT64(m_pixelSize.width());
4496 resourceDesc.Height = UINT(m_pixelSize.height());
4497 resourceDesc.DepthOrArraySize = 1;
4498 resourceDesc.MipLevels = 1;
4499 resourceDesc.Format = dxgiFormat;
4500 resourceDesc.SampleDesc = sampleDesc;
4501 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4502 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
4503 if (m_flags.testFlag(UsedWithSwapChainOnly))
4504 resourceDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
4505 D3D12_CLEAR_VALUE clearValue = {};
4506 clearValue.Format = dxgiFormat;
4507 clearValue.DepthStencil.Depth = 1.0f;
4508 clearValue.DepthStencil.Stencil = 0;
4509 ID3D12Resource *resource = nullptr;
4510 D3D12MA::Allocation *allocation = nullptr;
4511 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4512 &resourceDesc,
4513 D3D12_RESOURCE_STATE_DEPTH_WRITE,
4514 &clearValue,
4515 &allocation,
4516 __uuidof(ID3D12Resource),
4517 reinterpret_cast<void **>(&resource));
4518 if (FAILED(hr)) {
4519 qWarning("Failed to create depth-stencil buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
4520 return false;
4521 }
4522 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_DEPTH_WRITE, allocation);
4523 dsv = rhiD->dsvPool.allocate(1);
4524 if (!dsv.isValid())
4525 return false;
4526 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
4527 dsvDesc.Format = dxgiFormat;
4528 dsvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
4529 : D3D12_DSV_DIMENSION_TEXTURE2D;
4530 rhiD->dev->CreateDepthStencilView(resource, &dsvDesc, dsv.cpuHandle);
4531 }
4532 break;
4533 }
4534
4535 if (!m_objectName.isEmpty()) {
4536 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
4537 const QString name = QString::fromUtf8(m_objectName);
4538 res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
4539 }
4540 }
4541
4542 generation += 1;
4543 rhiD->registerResource(this);
4544 return true;
4545}
4546
4547QRhiTexture::Format QD3D12RenderBuffer::backingFormat() const
4548{
4549 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4550 return m_backingFormatHint;
4551 else
4552 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4553}
4554
4555QD3D12Texture::QD3D12Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
4556 int arraySize, int sampleCount, Flags flags)
4557 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
4558{
4559}
4560
4561QD3D12Texture::~QD3D12Texture()
4562{
4563 destroy();
4564}
4565
4566void QD3D12Texture::destroy()
4567{
4568 if (handle.isNull())
4569 return;
4570
4571 QRHI_RES_RHI(QRhiD3D12);
4572 if (rhiD)
4573 rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->cbvSrvUavPool, srv, 1);
4574
4575 handle = {};
4576 srv = {};
4577
4578 if (rhiD)
4579 rhiD->unregisterResource(this);
4580}
4581
4582static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
4583{
4584 switch (format) {
4585 case QRhiTexture::Format::D16:
4586 return DXGI_FORMAT_R16_FLOAT;
4587 case QRhiTexture::Format::D24:
4588 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
4589 case QRhiTexture::Format::D24S8:
4590 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
4591 case QRhiTexture::Format::D32F:
4592 return DXGI_FORMAT_R32_FLOAT;
4593 case QRhiTexture::Format::D32FS8:
4594 return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
4595 default:
4596 break;
4597 }
4598 Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32_FLOAT);
4599}
4600
4601static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
4602{
4603 // here the result cannot be typeless
4604 switch (format) {
4605 case QRhiTexture::Format::D16:
4606 return DXGI_FORMAT_D16_UNORM;
4607 case QRhiTexture::Format::D24:
4608 return DXGI_FORMAT_D24_UNORM_S8_UINT;
4609 case QRhiTexture::Format::D24S8:
4610 return DXGI_FORMAT_D24_UNORM_S8_UINT;
4611 case QRhiTexture::Format::D32F:
4612 return DXGI_FORMAT_D32_FLOAT;
4613 case QRhiTexture::Format::D32FS8:
4614 return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
4615 default:
4616 break;
4617 }
4618 Q_UNREACHABLE_RETURN(DXGI_FORMAT_D32_FLOAT);
4619}
4620
4621static inline bool isDepthTextureFormat(QRhiTexture::Format format)
4622{
4623 switch (format) {
4624 case QRhiTexture::Format::D16:
4625 case QRhiTexture::Format::D24:
4626 case QRhiTexture::Format::D24S8:
4627 case QRhiTexture::Format::D32F:
4628 case QRhiTexture::Format::D32FS8:
4629 return true;
4630 default:
4631 return false;
4632 }
4633}
4634
4635bool QD3D12Texture::prepareCreate(QSize *adjustedSize)
4636{
4637 if (!handle.isNull())
4638 destroy();
4639
4640 QRHI_RES_RHI(QRhiD3D12);
4641 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
4642 return false;
4643
4644 const bool isDepth = isDepthTextureFormat(m_format);
4645 const bool isCube = m_flags.testFlag(CubeMap);
4646 const bool is3D = m_flags.testFlag(ThreeDimensional);
4647 const bool isArray = m_flags.testFlag(TextureArray);
4648 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4649 const bool is1D = m_flags.testFlag(OneDimensional);
4650
4651 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4652 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4653
4654 dxgiFormat = toD3DTextureFormat(m_format, m_flags);
4655 if (isDepth) {
4656 srvFormat = toD3DDepthTextureSRVFormat(m_format);
4657 rtFormat = toD3DDepthTextureDSVFormat(m_format);
4658 } else {
4659 srvFormat = dxgiFormat;
4660 rtFormat = dxgiFormat;
4661 }
4662 if (m_writeViewFormat.format != UnknownFormat) {
4663 if (isDepth)
4664 rtFormat = toD3DDepthTextureDSVFormat(m_writeViewFormat.format);
4665 else
4666 rtFormat = toD3DTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
4667 }
4668 if (m_readViewFormat.format != UnknownFormat) {
4669 if (isDepth)
4670 srvFormat = toD3DDepthTextureSRVFormat(m_readViewFormat.format);
4671 else
4672 srvFormat = toD3DTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
4673 }
4674
4675 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
4676 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
4677 if (sampleDesc.Count > 1) {
4678 if (isCube) {
4679 qWarning("Cubemap texture cannot be multisample");
4680 return false;
4681 }
4682 if (is3D) {
4683 qWarning("3D texture cannot be multisample");
4684 return false;
4685 }
4686 if (hasMipMaps) {
4687 qWarning("Multisample texture cannot have mipmaps");
4688 return false;
4689 }
4690 }
4691 if (isDepth && hasMipMaps) {
4692 qWarning("Depth texture cannot have mipmaps");
4693 return false;
4694 }
4695 if (isCube && is3D) {
4696 qWarning("Texture cannot be both cube and 3D");
4697 return false;
4698 }
4699 if (isArray && is3D) {
4700 qWarning("Texture cannot be both array and 3D");
4701 return false;
4702 }
4703 if (isCube && is1D) {
4704 qWarning("Texture cannot be both cube and 1D");
4705 return false;
4706 }
4707 if (is1D && is3D) {
4708 qWarning("Texture cannot be both 1D and 3D");
4709 return false;
4710 }
4711 if (m_depth > 1 && !is3D) {
4712 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
4713 return false;
4714 }
4715 if (m_arraySize > 0 && !isArray) {
4716 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
4717 return false;
4718 }
4719 if (m_arraySize < 1 && isArray) {
4720 qWarning("Texture is an array but array size is %d", m_arraySize);
4721 return false;
4722 }
4723
4724 if (adjustedSize)
4725 *adjustedSize = size;
4726
4727 return true;
4728}
4729
4730bool QD3D12Texture::finishCreate()
4731{
4732 QRHI_RES_RHI(QRhiD3D12);
4733 const bool isCube = m_flags.testFlag(CubeMap);
4734 const bool is3D = m_flags.testFlag(ThreeDimensional);
4735 const bool isArray = m_flags.testFlag(TextureArray);
4736 const bool is1D = m_flags.testFlag(OneDimensional);
4737
4738 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
4739 srvDesc.Format = srvFormat;
4740 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
4741
4742 if (isCube) {
4743 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
4744 srvDesc.TextureCube.MipLevels = mipLevelCount;
4745 } else {
4746 if (is1D) {
4747 if (isArray) {
4748 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
4749 srvDesc.Texture1DArray.MipLevels = mipLevelCount;
4750 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4751 srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
4752 srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
4753 } else {
4754 srvDesc.Texture1DArray.FirstArraySlice = 0;
4755 srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
4756 }
4757 } else {
4758 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
4759 srvDesc.Texture1D.MipLevels = mipLevelCount;
4760 }
4761 } else if (isArray) {
4762 if (sampleDesc.Count > 1) {
4763 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
4764 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4765 srvDesc.Texture2DMSArray.FirstArraySlice = UINT(m_arrayRangeStart);
4766 srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
4767 } else {
4768 srvDesc.Texture2DMSArray.FirstArraySlice = 0;
4769 srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
4770 }
4771 } else {
4772 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
4773 srvDesc.Texture2DArray.MipLevels = mipLevelCount;
4774 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
4775 srvDesc.Texture2DArray.FirstArraySlice = UINT(m_arrayRangeStart);
4776 srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
4777 } else {
4778 srvDesc.Texture2DArray.FirstArraySlice = 0;
4779 srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
4780 }
4781 }
4782 } else {
4783 if (sampleDesc.Count > 1) {
4784 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
4785 } else if (is3D) {
4786 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
4787 srvDesc.Texture3D.MipLevels = mipLevelCount;
4788 } else {
4789 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
4790 srvDesc.Texture2D.MipLevels = mipLevelCount;
4791 }
4792 }
4793 }
4794
4795 srv = rhiD->cbvSrvUavPool.allocate(1);
4796 if (!srv.isValid())
4797 return false;
4798
4799 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
4800 rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
4801 if (!m_objectName.isEmpty()) {
4802 const QString name = QString::fromUtf8(m_objectName);
4803 res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
4804 }
4805 } else {
4806 return false;
4807 }
4808
4809 generation += 1;
4810 return true;
4811}
4812
4813bool QD3D12Texture::create()
4814{
4815 QSize size;
4816 if (!prepareCreate(&size))
4817 return false;
4818
4819 const bool isDepth = isDepthTextureFormat(m_format);
4820 const bool isCube = m_flags.testFlag(CubeMap);
4821 const bool is3D = m_flags.testFlag(ThreeDimensional);
4822 const bool isArray = m_flags.testFlag(TextureArray);
4823 const bool is1D = m_flags.testFlag(OneDimensional);
4824
4825 QRHI_RES_RHI(QRhiD3D12);
4826
4827 bool needsOptimizedClearValueSpecified = false;
4828 UINT resourceFlags = 0;
4829 if (m_flags.testFlag(RenderTarget) || sampleDesc.Count > 1) {
4830 if (isDepth)
4831 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
4832 else
4833 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
4834 needsOptimizedClearValueSpecified = true;
4835 }
4836 if (m_flags.testFlag(UsedWithGenerateMips)) {
4837 if (isDepth) {
4838 qWarning("Depth texture cannot have mipmaps generated");
4839 return false;
4840 }
4841 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4842 }
4843 if (m_flags.testFlag(UsedWithLoadStore))
4844 resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
4845
4846 D3D12_RESOURCE_DESC resourceDesc = {};
4847 resourceDesc.Dimension = is1D ? D3D12_RESOURCE_DIMENSION_TEXTURE1D
4848 : (is3D ? D3D12_RESOURCE_DIMENSION_TEXTURE3D
4849 : D3D12_RESOURCE_DIMENSION_TEXTURE2D);
4850 resourceDesc.Width = UINT64(size.width());
4851 resourceDesc.Height = UINT(size.height());
4852 resourceDesc.DepthOrArraySize = isCube ? 6
4853 : (isArray ? UINT(qMax(0, m_arraySize))
4854 : (is3D ? qMax(1, m_depth)
4855 : 1));
4856 resourceDesc.MipLevels = mipLevelCount;
4857 resourceDesc.Format = dxgiFormat;
4858 resourceDesc.SampleDesc = sampleDesc;
4859 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
4860 resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
4861 D3D12_CLEAR_VALUE clearValue = {};
4862 clearValue.Format = dxgiFormat;
4863 if (isDepth) {
4864 clearValue.Format = toD3DDepthTextureDSVFormat(m_format);
4865 clearValue.DepthStencil.Depth = 1.0f;
4866 clearValue.DepthStencil.Stencil = 0;
4867 }
4868 ID3D12Resource *resource = nullptr;
4869 D3D12MA::Allocation *allocation = nullptr;
4870 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
4871 &resourceDesc,
4872 D3D12_RESOURCE_STATE_COMMON,
4873 needsOptimizedClearValueSpecified ? &clearValue : nullptr,
4874 &allocation,
4875 __uuidof(ID3D12Resource),
4876 reinterpret_cast<void **>(&resource));
4877 if (FAILED(hr)) {
4878 qWarning("Failed to create texture: '%s'"
4879 " Dim was %d Size was %ux%u Depth/ArraySize was %u MipLevels was %u Format was %d Sample count was %d",
4880 qPrintable(QSystemError::windowsComString(hr)),
4881 int(resourceDesc.Dimension),
4882 uint(resourceDesc.Width),
4883 uint(resourceDesc.Height),
4884 uint(resourceDesc.DepthOrArraySize),
4885 uint(resourceDesc.MipLevels),
4886 int(resourceDesc.Format),
4887 int(resourceDesc.SampleDesc.Count));
4888 return false;
4889 }
4890
4891 handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_COMMON, allocation);
4892
4893 if (!finishCreate())
4894 return false;
4895
4896 rhiD->registerResource(this);
4897 return true;
4898}
4899
4900bool QD3D12Texture::createFrom(QRhiTexture::NativeTexture src)
4901{
4902 if (!src.object)
4903 return false;
4904
4905 if (!prepareCreate())
4906 return false;
4907
4908 ID3D12Resource *resource = reinterpret_cast<ID3D12Resource *>(src.object);
4909 D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATES(src.layout);
4910
4911 QRHI_RES_RHI(QRhiD3D12);
4912 handle = QD3D12Resource::addNonOwningToPool(&rhiD->resourcePool, resource, state);
4913
4914 if (!finishCreate())
4915 return false;
4916
4917 rhiD->registerResource(this);
4918 return true;
4919}
4920
4921QRhiTexture::NativeTexture QD3D12Texture::nativeTexture()
4922{
4923 QRHI_RES_RHI(QRhiD3D12);
4924 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
4925 return { quint64(res->resource), int(res->state) };
4926
4927 return {};
4928}
4929
4930void QD3D12Texture::setNativeLayout(int layout)
4931{
4932 QRHI_RES_RHI(QRhiD3D12);
4933 if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
4934 res->state = D3D12_RESOURCE_STATES(layout);
4935}
4936
4937QD3D12Sampler::QD3D12Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
4938 AddressMode u, AddressMode v, AddressMode w)
4939 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
4940{
4941}
4942
4943QD3D12Sampler::~QD3D12Sampler()
4944{
4945 destroy();
4946}
4947
4948void QD3D12Sampler::destroy()
4949{
4950 shaderVisibleDescriptor = {};
4951
4952 QRHI_RES_RHI(QRhiD3D12);
4953 if (rhiD)
4954 rhiD->unregisterResource(this);
4955}
4956
4957static inline D3D12_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
4958{
4959 if (minFilter == QRhiSampler::Nearest) {
4960 if (magFilter == QRhiSampler::Nearest) {
4961 if (mipFilter == QRhiSampler::Linear)
4962 return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
4963 else
4964 return D3D12_FILTER_MIN_MAG_MIP_POINT;
4965 } else {
4966 if (mipFilter == QRhiSampler::Linear)
4967 return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
4968 else
4969 return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
4970 }
4971 } else {
4972 if (magFilter == QRhiSampler::Nearest) {
4973 if (mipFilter == QRhiSampler::Linear)
4974 return D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
4975 else
4976 return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
4977 } else {
4978 if (mipFilter == QRhiSampler::Linear)
4979 return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
4980 else
4981 return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
4982 }
4983 }
4984 Q_UNREACHABLE_RETURN(D3D12_FILTER_MIN_MAG_MIP_LINEAR);
4985}
4986
4987static inline D3D12_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
4988{
4989 switch (m) {
4990 case QRhiSampler::Repeat:
4991 return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
4992 case QRhiSampler::ClampToEdge:
4993 return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
4994 case QRhiSampler::Mirror:
4995 return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
4996 }
4997 Q_UNREACHABLE_RETURN(D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
4998}
4999
5000static inline D3D12_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
5001{
5002 switch (op) {
5003 case QRhiSampler::Never:
5004 return D3D12_COMPARISON_FUNC_NEVER;
5005 case QRhiSampler::Less:
5006 return D3D12_COMPARISON_FUNC_LESS;
5007 case QRhiSampler::Equal:
5008 return D3D12_COMPARISON_FUNC_EQUAL;
5009 case QRhiSampler::LessOrEqual:
5010 return D3D12_COMPARISON_FUNC_LESS_EQUAL;
5011 case QRhiSampler::Greater:
5012 return D3D12_COMPARISON_FUNC_GREATER;
5013 case QRhiSampler::NotEqual:
5014 return D3D12_COMPARISON_FUNC_NOT_EQUAL;
5015 case QRhiSampler::GreaterOrEqual:
5016 return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
5017 case QRhiSampler::Always:
5018 return D3D12_COMPARISON_FUNC_ALWAYS;
5019 }
5020 Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_NEVER);
5021}
5022
5023bool QD3D12Sampler::create()
5024{
5025 desc = {};
5026 desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
5027 if (m_compareOp != Never)
5028 desc.Filter = D3D12_FILTER(desc.Filter | 0x80);
5029 desc.AddressU = toD3DAddressMode(m_addressU);
5030 desc.AddressV = toD3DAddressMode(m_addressV);
5031 desc.AddressW = toD3DAddressMode(m_addressW);
5032 desc.MaxAnisotropy = 1.0f;
5033 desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
5034 desc.MaxLOD = m_mipmapMode == None ? 0.0f : 10000.0f;
5035
5036 QRHI_RES_RHI(QRhiD3D12);
5037 rhiD->registerResource(this, false);
5038 return true;
5039}
5040
5041QD3D12Descriptor QD3D12Sampler::lookupOrCreateShaderVisibleDescriptor()
5042{
5043 if (!shaderVisibleDescriptor.isValid()) {
5044 QRHI_RES_RHI(QRhiD3D12);
5045 shaderVisibleDescriptor = rhiD->samplerMgr.getShaderVisibleDescriptor(desc);
5046 }
5047 return shaderVisibleDescriptor;
5048}
5049
5050QD3D12ShadingRateMap::QD3D12ShadingRateMap(QRhiImplementation *rhi)
5051 : QRhiShadingRateMap(rhi)
5052{
5053}
5054
5055QD3D12ShadingRateMap::~QD3D12ShadingRateMap()
5056{
5057 destroy();
5058}
5059
5060void QD3D12ShadingRateMap::destroy()
5061{
5062 if (handle.isNull())
5063 return;
5064
5065 handle = {};
5066}
5067
5068bool QD3D12ShadingRateMap::createFrom(QRhiTexture *src)
5069{
5070 if (!handle.isNull())
5071 destroy();
5072
5073 handle = QRHI_RES(QD3D12Texture, src)->handle;
5074
5075 return true;
5076}
5077
5078QD3D12TextureRenderTarget::QD3D12TextureRenderTarget(QRhiImplementation *rhi,
5079 const QRhiTextureRenderTargetDescription &desc,
5080 Flags flags)
5081 : QRhiTextureRenderTarget(rhi, desc, flags),
5082 d(rhi)
5083{
5084}
5085
5086QD3D12TextureRenderTarget::~QD3D12TextureRenderTarget()
5087{
5088 destroy();
5089}
5090
5091void QD3D12TextureRenderTarget::destroy()
5092{
5093 if (!rtv[0].isValid() && !dsv.isValid())
5094 return;
5095
5096 QRHI_RES_RHI(QRhiD3D12);
5097 if (dsv.isValid()) {
5098 if (ownsDsv && rhiD)
5099 rhiD->releaseQueue.deferredReleaseViews(&rhiD->dsvPool, dsv, 1);
5100 dsv = {};
5101 }
5102
5103 for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
5104 if (rtv[i].isValid()) {
5105 if (ownsRtv[i] && rhiD)
5106 rhiD->releaseQueue.deferredReleaseViews(&rhiD->rtvPool, rtv[i], 1);
5107 rtv[i] = {};
5108 }
5109 }
5110
5111 if (rhiD)
5112 rhiD->unregisterResource(this);
5113}
5114
5115QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDescriptor()
5116{
5117 // not yet built so cannot rely on data computed in create()
5118
5119 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
5120
5121 rpD->colorAttachmentCount = 0;
5122 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it) {
5123 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
5124 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
5125 if (texD)
5126 rpD->colorFormat[rpD->colorAttachmentCount] = texD->rtFormat;
5127 else if (rbD)
5128 rpD->colorFormat[rpD->colorAttachmentCount] = rbD->dxgiFormat;
5129 rpD->colorAttachmentCount += 1;
5130 }
5131
5132 rpD->hasDepthStencil = false;
5133 if (m_desc.depthStencilBuffer()) {
5134 rpD->hasDepthStencil = true;
5135 rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
5136 } else if (m_desc.depthTexture()) {
5137 QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
5138 rpD->hasDepthStencil = true;
5139 rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
5140 }
5141
5142 rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
5143
5144 rpD->updateSerializedFormat();
5145
5146 QRHI_RES_RHI(QRhiD3D12);
5147 rhiD->registerResource(rpD);
5148 return rpD;
5149}
5150
5151bool QD3D12TextureRenderTarget::create()
5152{
5153 if (rtv[0].isValid() || dsv.isValid())
5154 destroy();
5155
5156 QRHI_RES_RHI(QRhiD3D12);
5157 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
5158 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
5159 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5160 d.colorAttCount = 0;
5161 int attIndex = 0;
5162
5163 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
5164 d.colorAttCount += 1;
5165 const QRhiColorAttachment &colorAtt(*it);
5166 QRhiTexture *texture = colorAtt.texture();
5167 QRhiRenderBuffer *rb = colorAtt.renderBuffer();
5168 Q_ASSERT(texture || rb);
5169 if (texture) {
5170 QD3D12Texture *texD = QRHI_RES(QD3D12Texture, texture);
5171 QD3D12Resource *res = rhiD->resourcePool.lookupRef(texD->handle);
5172 if (!res) {
5173 qWarning("Could not look up texture handle for render target");
5174 return false;
5175 }
5176 const bool isMultiView = it->multiViewCount() >= 2;
5177 UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1;
5178 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5179 rtvDesc.Format = texD->rtFormat;
5180 if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
5181 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
5182 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
5183 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
5184 rtvDesc.Texture2DArray.ArraySize = layerCount;
5185 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
5186 if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
5187 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
5188 rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
5189 rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
5190 rtvDesc.Texture1DArray.ArraySize = layerCount;
5191 } else {
5192 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D;
5193 rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
5194 }
5195 } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
5196 if (texD->sampleDesc.Count > 1) {
5197 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
5198 rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
5199 rtvDesc.Texture2DMSArray.ArraySize = layerCount;
5200 } else {
5201 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
5202 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
5203 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
5204 rtvDesc.Texture2DArray.ArraySize = layerCount;
5205 }
5206 } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
5207 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
5208 rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
5209 rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
5210 rtvDesc.Texture3D.WSize = layerCount;
5211 } else {
5212 if (texD->sampleDesc.Count > 1) {
5213 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
5214 } else {
5215 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
5216 rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
5217 }
5218 }
5219 rtv[attIndex] = rhiD->rtvPool.allocate(1);
5220 if (!rtv[attIndex].isValid()) {
5221 qWarning("Failed to allocate RTV for texture render target");
5222 return false;
5223 }
5224 rhiD->dev->CreateRenderTargetView(res->resource, &rtvDesc, rtv[attIndex].cpuHandle);
5225 ownsRtv[attIndex] = true;
5226 if (attIndex == 0) {
5227 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
5228 d.sampleCount = int(texD->sampleDesc.Count);
5229 }
5230 } else if (rb) {
5231 QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, rb);
5232 ownsRtv[attIndex] = false;
5233 rtv[attIndex] = rbD->rtv;
5234 if (attIndex == 0) {
5235 d.pixelSize = rbD->pixelSize();
5236 d.sampleCount = int(rbD->sampleDesc.Count);
5237 }
5238 }
5239 }
5240
5241 d.dpr = 1;
5242
5243 if (hasDepthStencil) {
5244 if (m_desc.depthTexture()) {
5245 ownsDsv = true;
5246 QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
5247 QD3D12Resource *res = rhiD->resourcePool.lookupRef(depthTexD->handle);
5248 if (!res) {
5249 qWarning("Could not look up depth texture handle");
5250 return false;
5251 }
5252 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
5253 dsvDesc.Format = depthTexD->rtFormat;
5254 dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
5255 : D3D12_DSV_DIMENSION_TEXTURE2D;
5256 if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
5257 if (depthTexD->sampleDesc.Count > 1) {
5258 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY;
5259 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
5260 dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
5261 dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
5262 } else {
5263 dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
5264 dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
5265 }
5266 } else {
5267 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
5268 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
5269 dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
5270 dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
5271 } else {
5272 dsvDesc.Texture2DArray.FirstArraySlice = 0;
5273 dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
5274 }
5275 }
5276 }
5277 dsv = rhiD->dsvPool.allocate(1);
5278 if (!dsv.isValid()) {
5279 qWarning("Failed to allocate DSV for texture render target");
5280 return false;
5281 }
5282 rhiD->dev->CreateDepthStencilView(res->resource, &dsvDesc, dsv.cpuHandle);
5283 if (d.colorAttCount == 0) {
5284 d.pixelSize = depthTexD->pixelSize();
5285 d.sampleCount = int(depthTexD->sampleDesc.Count);
5286 }
5287 } else {
5288 ownsDsv = false;
5289 QD3D12RenderBuffer *depthRbD = QRHI_RES(QD3D12RenderBuffer, m_desc.depthStencilBuffer());
5290 dsv = depthRbD->dsv;
5291 if (d.colorAttCount == 0) {
5292 d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
5293 d.sampleCount = int(depthRbD->sampleDesc.Count);
5294 }
5295 }
5296 d.dsAttCount = 1;
5297 } else {
5298 d.dsAttCount = 0;
5299 }
5300
5301 D3D12_CPU_DESCRIPTOR_HANDLE nullDescHandle = { 0 };
5302 for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
5303 d.rtv[i] = i < d.colorAttCount ? rtv[i].cpuHandle : nullDescHandle;
5304 d.dsv = dsv.cpuHandle;
5305 d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
5306
5307 QRhiRenderTargetAttachmentTracker::updateResIdList<QD3D12Texture, QD3D12RenderBuffer>(m_desc, &d.currentResIdList);
5308
5309 rhiD->registerResource(this);
5310 return true;
5311}
5312
5313QSize QD3D12TextureRenderTarget::pixelSize() const
5314{
5315 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D12Texture, QD3D12RenderBuffer>(m_desc, d.currentResIdList))
5316 const_cast<QD3D12TextureRenderTarget *>(this)->create();
5317
5318 return d.pixelSize;
5319}
5320
5321float QD3D12TextureRenderTarget::devicePixelRatio() const
5322{
5323 return d.dpr;
5324}
5325
5326int QD3D12TextureRenderTarget::sampleCount() const
5327{
5328 return d.sampleCount;
5329}
5330
5331QD3D12ShaderResourceBindings::QD3D12ShaderResourceBindings(QRhiImplementation *rhi)
5332 : QRhiShaderResourceBindings(rhi)
5333{
5334}
5335
5336QD3D12ShaderResourceBindings::~QD3D12ShaderResourceBindings()
5337{
5338 destroy();
5339}
5340
5341void QD3D12ShaderResourceBindings::destroy()
5342{
5343 QRHI_RES_RHI(QRhiD3D12);
5344 if (rhiD)
5345 rhiD->unregisterResource(this);
5346}
5347
5348bool QD3D12ShaderResourceBindings::create()
5349{
5350 QRHI_RES_RHI(QRhiD3D12);
5351 if (!rhiD->sanityCheckShaderResourceBindings(this))
5352 return false;
5353
5354 rhiD->updateLayoutDesc(this);
5355
5356 hasDynamicOffset = false;
5357 for (const QRhiShaderResourceBinding &b : std::as_const(m_bindings)) {
5358 const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
5359 if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
5360 hasDynamicOffset = true;
5361 break;
5362 }
5363 }
5364
5365 // The root signature is not part of the srb. Unintuitive, but the shader
5366 // translation pipeline ties our hands: as long as the per-shader (so per
5367 // stage!) nativeResourceBindingMap exist, meaning f.ex. that a SPIR-V
5368 // combined image sampler binding X passed in here may map to the tY and sY
5369 // HLSL registers, where Y is known only once the mapping table from the
5370 // shader is looked up. Creating a root parameters at this stage is
5371 // therefore impossible.
5372
5373 generation += 1;
5374 rhiD->registerResource(this, false);
5375 return true;
5376}
5377
5378void QD3D12ShaderResourceBindings::updateResources(UpdateFlags flags)
5379{
5380 Q_UNUSED(flags);
5381 generation += 1;
5382}
5383
5384// Accessing the QRhiBuffer/Texture/Sampler resources must be avoided in the
5385// callbacks; that would only be possible if the srb had those specified, and
5386// that's not required at the time of srb and pipeline create() time, and
5387// createRootSignature is called from the pipeline create().
5388
5389void QD3D12ShaderResourceBindings::visitUniformBuffer(QD3D12Stage s,
5390 const QRhiShaderResourceBinding::Data::UniformBufferData &,
5391 int shaderRegister,
5392 int)
5393{
5394 D3D12_ROOT_PARAMETER1 rootParam = {};
5395 rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
5396 rootParam.ShaderVisibility = qd3d12_stageToVisibility(s);
5397 rootParam.Descriptor.ShaderRegister = shaderRegister;
5398 rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
5399 visitorData.cbParams[s].append(rootParam);
5400}
5401
5402void QD3D12ShaderResourceBindings::visitTexture(QD3D12Stage s,
5403 const QRhiShaderResourceBinding::TextureAndSampler &,
5404 int shaderRegister)
5405{
5406 D3D12_DESCRIPTOR_RANGE1 range = {};
5407 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
5408 range.NumDescriptors = 1;
5409 range.BaseShaderRegister = shaderRegister;
5410 range.OffsetInDescriptorsFromTableStart = visitorData.currentSrvRangeOffset[s];
5411 visitorData.currentSrvRangeOffset[s] += 1;
5412 visitorData.srvRanges[s].append(range);
5413 if (visitorData.srvRanges[s].count() == 1) {
5414 visitorData.srvTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5415 visitorData.srvTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5416 }
5417}
5418
5419void QD3D12ShaderResourceBindings::visitSampler(QD3D12Stage s,
5420 const QRhiShaderResourceBinding::TextureAndSampler &,
5421 int shaderRegister)
5422{
5423 // Unlike SRVs and UAVs, samplers are handled so that each sampler becomes
5424 // a root parameter with its own descriptor table.
5425
5426 int &rangeStoreIdx(visitorData.samplerRangeHeads[s]);
5427 if (rangeStoreIdx == 16) {
5428 qWarning("Sampler count in QD3D12Stage %d exceeds the limit of 16, this is disallowed by QRhi", s);
5429 return;
5430 }
5431 D3D12_DESCRIPTOR_RANGE1 range = {};
5432 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
5433 range.NumDescriptors = 1;
5434 range.BaseShaderRegister = shaderRegister;
5435 visitorData.samplerRanges[s][rangeStoreIdx] = range;
5436 D3D12_ROOT_PARAMETER1 param = {};
5437 param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5438 param.ShaderVisibility = qd3d12_stageToVisibility(s);
5439 param.DescriptorTable.NumDescriptorRanges = 1;
5440 param.DescriptorTable.pDescriptorRanges = &visitorData.samplerRanges[s][rangeStoreIdx];
5441 rangeStoreIdx += 1;
5442 visitorData.samplerTables[s].append(param);
5443}
5444
5445void QD3D12ShaderResourceBindings::visitStorageBuffer(QD3D12Stage s,
5446 const QRhiShaderResourceBinding::Data::StorageBufferData &,
5447 QD3D12ShaderResourceVisitor::StorageOp,
5448 int shaderRegister)
5449{
5450 D3D12_DESCRIPTOR_RANGE1 range = {};
5451 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
5452 range.NumDescriptors = 1;
5453 range.BaseShaderRegister = shaderRegister;
5454 range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
5455 visitorData.currentUavRangeOffset[s] += 1;
5456 visitorData.uavRanges[s].append(range);
5457 if (visitorData.uavRanges[s].count() == 1) {
5458 visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5459 visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5460 }
5461}
5462
5463void QD3D12ShaderResourceBindings::visitStorageImage(QD3D12Stage s,
5464 const QRhiShaderResourceBinding::Data::StorageImageData &,
5465 QD3D12ShaderResourceVisitor::StorageOp,
5466 int shaderRegister)
5467{
5468 D3D12_DESCRIPTOR_RANGE1 range = {};
5469 range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
5470 range.NumDescriptors = 1;
5471 range.BaseShaderRegister = shaderRegister;
5472 range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
5473 visitorData.currentUavRangeOffset[s] += 1;
5474 visitorData.uavRanges[s].append(range);
5475 if (visitorData.uavRanges[s].count() == 1) {
5476 visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
5477 visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
5478 }
5479}
5480
5481QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D12ShaderStageData *stageData,
5482 int stageCount)
5483{
5484 QRHI_RES_RHI(QRhiD3D12);
5485
5486 // It's not just that the root signature has to be tied to the pipeline
5487 // (cannot just freely create it like e.g. with Vulkan where one just
5488 // creates a descriptor layout 1:1 with the QRhiShaderResourceBindings'
5489 // data), due to not knowing the shader-specific resource binding mapping
5490 // tables at the point of srb creation, but each shader stage may have a
5491 // different mapping table. (ugh!)
5492 //
5493 // Hence we set up everything per-stage, even if it means the root
5494 // signature gets unnecessarily big. (note that the magic is in the
5495 // ShaderVisibility: even though the register range is the same in the
5496 // descriptor tables, the visibility is different)
5497
5498 QD3D12ShaderResourceVisitor visitor(this, stageData, stageCount);
5499
5500 visitorData = {};
5501
5502 using namespace std::placeholders;
5503 visitor.uniformBuffer = std::bind(&QD3D12ShaderResourceBindings::visitUniformBuffer, this, _1, _2, _3, _4);
5504 visitor.texture = std::bind(&QD3D12ShaderResourceBindings::visitTexture, this, _1, _2, _3);
5505 visitor.sampler = std::bind(&QD3D12ShaderResourceBindings::visitSampler, this, _1, _2, _3);
5506 visitor.storageBuffer = std::bind(&QD3D12ShaderResourceBindings::visitStorageBuffer, this, _1, _2, _3, _4);
5507 visitor.storageImage = std::bind(&QD3D12ShaderResourceBindings::visitStorageImage, this, _1, _2, _3, _4);
5508
5509 visitor.visit();
5510
5511 // The maximum size of a root signature is 256 bytes, where a descriptor
5512 // table is 4, a root descriptor (e.g. CBV) is 8. We have 5 stages at most
5513 // (or 1 with compute) and a separate descriptor table for SRVs (->
5514 // textures) and UAVs (-> storage buffers and images) per stage, plus each
5515 // uniform buffer counts as a CBV in the stages it is visible.
5516 //
5517 // Due to the limited maximum size of a shader-visible sampler heap (2048)
5518 // and the potential costly switching of descriptor heaps, each sampler is
5519 // declared as a separate root parameter / descriptor table (meaning that
5520 // two samplers in the same stage are two parameters and two tables, not
5521 // just one). QRhi documents a hard limit of 16 on texture/sampler bindings
5522 // in a shader (matching D3D11), so we can hopefully get away with this.
5523 //
5524 // This means that e.g. a vertex+fragment shader with a uniform buffer
5525 // visible in both and one texture+sampler in the fragment shader would
5526 // consume 2*8 + 4 + 4 = 24 bytes. This also implies that clients
5527 // specifying the minimal stage bit mask for each entry in
5528 // QRhiShaderResourceBindings are ideal for this backend since it helps
5529 // reducing the chance of hitting the size limit.
5530
5531 QVarLengthArray<D3D12_ROOT_PARAMETER1, 4> rootParams;
5532 for (int s = 0; s < 6; ++s) {
5533 if (!visitorData.cbParams[s].isEmpty())
5534 rootParams.append(visitorData.cbParams[s].constData(), visitorData.cbParams[s].count());
5535 }
5536 for (int s = 0; s < 6; ++s) {
5537 if (!visitorData.srvRanges[s].isEmpty()) {
5538 visitorData.srvTables[s].DescriptorTable.NumDescriptorRanges = visitorData.srvRanges[s].count();
5539 visitorData.srvTables[s].DescriptorTable.pDescriptorRanges = visitorData.srvRanges[s].constData();
5540 rootParams.append(visitorData.srvTables[s]);
5541 }
5542 }
5543 for (int s = 0; s < 6; ++s) {
5544 if (!visitorData.samplerTables[s].isEmpty())
5545 rootParams.append(visitorData.samplerTables[s].constData(), visitorData.samplerTables[s].count());
5546 }
5547 for (int s = 0; s < 6; ++s) {
5548 if (!visitorData.uavRanges[s].isEmpty()) {
5549 visitorData.uavTables[s].DescriptorTable.NumDescriptorRanges = visitorData.uavRanges[s].count();
5550 visitorData.uavTables[s].DescriptorTable.pDescriptorRanges = visitorData.uavRanges[s].constData();
5551 rootParams.append(visitorData.uavTables[s]);
5552 }
5553 }
5554
5555 D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
5556 rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
5557 if (!rootParams.isEmpty()) {
5558 rsDesc.Desc_1_1.NumParameters = rootParams.count();
5559 rsDesc.Desc_1_1.pParameters = rootParams.constData();
5560 }
5561
5562 UINT rsFlags = 0;
5563 for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) {
5564 if (stageData[stageIdx].valid && stageData[stageIdx].stage == VS)
5565 rsFlags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
5566 }
5567 rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags);
5568
5569 ID3DBlob *signature = nullptr;
5570 HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
5571 if (FAILED(hr)) {
5572 qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
5573 return {};
5574 }
5575 ID3D12RootSignature *rootSig = nullptr;
5576 hr = rhiD->dev->CreateRootSignature(0,
5577 signature->GetBufferPointer(),
5578 signature->GetBufferSize(),
5579 __uuidof(ID3D12RootSignature),
5580 reinterpret_cast<void **>(&rootSig));
5581 signature->Release();
5582 if (FAILED(hr)) {
5583 qWarning("Failed to create root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
5584 return {};
5585 }
5586
5587 return QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
5588}
5589
5590// For shader model < 6.0 we do the same as the D3D11 backend: use the old
5591// compiler (D3DCompile) to generate DXBC, just as qsb does (when -c is passed)
5592// by invoking fxc, not dxc. For SM >= 6.0 we have to use the new compiler and
5593// work with DXIL. And that involves IDxcCompiler and needs the presence of
5594// dxcompiler.dll and dxil.dll at runtime. Plus there's a chance we have
5595// ancient SDK headers when not using MSVC. So this is heavily optional,
5596// meaning support for dxc can be disabled both at build time (no dxcapi.h) and
5597// at run time (no DLLs).
5598
5599static inline void makeHlslTargetString(char target[7], const char stage[3], int version)
5600{
5601 const int smMajor = version / 10;
5602 const int smMinor = version % 10;
5603 target[0] = stage[0];
5604 target[1] = stage[1];
5605 target[2] = '_';
5606 target[3] = '0' + smMajor;
5607 target[4] = '_';
5608 target[5] = '0' + smMinor;
5609 target[6] = '\0';
5610}
5611
5612enum class HlslCompileFlag
5613{
5614 WithDebugInfo = 0x01
5615};
5616
5617static QByteArray legacyCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
5618{
5619 static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
5620 if (!d3dCompile) {
5621 qWarning("Unable to resolve function D3DCompile()");
5622 return QByteArray();
5623 }
5624
5625 ID3DBlob *bytecode = nullptr;
5626 ID3DBlob *errors = nullptr;
5627 UINT d3dCompileFlags = 0;
5628 if (flags & int(HlslCompileFlag::WithDebugInfo))
5629 d3dCompileFlags |= D3DCOMPILE_DEBUG;
5630
5631 HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
5632 nullptr, nullptr, nullptr,
5633 hlslSource.entryPoint().constData(), target, d3dCompileFlags, 0, &bytecode, &errors);
5634 if (FAILED(hr) || !bytecode) {
5635 qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
5636 if (errors) {
5637 *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
5638 int(errors->GetBufferSize()));
5639 errors->Release();
5640 }
5641 return QByteArray();
5642 }
5643
5644 QByteArray result;
5645 result.resize(int(bytecode->GetBufferSize()));
5646 memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
5647 bytecode->Release();
5648 return result;
5649}
5650
5651#ifdef QRHI_D3D12_HAS_DXC
5652
5653#ifndef DXC_CP_UTF8
5654#define DXC_CP_UTF8 65001
5655#endif
5656
5657#ifndef DXC_ARG_DEBUG
5658#define DXC_ARG_DEBUG L"-Zi"
5659#endif
5660
5661static QByteArray dxcCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
5662{
5663 static std::pair<IDxcCompiler *, IDxcLibrary *> dxc = QRhiD3D::createDxcCompiler();
5664 IDxcCompiler *compiler = dxc.first;
5665 if (!compiler) {
5666 qWarning("Unable to instantiate IDxcCompiler. Likely no dxcompiler.dll and dxil.dll present. "
5667 "Use windeployqt or try https://github.com/microsoft/DirectXShaderCompiler/releases");
5668 return QByteArray();
5669 }
5670 IDxcLibrary *library = dxc.second;
5671 if (!library)
5672 return QByteArray();
5673
5674 IDxcBlobEncoding *sourceBlob = nullptr;
5675 HRESULT hr = library->CreateBlobWithEncodingOnHeapCopy(hlslSource.shader().constData(),
5676 UINT32(hlslSource.shader().size()),
5677 DXC_CP_UTF8,
5678 &sourceBlob);
5679 if (FAILED(hr)) {
5680 qWarning("Failed to create source blob for dxc: 0x%x (%s)",
5681 uint(hr),
5682 qPrintable(QSystemError::windowsComString(hr)));
5683 return QByteArray();
5684 }
5685
5686 const QString entryPointStr = QString::fromLatin1(hlslSource.entryPoint());
5687 const QString targetStr = QString::fromLatin1(target);
5688
5689 QVarLengthArray<LPCWSTR, 4> argPtrs;
5690 QString debugArg;
5691 if (flags & int(HlslCompileFlag::WithDebugInfo)) {
5692 debugArg = QString::fromUtf16(reinterpret_cast<const char16_t *>(DXC_ARG_DEBUG));
5693 argPtrs.append(reinterpret_cast<LPCWSTR>(debugArg.utf16()));
5694 }
5695
5696 IDxcOperationResult *result = nullptr;
5697 hr = compiler->Compile(sourceBlob,
5698 nullptr,
5699 reinterpret_cast<LPCWSTR>(entryPointStr.utf16()),
5700 reinterpret_cast<LPCWSTR>(targetStr.utf16()),
5701 argPtrs.data(), argPtrs.count(),
5702 nullptr, 0,
5703 nullptr,
5704 &result);
5705 sourceBlob->Release();
5706 if (SUCCEEDED(hr))
5707 result->GetStatus(&hr);
5708 if (FAILED(hr)) {
5709 qWarning("HLSL shader compilation failed: 0x%x (%s)",
5710 uint(hr),
5711 qPrintable(QSystemError::windowsComString(hr)));
5712 if (result) {
5713 IDxcBlobEncoding *errorsBlob = nullptr;
5714 if (SUCCEEDED(result->GetErrorBuffer(&errorsBlob))) {
5715 if (errorsBlob) {
5716 *error = QString::fromUtf8(static_cast<const char *>(errorsBlob->GetBufferPointer()),
5717 int(errorsBlob->GetBufferSize()));
5718 errorsBlob->Release();
5719 }
5720 }
5721 }
5722 return QByteArray();
5723 }
5724
5725 IDxcBlob *bytecode = nullptr;
5726 if FAILED(result->GetResult(&bytecode)) {
5727 qWarning("No result from IDxcCompiler: 0x%x (%s)",
5728 uint(hr),
5729 qPrintable(QSystemError::windowsComString(hr)));
5730 return QByteArray();
5731 }
5732
5733 QByteArray ba;
5734 ba.resize(int(bytecode->GetBufferSize()));
5735 memcpy(ba.data(), bytecode->GetBufferPointer(), size_t(ba.size()));
5736 bytecode->Release();
5737 return ba;
5738}
5739
5740#endif // QRHI_D3D12_HAS_DXC
5741
5742static QByteArray compileHlslShaderSource(const QShader &shader,
5743 QShader::Variant shaderVariant,
5744 int flags,
5745 QString *error,
5746 QShaderKey *usedShaderKey)
5747{
5748 // look for SM 6.7, 6.6, .., 5.0
5749 const int shaderModelMax = 67;
5750 for (int sm = shaderModelMax; sm >= 50; --sm) {
5751 for (QShader::Source type : { QShader::DxilShader, QShader::DxbcShader }) {
5752 QShaderKey key = { type, sm, shaderVariant };
5753 QShaderCode intermediateBytecodeShader = shader.shader(key);
5754 if (!intermediateBytecodeShader.shader().isEmpty()) {
5755 if (usedShaderKey)
5756 *usedShaderKey = key;
5757 return intermediateBytecodeShader.shader();
5758 }
5759 }
5760 }
5761
5762 QShaderCode hlslSource;
5763 QShaderKey key;
5764 for (int sm = shaderModelMax; sm >= 50; --sm) {
5765 key = { QShader::HlslShader, sm, shaderVariant };
5766 hlslSource = shader.shader(key);
5767 if (!hlslSource.shader().isEmpty())
5768 break;
5769 }
5770
5771 if (hlslSource.shader().isEmpty()) {
5772 qWarning() << "No HLSL (shader model 6.7..5.0) code found in baked shader" << shader;
5773 return QByteArray();
5774 }
5775
5776 if (usedShaderKey)
5777 *usedShaderKey = key;
5778
5779 char target[7];
5780 switch (shader.stage()) {
5781 case QShader::VertexStage:
5782 makeHlslTargetString(target, "vs", key.sourceVersion().version());
5783 break;
5784 case QShader::TessellationControlStage:
5785 makeHlslTargetString(target, "hs", key.sourceVersion().version());
5786 break;
5787 case QShader::TessellationEvaluationStage:
5788 makeHlslTargetString(target, "ds", key.sourceVersion().version());
5789 break;
5790 case QShader::GeometryStage:
5791 makeHlslTargetString(target, "gs", key.sourceVersion().version());
5792 break;
5793 case QShader::FragmentStage:
5794 makeHlslTargetString(target, "ps", key.sourceVersion().version());
5795 break;
5796 case QShader::ComputeStage:
5797 makeHlslTargetString(target, "cs", key.sourceVersion().version());
5798 break;
5799 }
5800
5801 if (key.sourceVersion().version() >= 60) {
5802#ifdef QRHI_D3D12_HAS_DXC
5803 return dxcCompile(hlslSource, target, flags, error);
5804#else
5805 qWarning("Attempted to runtime-compile HLSL source code for shader model >= 6.0 "
5806 "but the Qt build has no support for DXC. "
5807 "Rebuild Qt with a recent Windows SDK or switch to an MSVC build.");
5808#endif
5809 }
5810
5811 return legacyCompile(hlslSource, target, flags, error);
5812}
5813
5814static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
5815{
5816 UINT8 f = 0;
5817 if (c.testFlag(QRhiGraphicsPipeline::R))
5818 f |= D3D12_COLOR_WRITE_ENABLE_RED;
5819 if (c.testFlag(QRhiGraphicsPipeline::G))
5820 f |= D3D12_COLOR_WRITE_ENABLE_GREEN;
5821 if (c.testFlag(QRhiGraphicsPipeline::B))
5822 f |= D3D12_COLOR_WRITE_ENABLE_BLUE;
5823 if (c.testFlag(QRhiGraphicsPipeline::A))
5824 f |= D3D12_COLOR_WRITE_ENABLE_ALPHA;
5825 return f;
5826}
5827
5828static inline D3D12_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
5829{
5830 // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
5831 // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
5832 // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
5833 // set as an alpha src/dest factor), but for D3D we have to take care of it
5834 // ourselves. Hence the rgb argument.
5835
5836 switch (f) {
5837 case QRhiGraphicsPipeline::Zero:
5838 return D3D12_BLEND_ZERO;
5839 case QRhiGraphicsPipeline::One:
5840 return D3D12_BLEND_ONE;
5841 case QRhiGraphicsPipeline::SrcColor:
5842 return rgb ? D3D12_BLEND_SRC_COLOR : D3D12_BLEND_SRC_ALPHA;
5843 case QRhiGraphicsPipeline::OneMinusSrcColor:
5844 return rgb ? D3D12_BLEND_INV_SRC_COLOR : D3D12_BLEND_INV_SRC_ALPHA;
5845 case QRhiGraphicsPipeline::DstColor:
5846 return rgb ? D3D12_BLEND_DEST_COLOR : D3D12_BLEND_DEST_ALPHA;
5847 case QRhiGraphicsPipeline::OneMinusDstColor:
5848 return rgb ? D3D12_BLEND_INV_DEST_COLOR : D3D12_BLEND_INV_DEST_ALPHA;
5849 case QRhiGraphicsPipeline::SrcAlpha:
5850 return D3D12_BLEND_SRC_ALPHA;
5851 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5852 return D3D12_BLEND_INV_SRC_ALPHA;
5853 case QRhiGraphicsPipeline::DstAlpha:
5854 return D3D12_BLEND_DEST_ALPHA;
5855 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5856 return D3D12_BLEND_INV_DEST_ALPHA;
5857 case QRhiGraphicsPipeline::ConstantColor:
5858 case QRhiGraphicsPipeline::ConstantAlpha:
5859 return D3D12_BLEND_BLEND_FACTOR;
5860 case QRhiGraphicsPipeline::OneMinusConstantColor:
5861 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5862 return D3D12_BLEND_INV_BLEND_FACTOR;
5863 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5864 return D3D12_BLEND_SRC_ALPHA_SAT;
5865 case QRhiGraphicsPipeline::Src1Color:
5866 return rgb ? D3D12_BLEND_SRC1_COLOR : D3D12_BLEND_SRC1_ALPHA;
5867 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5868 return rgb ? D3D12_BLEND_INV_SRC1_COLOR : D3D12_BLEND_INV_SRC1_ALPHA;
5869 case QRhiGraphicsPipeline::Src1Alpha:
5870 return D3D12_BLEND_SRC1_ALPHA;
5871 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5872 return D3D12_BLEND_INV_SRC1_ALPHA;
5873 }
5874 Q_UNREACHABLE_RETURN(D3D12_BLEND_ZERO);
5875}
5876
5877static inline D3D12_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
5878{
5879 switch (op) {
5880 case QRhiGraphicsPipeline::Add:
5881 return D3D12_BLEND_OP_ADD;
5882 case QRhiGraphicsPipeline::Subtract:
5883 return D3D12_BLEND_OP_SUBTRACT;
5884 case QRhiGraphicsPipeline::ReverseSubtract:
5885 return D3D12_BLEND_OP_REV_SUBTRACT;
5886 case QRhiGraphicsPipeline::Min:
5887 return D3D12_BLEND_OP_MIN;
5888 case QRhiGraphicsPipeline::Max:
5889 return D3D12_BLEND_OP_MAX;
5890 }
5891 Q_UNREACHABLE_RETURN(D3D12_BLEND_OP_ADD);
5892}
5893
5894static inline D3D12_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
5895{
5896 switch (c) {
5897 case QRhiGraphicsPipeline::None:
5898 return D3D12_CULL_MODE_NONE;
5899 case QRhiGraphicsPipeline::Front:
5900 return D3D12_CULL_MODE_FRONT;
5901 case QRhiGraphicsPipeline::Back:
5902 return D3D12_CULL_MODE_BACK;
5903 }
5904 Q_UNREACHABLE_RETURN(D3D12_CULL_MODE_NONE);
5905}
5906
5907static inline D3D12_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
5908{
5909 switch (mode) {
5910 case QRhiGraphicsPipeline::Fill:
5911 return D3D12_FILL_MODE_SOLID;
5912 case QRhiGraphicsPipeline::Line:
5913 return D3D12_FILL_MODE_WIREFRAME;
5914 }
5915 Q_UNREACHABLE_RETURN(D3D12_FILL_MODE_SOLID);
5916}
5917
5918static inline D3D12_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
5919{
5920 switch (op) {
5921 case QRhiGraphicsPipeline::Never:
5922 return D3D12_COMPARISON_FUNC_NEVER;
5923 case QRhiGraphicsPipeline::Less:
5924 return D3D12_COMPARISON_FUNC_LESS;
5925 case QRhiGraphicsPipeline::Equal:
5926 return D3D12_COMPARISON_FUNC_EQUAL;
5927 case QRhiGraphicsPipeline::LessOrEqual:
5928 return D3D12_COMPARISON_FUNC_LESS_EQUAL;
5929 case QRhiGraphicsPipeline::Greater:
5930 return D3D12_COMPARISON_FUNC_GREATER;
5931 case QRhiGraphicsPipeline::NotEqual:
5932 return D3D12_COMPARISON_FUNC_NOT_EQUAL;
5933 case QRhiGraphicsPipeline::GreaterOrEqual:
5934 return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
5935 case QRhiGraphicsPipeline::Always:
5936 return D3D12_COMPARISON_FUNC_ALWAYS;
5937 }
5938 Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_ALWAYS);
5939}
5940
5941static inline D3D12_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
5942{
5943 switch (op) {
5944 case QRhiGraphicsPipeline::StencilZero:
5945 return D3D12_STENCIL_OP_ZERO;
5946 case QRhiGraphicsPipeline::Keep:
5947 return D3D12_STENCIL_OP_KEEP;
5948 case QRhiGraphicsPipeline::Replace:
5949 return D3D12_STENCIL_OP_REPLACE;
5950 case QRhiGraphicsPipeline::IncrementAndClamp:
5951 return D3D12_STENCIL_OP_INCR_SAT;
5952 case QRhiGraphicsPipeline::DecrementAndClamp:
5953 return D3D12_STENCIL_OP_DECR_SAT;
5954 case QRhiGraphicsPipeline::Invert:
5955 return D3D12_STENCIL_OP_INVERT;
5956 case QRhiGraphicsPipeline::IncrementAndWrap:
5957 return D3D12_STENCIL_OP_INCR;
5958 case QRhiGraphicsPipeline::DecrementAndWrap:
5959 return D3D12_STENCIL_OP_DECR;
5960 }
5961 Q_UNREACHABLE_RETURN(D3D12_STENCIL_OP_KEEP);
5962}
5963
5964static inline D3D12_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
5965{
5966 switch (t) {
5967 case QRhiGraphicsPipeline::Triangles:
5968 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
5969 case QRhiGraphicsPipeline::TriangleStrip:
5970 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
5971 case QRhiGraphicsPipeline::TriangleFan:
5972 qWarning("Triangle fans are not supported with D3D");
5973 return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
5974 case QRhiGraphicsPipeline::Lines:
5975 return D3D_PRIMITIVE_TOPOLOGY_LINELIST;
5976 case QRhiGraphicsPipeline::LineStrip:
5977 return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
5978 case QRhiGraphicsPipeline::Points:
5979 return D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
5980 case QRhiGraphicsPipeline::Patches:
5981 Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
5982 return D3D_PRIMITIVE_TOPOLOGY(D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
5983 }
5984 Q_UNREACHABLE_RETURN(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
5985}
5986
5987static inline D3D12_PRIMITIVE_TOPOLOGY_TYPE toD3DTopologyType(QRhiGraphicsPipeline::Topology t)
5988{
5989 switch (t) {
5990 case QRhiGraphicsPipeline::Triangles:
5991 case QRhiGraphicsPipeline::TriangleStrip:
5992 case QRhiGraphicsPipeline::TriangleFan:
5993 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
5994 case QRhiGraphicsPipeline::Lines:
5995 case QRhiGraphicsPipeline::LineStrip:
5996 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
5997 case QRhiGraphicsPipeline::Points:
5998 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
5999 case QRhiGraphicsPipeline::Patches:
6000 return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
6001 }
6002 Q_UNREACHABLE_RETURN(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
6003}
6004
6005static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
6006{
6007 switch (format) {
6008 case QRhiVertexInputAttribute::Float4:
6009 return DXGI_FORMAT_R32G32B32A32_FLOAT;
6010 case QRhiVertexInputAttribute::Float3:
6011 return DXGI_FORMAT_R32G32B32_FLOAT;
6012 case QRhiVertexInputAttribute::Float2:
6013 return DXGI_FORMAT_R32G32_FLOAT;
6014 case QRhiVertexInputAttribute::Float:
6015 return DXGI_FORMAT_R32_FLOAT;
6016 case QRhiVertexInputAttribute::UNormByte4:
6017 return DXGI_FORMAT_R8G8B8A8_UNORM;
6018 case QRhiVertexInputAttribute::UNormByte2:
6019 return DXGI_FORMAT_R8G8_UNORM;
6020 case QRhiVertexInputAttribute::UNormByte:
6021 return DXGI_FORMAT_R8_UNORM;
6022 case QRhiVertexInputAttribute::UInt4:
6023 return DXGI_FORMAT_R32G32B32A32_UINT;
6024 case QRhiVertexInputAttribute::UInt3:
6025 return DXGI_FORMAT_R32G32B32_UINT;
6026 case QRhiVertexInputAttribute::UInt2:
6027 return DXGI_FORMAT_R32G32_UINT;
6028 case QRhiVertexInputAttribute::UInt:
6029 return DXGI_FORMAT_R32_UINT;
6030 case QRhiVertexInputAttribute::SInt4:
6031 return DXGI_FORMAT_R32G32B32A32_SINT;
6032 case QRhiVertexInputAttribute::SInt3:
6033 return DXGI_FORMAT_R32G32B32_SINT;
6034 case QRhiVertexInputAttribute::SInt2:
6035 return DXGI_FORMAT_R32G32_SINT;
6036 case QRhiVertexInputAttribute::SInt:
6037 return DXGI_FORMAT_R32_SINT;
6038 case QRhiVertexInputAttribute::Half4:
6039 // Note: D3D does not support half3. Pass through half3 as half4.
6040 case QRhiVertexInputAttribute::Half3:
6041 return DXGI_FORMAT_R16G16B16A16_FLOAT;
6042 case QRhiVertexInputAttribute::Half2:
6043 return DXGI_FORMAT_R16G16_FLOAT;
6044 case QRhiVertexInputAttribute::Half:
6045 return DXGI_FORMAT_R16_FLOAT;
6046 case QRhiVertexInputAttribute::UShort4:
6047 // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
6048 case QRhiVertexInputAttribute::UShort3:
6049 return DXGI_FORMAT_R16G16B16A16_UINT;
6050 case QRhiVertexInputAttribute::UShort2:
6051 return DXGI_FORMAT_R16G16_UINT;
6052 case QRhiVertexInputAttribute::UShort:
6053 return DXGI_FORMAT_R16_UINT;
6054 case QRhiVertexInputAttribute::SShort4:
6055 // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
6056 case QRhiVertexInputAttribute::SShort3:
6057 return DXGI_FORMAT_R16G16B16A16_SINT;
6058 case QRhiVertexInputAttribute::SShort2:
6059 return DXGI_FORMAT_R16G16_SINT;
6060 case QRhiVertexInputAttribute::SShort:
6061 return DXGI_FORMAT_R16_SINT;
6062 }
6063 Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32G32B32A32_FLOAT);
6064}
6065
6066QD3D12GraphicsPipeline::QD3D12GraphicsPipeline(QRhiImplementation *rhi)
6067 : QRhiGraphicsPipeline(rhi)
6068{
6069}
6070
6071QD3D12GraphicsPipeline::~QD3D12GraphicsPipeline()
6072{
6073 destroy();
6074}
6075
6076void QD3D12GraphicsPipeline::destroy()
6077{
6078 if (handle.isNull())
6079 return;
6080
6081 QRHI_RES_RHI(QRhiD3D12);
6082 if (rhiD) {
6083 rhiD->releaseQueue.deferredReleasePipeline(handle);
6084 rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
6085 }
6086
6087 handle = {};
6088 stageData = {};
6089
6090 if (rhiD)
6091 rhiD->unregisterResource(this);
6092}
6093
6094bool QD3D12GraphicsPipeline::create()
6095{
6096 if (!handle.isNull())
6097 destroy();
6098
6099 QRHI_RES_RHI(QRhiD3D12);
6100 if (!rhiD->sanityCheckGraphicsPipeline(this))
6101 return false;
6102
6103 rhiD->pipelineCreationStart();
6104
6105 QByteArray shaderBytecode[5];
6106 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6107 const QD3D12Stage d3dStage = qd3d12_stage(shaderStage.type());
6108 stageData[d3dStage].valid = true;
6109 stageData[d3dStage].stage = d3dStage;
6110 auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(shaderStage);
6111 if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
6112 shaderBytecode[d3dStage] = cacheIt->bytecode;
6113 stageData[d3dStage].nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
6114 } else {
6115 QString error;
6116 QShaderKey shaderKey;
6117 int compileFlags = 0;
6118 if (m_flags.testFlag(CompileShadersWithDebugInfo))
6119 compileFlags |= int(HlslCompileFlag::WithDebugInfo);
6120 const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(),
6121 shaderStage.shaderVariant(),
6122 compileFlags,
6123 &error,
6124 &shaderKey);
6125 if (bytecode.isEmpty()) {
6126 qWarning("HLSL graphics shader compilation failed: %s", qPrintable(error));
6127 return false;
6128 }
6129
6130 shaderBytecode[d3dStage] = bytecode;
6131 stageData[d3dStage].nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
6132 rhiD->shaderBytecodeCache.insertWithCapacityLimit(shaderStage,
6133 { bytecode, stageData[d3dStage].nativeResourceBindingMap });
6134 }
6135 }
6136
6137 QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
6138 if (srbD) {
6139 rootSigHandle = srbD->createRootSignature(stageData.data(), 5);
6140 if (rootSigHandle.isNull()) {
6141 qWarning("Failed to create root signature");
6142 return false;
6143 }
6144 }
6145 ID3D12RootSignature *rootSig = nullptr;
6146 if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
6147 rootSig = rs->rootSig;
6148 if (!rootSig) {
6149 qWarning("Cannot create graphics pipeline state without root signature");
6150 return false;
6151 }
6152
6153 QD3D12RenderPassDescriptor *rpD = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
6154 DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
6155 if (rpD->colorAttachmentCount > 0) {
6156 format = DXGI_FORMAT(rpD->colorFormat[0]);
6157 } else if (rpD->hasDepthStencil) {
6158 format = DXGI_FORMAT(rpD->dsFormat);
6159 } else {
6160 qWarning("Cannot create graphics pipeline state without color or depthStencil format");
6161 return false;
6162 }
6163 const DXGI_SAMPLE_DESC sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, format);
6164
6165 struct {
6166 QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
6167 QD3D12PipelineStateSubObject<D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> inputLayout;
6168 QD3D12PipelineStateSubObject<D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE> primitiveRestartValue;
6169 QD3D12PipelineStateSubObject<D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> primitiveTopology;
6170 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> VS;
6171 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> HS;
6172 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> DS;
6173 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> GS;
6174 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> PS;
6175 QD3D12PipelineStateSubObject<D3D12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER> rasterizerState;
6176 QD3D12PipelineStateSubObject<D3D12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL> depthStencilState;
6177 QD3D12PipelineStateSubObject<D3D12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND> blendState;
6178 QD3D12PipelineStateSubObject<D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> rtFormats;
6179 QD3D12PipelineStateSubObject<DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> dsFormat;
6180 QD3D12PipelineStateSubObject<DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC> sampleDesc;
6181 QD3D12PipelineStateSubObject<UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK> sampleMask;
6182 QD3D12PipelineStateSubObject<D3D12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING> viewInstancingDesc;
6183 } stream;
6184
6185 stream.rootSig.object = rootSig;
6186
6187 QVarLengthArray<D3D12_INPUT_ELEMENT_DESC, 4> inputDescs;
6188 QByteArrayList matrixSliceSemantics;
6189 if (!shaderBytecode[VS].isEmpty()) {
6190 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
6191 it != itEnd; ++it)
6192 {
6193 D3D12_INPUT_ELEMENT_DESC desc = {};
6194 // The output from SPIRV-Cross uses TEXCOORD<location> as the
6195 // semantic, except for matrices that are unrolled into consecutive
6196 // vec2/3/4s attributes and need TEXCOORD<location>_ as
6197 // SemanticName and row/column index as SemanticIndex.
6198 const int matrixSlice = it->matrixSlice();
6199 if (matrixSlice < 0) {
6200 desc.SemanticName = "TEXCOORD";
6201 desc.SemanticIndex = UINT(it->location());
6202 } else {
6203 QByteArray sem;
6204 sem.resize(16);
6205 std::snprintf(sem.data(), sem.size(), "TEXCOORD%d_", it->location() - matrixSlice);
6206 matrixSliceSemantics.append(sem);
6207 desc.SemanticName = matrixSliceSemantics.last().constData();
6208 desc.SemanticIndex = UINT(matrixSlice);
6209 }
6210 desc.Format = toD3DAttributeFormat(it->format());
6211 desc.InputSlot = UINT(it->binding());
6212 desc.AlignedByteOffset = it->offset();
6213 const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
6214 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
6215 desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA;
6216 desc.InstanceDataStepRate = inputBinding->instanceStepRate();
6217 } else {
6218 desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
6219 }
6220 inputDescs.append(desc);
6221 }
6222 }
6223
6224 stream.inputLayout.object.NumElements = inputDescs.count();
6225 stream.inputLayout.object.pInputElementDescs = inputDescs.isEmpty() ? nullptr : inputDescs.constData();
6226
6227 stream.primitiveRestartValue.object = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF;
6228
6229 stream.primitiveTopology.object = toD3DTopologyType(m_topology);
6230 topology = toD3DTopology(m_topology, m_patchControlPointCount);
6231
6232 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6233 const int d3dStage = qd3d12_stage(shaderStage.type());
6234 switch (d3dStage) {
6235 case VS:
6236 stream.VS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6237 stream.VS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6238 break;
6239 case HS:
6240 stream.HS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6241 stream.HS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6242 break;
6243 case DS:
6244 stream.DS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6245 stream.DS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6246 break;
6247 case GS:
6248 stream.GS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6249 stream.GS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6250 break;
6251 case PS:
6252 stream.PS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
6253 stream.PS.object.BytecodeLength = shaderBytecode[d3dStage].size();
6254 break;
6255 default:
6256 Q_UNREACHABLE();
6257 break;
6258 }
6259 }
6260
6261 stream.rasterizerState.object.FillMode = toD3DFillMode(m_polygonMode);
6262 stream.rasterizerState.object.CullMode = toD3DCullMode(m_cullMode);
6263 stream.rasterizerState.object.FrontCounterClockwise = m_frontFace == CCW;
6264 stream.rasterizerState.object.DepthBias = m_depthBias;
6265 stream.rasterizerState.object.SlopeScaledDepthBias = m_slopeScaledDepthBias;
6266 stream.rasterizerState.object.DepthClipEnable = TRUE;
6267 stream.rasterizerState.object.MultisampleEnable = sampleDesc.Count > 1;
6268
6269 stream.depthStencilState.object.DepthEnable = m_depthTest;
6270 stream.depthStencilState.object.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
6271 stream.depthStencilState.object.DepthFunc = toD3DCompareOp(m_depthOp);
6272 stream.depthStencilState.object.StencilEnable = m_stencilTest;
6273 if (m_stencilTest) {
6274 stream.depthStencilState.object.StencilReadMask = UINT8(m_stencilReadMask);
6275 stream.depthStencilState.object.StencilWriteMask = UINT8(m_stencilWriteMask);
6276 stream.depthStencilState.object.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
6277 stream.depthStencilState.object.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
6278 stream.depthStencilState.object.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
6279 stream.depthStencilState.object.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
6280 stream.depthStencilState.object.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
6281 stream.depthStencilState.object.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
6282 stream.depthStencilState.object.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
6283 stream.depthStencilState.object.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
6284 }
6285
6286 stream.blendState.object.IndependentBlendEnable = m_targetBlends.count() > 1;
6287 for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
6288 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
6289 D3D12_RENDER_TARGET_BLEND_DESC blend = {};
6290 blend.BlendEnable = b.enable;
6291 blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
6292 blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
6293 blend.BlendOp = toD3DBlendOp(b.opColor);
6294 blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
6295 blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
6296 blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
6297 blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
6298 stream.blendState.object.RenderTarget[i] = blend;
6299 }
6300 if (m_targetBlends.isEmpty()) {
6301 D3D12_RENDER_TARGET_BLEND_DESC blend = {};
6302 blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
6303 stream.blendState.object.RenderTarget[0] = blend;
6304 }
6305
6306 stream.rtFormats.object.NumRenderTargets = rpD->colorAttachmentCount;
6307 for (int i = 0; i < rpD->colorAttachmentCount; ++i)
6308 stream.rtFormats.object.RTFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]);
6309
6310 stream.dsFormat.object = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN;
6311
6312 stream.sampleDesc.object = sampleDesc;
6313
6314 stream.sampleMask.object = 0xFFFFFFFF;
6315
6316 viewInstanceMask = 0;
6317 const bool isMultiView = m_multiViewCount >= 2;
6318 stream.viewInstancingDesc.object.ViewInstanceCount = isMultiView ? m_multiViewCount : 0;
6319 QVarLengthArray<D3D12_VIEW_INSTANCE_LOCATION, 4> viewInstanceLocations;
6320 if (isMultiView) {
6321 for (int i = 0; i < m_multiViewCount; ++i) {
6322 viewInstanceMask |= (1 << i);
6323 viewInstanceLocations.append({ 0, UINT(i) });
6324 }
6325 stream.viewInstancingDesc.object.pViewInstanceLocations = viewInstanceLocations.constData();
6326 }
6327
6328 const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
6329
6330 ID3D12PipelineState *pso = nullptr;
6331 HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
6332 if (FAILED(hr)) {
6333 qWarning("Failed to create graphics pipeline state: %s",
6334 qPrintable(QSystemError::windowsComString(hr)));
6335 rhiD->rootSignaturePool.remove(rootSigHandle);
6336 rootSigHandle = {};
6337 return false;
6338 }
6339
6340 handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Graphics, pso);
6341
6342 rhiD->pipelineCreationEnd();
6343 generation += 1;
6344 rhiD->registerResource(this);
6345 return true;
6346}
6347
6348QD3D12ComputePipeline::QD3D12ComputePipeline(QRhiImplementation *rhi)
6349 : QRhiComputePipeline(rhi)
6350{
6351}
6352
6353QD3D12ComputePipeline::~QD3D12ComputePipeline()
6354{
6355 destroy();
6356}
6357
6358void QD3D12ComputePipeline::destroy()
6359{
6360 if (handle.isNull())
6361 return;
6362
6363 QRHI_RES_RHI(QRhiD3D12);
6364 if (rhiD) {
6365 rhiD->releaseQueue.deferredReleasePipeline(handle);
6366 rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
6367 }
6368
6369 handle = {};
6370 stageData = {};
6371
6372 if (rhiD)
6373 rhiD->unregisterResource(this);
6374}
6375
6376bool QD3D12ComputePipeline::create()
6377{
6378 if (!handle.isNull())
6379 destroy();
6380
6381 QRHI_RES_RHI(QRhiD3D12);
6382 rhiD->pipelineCreationStart();
6383
6384 stageData.valid = true;
6385 stageData.stage = CS;
6386
6387 QByteArray shaderBytecode;
6388 auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(m_shaderStage);
6389 if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
6390 shaderBytecode = cacheIt->bytecode;
6391 stageData.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
6392 } else {
6393 QString error;
6394 QShaderKey shaderKey;
6395 int compileFlags = 0;
6396 if (m_flags.testFlag(CompileShadersWithDebugInfo))
6397 compileFlags |= int(HlslCompileFlag::WithDebugInfo);
6398 const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(),
6399 m_shaderStage.shaderVariant(),
6400 compileFlags,
6401 &error,
6402 &shaderKey);
6403 if (bytecode.isEmpty()) {
6404 qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
6405 return false;
6406 }
6407
6408 shaderBytecode = bytecode;
6409 stageData.nativeResourceBindingMap = m_shaderStage.shader().nativeResourceBindingMap(shaderKey);
6410 rhiD->shaderBytecodeCache.insertWithCapacityLimit(m_shaderStage, { bytecode,
6411 stageData.nativeResourceBindingMap });
6412 }
6413
6414 QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
6415 if (srbD) {
6416 rootSigHandle = srbD->createRootSignature(&stageData, 1);
6417 if (rootSigHandle.isNull()) {
6418 qWarning("Failed to create root signature");
6419 return false;
6420 }
6421 }
6422 ID3D12RootSignature *rootSig = nullptr;
6423 if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
6424 rootSig = rs->rootSig;
6425 if (!rootSig) {
6426 qWarning("Cannot create compute pipeline state without root signature");
6427 return false;
6428 }
6429
6430 struct {
6431 QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
6432 QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CS;
6433 } stream;
6434 stream.rootSig.object = rootSig;
6435 stream.CS.object.pShaderBytecode = shaderBytecode.constData();
6436 stream.CS.object.BytecodeLength = shaderBytecode.size();
6437 const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
6438 ID3D12PipelineState *pso = nullptr;
6439 HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
6440 if (FAILED(hr)) {
6441 qWarning("Failed to create compute pipeline state: %s",
6442 qPrintable(QSystemError::windowsComString(hr)));
6443 rhiD->rootSignaturePool.remove(rootSigHandle);
6444 rootSigHandle = {};
6445 return false;
6446 }
6447
6448 handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
6449
6450 rhiD->pipelineCreationEnd();
6451 generation += 1;
6452 rhiD->registerResource(this);
6453 return true;
6454}
6455
6456// This is a lot like in the Metal backend: we need to now the rtv and dsv
6457// formats to create a graphics pipeline, and that's exactly what our
6458// "renderpass descriptor" is going to hold.
6459QD3D12RenderPassDescriptor::QD3D12RenderPassDescriptor(QRhiImplementation *rhi)
6460 : QRhiRenderPassDescriptor(rhi)
6461{
6462 serializedFormatData.reserve(16);
6463}
6464
6465QD3D12RenderPassDescriptor::~QD3D12RenderPassDescriptor()
6466{
6467 destroy();
6468}
6469
6470void QD3D12RenderPassDescriptor::destroy()
6471{
6472 QRHI_RES_RHI(QRhiD3D12);
6473 if (rhiD)
6474 rhiD->unregisterResource(this);
6475}
6476
6477bool QD3D12RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
6478{
6479 if (!other)
6480 return false;
6481
6482 const QD3D12RenderPassDescriptor *o = QRHI_RES(const QD3D12RenderPassDescriptor, other);
6483
6484 if (colorAttachmentCount != o->colorAttachmentCount)
6485 return false;
6486
6487 if (hasDepthStencil != o->hasDepthStencil)
6488 return false;
6489
6490 for (int i = 0; i < colorAttachmentCount; ++i) {
6491 if (colorFormat[i] != o->colorFormat[i])
6492 return false;
6493 }
6494
6495 if (hasDepthStencil) {
6496 if (dsFormat != o->dsFormat)
6497 return false;
6498 }
6499
6500 if (hasShadingRateMap != o->hasShadingRateMap)
6501 return false;
6502
6503 return true;
6504}
6505
6506void QD3D12RenderPassDescriptor::updateSerializedFormat()
6507{
6508 serializedFormatData.clear();
6509 auto p = std::back_inserter(serializedFormatData);
6510
6511 *p++ = colorAttachmentCount;
6512 *p++ = hasDepthStencil;
6513 for (int i = 0; i < colorAttachmentCount; ++i)
6514 *p++ = colorFormat[i];
6515 *p++ = hasDepthStencil ? dsFormat : 0;
6516}
6517
6518QRhiRenderPassDescriptor *QD3D12RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
6519{
6520 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
6521 rpD->colorAttachmentCount = colorAttachmentCount;
6522 rpD->hasDepthStencil = hasDepthStencil;
6523 memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
6524 rpD->dsFormat = dsFormat;
6525 rpD->hasShadingRateMap = hasShadingRateMap;
6526
6527 rpD->updateSerializedFormat();
6528
6529 QRHI_RES_RHI(QRhiD3D12);
6530 rhiD->registerResource(rpD);
6531 return rpD;
6532}
6533
6534QVector<quint32> QD3D12RenderPassDescriptor::serializedFormat() const
6535{
6536 return serializedFormatData;
6537}
6538
6539QD3D12CommandBuffer::QD3D12CommandBuffer(QRhiImplementation *rhi)
6540 : QRhiCommandBuffer(rhi)
6541{
6542 resetState();
6543}
6544
6545QD3D12CommandBuffer::~QD3D12CommandBuffer()
6546{
6547 destroy();
6548}
6549
6550void QD3D12CommandBuffer::destroy()
6551{
6552 // nothing to do here, the command list is not owned by us
6553}
6554
6555const QRhiNativeHandles *QD3D12CommandBuffer::nativeHandles()
6556{
6557 nativeHandlesStruct.commandList = cmdList;
6558 return &nativeHandlesStruct;
6559}
6560
6561QD3D12SwapChainRenderTarget::QD3D12SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
6562 : QRhiSwapChainRenderTarget(rhi, swapchain),
6563 d(rhi)
6564{
6565}
6566
6567QD3D12SwapChainRenderTarget::~QD3D12SwapChainRenderTarget()
6568{
6569 destroy();
6570}
6571
6572void QD3D12SwapChainRenderTarget::destroy()
6573{
6574 // nothing to do here
6575}
6576
6577QSize QD3D12SwapChainRenderTarget::pixelSize() const
6578{
6579 return d.pixelSize;
6580}
6581
6582float QD3D12SwapChainRenderTarget::devicePixelRatio() const
6583{
6584 return d.dpr;
6585}
6586
6587int QD3D12SwapChainRenderTarget::sampleCount() const
6588{
6589 return d.sampleCount;
6590}
6591
6592QD3D12SwapChain::QD3D12SwapChain(QRhiImplementation *rhi)
6593 : QRhiSwapChain(rhi),
6594 rtWrapper(rhi, this),
6595 rtWrapperRight(rhi, this),
6596 cbWrapper(rhi)
6597{
6598}
6599
6600QD3D12SwapChain::~QD3D12SwapChain()
6601{
6602 destroy();
6603}
6604
6605void QD3D12SwapChain::destroy()
6606{
6607 if (!swapChain)
6608 return;
6609
6610 releaseBuffers();
6611
6612 swapChain->Release();
6613 swapChain = nullptr;
6614 sourceSwapChain1->Release();
6615 sourceSwapChain1 = nullptr;
6616
6617 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
6618 FrameResources &fr(frameRes[i]);
6619 if (fr.fence)
6620 fr.fence->Release();
6621 if (fr.fenceEvent)
6622 CloseHandle(fr.fenceEvent);
6623 if (fr.cmdList)
6624 fr.cmdList->Release();
6625 fr = {};
6626 }
6627
6628 if (dcompVisual) {
6629 dcompVisual->Release();
6630 dcompVisual = nullptr;
6631 }
6632
6633 if (dcompTarget) {
6634 dcompTarget->Release();
6635 dcompTarget = nullptr;
6636 }
6637
6638 if (frameLatencyWaitableObject) {
6639 CloseHandle(frameLatencyWaitableObject);
6640 frameLatencyWaitableObject = nullptr;
6641 }
6642
6643 QDxgiVSyncService::instance()->unregisterWindow(window);
6644
6645 QRHI_RES_RHI(QRhiD3D12);
6646 if (rhiD) {
6647 rhiD->swapchains.remove(this);
6648 rhiD->unregisterResource(this);
6649 }
6650}
6651
6652void QD3D12SwapChain::releaseBuffers()
6653{
6654 QRHI_RES_RHI(QRhiD3D12);
6655 rhiD->waitGpu();
6656 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
6657 rhiD->resourcePool.remove(colorBuffers[i]);
6658 rhiD->rtvPool.release(rtvs[i], 1);
6659 if (stereo)
6660 rhiD->rtvPool.release(rtvsRight[i], 1);
6661 if (!msaaBuffers[i].isNull())
6662 rhiD->resourcePool.remove(msaaBuffers[i]);
6663 if (msaaRtvs[i].isValid())
6664 rhiD->rtvPool.release(msaaRtvs[i], 1);
6665 }
6666}
6667
6668void QD3D12SwapChain::waitCommandCompletionForFrameSlot(int frameSlot)
6669{
6670 FrameResources &fr(frameRes[frameSlot]);
6671 if (fr.fence->GetCompletedValue() < fr.fenceCounter) {
6672 fr.fence->SetEventOnCompletion(fr.fenceCounter, fr.fenceEvent);
6673 WaitForSingleObject(fr.fenceEvent, INFINITE);
6674 }
6675}
6676
6677void QD3D12SwapChain::addCommandCompletionSignalForCurrentFrameSlot()
6678{
6679 QRHI_RES_RHI(QRhiD3D12);
6680 FrameResources &fr(frameRes[currentFrameSlot]);
6681 fr.fenceCounter += 1u;
6682 rhiD->cmdQueue->Signal(fr.fence, fr.fenceCounter);
6683}
6684
6685QRhiCommandBuffer *QD3D12SwapChain::currentFrameCommandBuffer()
6686{
6687 return &cbWrapper;
6688}
6689
6690QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget()
6691{
6692 return &rtWrapper;
6693}
6694
6695QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
6696{
6697 return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
6698}
6699
6700QSize QD3D12SwapChain::surfacePixelSize()
6701{
6702 Q_ASSERT(m_window);
6703 return m_window->size() * m_window->devicePixelRatio();
6704}
6705
6706bool QD3D12SwapChain::isFormatSupported(Format f)
6707{
6708 if (f == SDR)
6709 return true;
6710
6711 if (!m_window) {
6712 qWarning("Attempted to call isFormatSupported() without a window set");
6713 return false;
6714 }
6715
6716 QRHI_RES_RHI(QRhiD3D12);
6717 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window))
6718 return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
6719
6720 return false;
6721}
6722
6723QRhiSwapChainHdrInfo QD3D12SwapChain::hdrInfo()
6724{
6725 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
6726 // Must use m_window, not window, given this may be called before createOrResize().
6727 if (m_window) {
6728 QRHI_RES_RHI(QRhiD3D12);
6729 info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window);
6730 }
6731 return info;
6732}
6733
6734QRhiRenderPassDescriptor *QD3D12SwapChain::newCompatibleRenderPassDescriptor()
6735{
6736 // not yet built so cannot rely on data computed in createOrResize()
6737 chooseFormats();
6738
6739 QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
6740 rpD->colorAttachmentCount = 1;
6741 rpD->hasDepthStencil = m_depthStencil != nullptr;
6742 rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
6743 rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
6744
6745 rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
6746
6747 rpD->updateSerializedFormat();
6748
6749 QRHI_RES_RHI(QRhiD3D12);
6750 rhiD->registerResource(rpD);
6751 return rpD;
6752}
6753
6754bool QRhiD3D12::ensureDirectCompositionDevice()
6755{
6756 if (dcompDevice)
6757 return true;
6758
6759 qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
6760 dcompDevice = QRhiD3D::createDirectCompositionDevice();
6761 return dcompDevice ? true : false;
6762}
6763
6764static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
6765static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
6766
6767void QD3D12SwapChain::chooseFormats()
6768{
6769 colorFormat = DEFAULT_FORMAT;
6770 srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
6771 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
6772 QRHI_RES_RHI(QRhiD3D12);
6773 if (m_format != SDR) {
6774 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) {
6775 // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
6776 switch (m_format) {
6777 case HDRExtendedSrgbLinear:
6778 colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
6779 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
6780 srgbAdjustedColorFormat = colorFormat;
6781 break;
6782 case HDR10:
6783 colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
6784 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
6785 srgbAdjustedColorFormat = colorFormat;
6786 break;
6787 default:
6788 break;
6789 }
6790 } else {
6791 // This happens also when Use HDR is set to Off in the Windows
6792 // Display settings. Show a helpful warning, but continue with the
6793 // default non-HDR format.
6794 qWarning("The output associated with the window is not HDR capable "
6795 "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
6796 }
6797 }
6798 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, colorFormat);
6799}
6800
6801bool QD3D12SwapChain::createOrResize()
6802{
6803 // Can be called multiple times due to window resizes - that is not the
6804 // same as a simple destroy+create (as with other resources). Just need to
6805 // resize the buffers then.
6806
6807 const bool needsRegistration = !window || window != m_window;
6808
6809 // except if the window actually changes
6810 if (window && window != m_window)
6811 destroy();
6812
6813 window = m_window;
6814 m_currentPixelSize = surfacePixelSize();
6815 pixelSize = m_currentPixelSize;
6816
6817 if (pixelSize.isEmpty())
6818 return false;
6819
6820 HWND hwnd = reinterpret_cast<HWND>(window->winId());
6821 HRESULT hr;
6822 QRHI_RES_RHI(QRhiD3D12);
6823 stereo = m_window->format().stereo() && rhiD->dxgiFactory->IsWindowedStereoEnabled();
6824
6825 if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6826 if (rhiD->ensureDirectCompositionDevice()) {
6827 if (!dcompTarget) {
6828 hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
6829 if (FAILED(hr)) {
6830 qWarning("Failed to create Direct Composition target for the window: %s",
6831 qPrintable(QSystemError::windowsComString(hr)));
6832 }
6833 }
6834 if (dcompTarget && !dcompVisual) {
6835 hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
6836 if (FAILED(hr)) {
6837 qWarning("Failed to create DirectComposition visual: %s",
6838 qPrintable(QSystemError::windowsComString(hr)));
6839 }
6840 }
6841 }
6842 // simple consistency check
6843 if (window->requestedFormat().alphaBufferSize() <= 0)
6844 qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
6845 "This may lead to problems.");
6846 }
6847
6848 swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
6849 swapChainFlags = 0;
6850 if (swapInterval == 0 && rhiD->supportsAllowTearing)
6851 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
6852
6853 // maxFrameLatency 0 means no waitable object usage.
6854 // Ignore it also when NoVSync is on, and when using WARP.
6855 const bool useFrameLatencyWaitableObject = rhiD->maxFrameLatency != 0
6856 && swapInterval != 0
6857 && rhiD->driverInfoStruct.deviceType != QRhiDriverInfo::CpuDevice;
6858 if (useFrameLatencyWaitableObject)
6859 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
6860
6861 if (!swapChain) {
6862 chooseFormats();
6863
6864 DXGI_SWAP_CHAIN_DESC1 desc = {};
6865 desc.Width = UINT(pixelSize.width());
6866 desc.Height = UINT(pixelSize.height());
6867 desc.Format = colorFormat;
6868 desc.SampleDesc.Count = 1;
6869 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6870 desc.BufferCount = BUFFER_COUNT;
6871 desc.Flags = swapChainFlags;
6872 desc.Scaling = DXGI_SCALING_NONE;
6873 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
6874 desc.Stereo = stereo;
6875
6876 if (dcompVisual) {
6877 // With DirectComposition setting AlphaMode to STRAIGHT fails the
6878 // swapchain creation, whereas the result seems to be identical
6879 // with any of the other values, including IGNORE. (?)
6880 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
6881
6882 // DirectComposition has its own limitations, cannot use
6883 // SCALING_NONE. So with semi-transparency requested we are forced
6884 // to SCALING_STRETCH.
6885 desc.Scaling = DXGI_SCALING_STRETCH;
6886 }
6887
6888 if (dcompVisual)
6889 hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
6890 else
6891 hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
6892
6893 // If failed and we tried a HDR format, then try with SDR. This
6894 // matches other backends, such as Vulkan where if the format is
6895 // not supported, the default one is used instead.
6896 if (FAILED(hr) && m_format != SDR) {
6897 colorFormat = DEFAULT_FORMAT;
6898 desc.Format = DEFAULT_FORMAT;
6899 if (dcompVisual)
6900 hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
6901 else
6902 hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
6903 }
6904
6905 if (SUCCEEDED(hr)) {
6906 if (FAILED(sourceSwapChain1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&swapChain)))) {
6907 qWarning("IDXGISwapChain3 not available");
6908 return false;
6909 }
6910 if (m_format != SDR) {
6911 hr = swapChain->SetColorSpace1(hdrColorSpace);
6912 if (FAILED(hr)) {
6913 qWarning("Failed to set color space on swapchain: %s",
6914 qPrintable(QSystemError::windowsComString(hr)));
6915 }
6916 }
6917 if (useFrameLatencyWaitableObject) {
6918 swapChain->SetMaximumFrameLatency(rhiD->maxFrameLatency);
6919 frameLatencyWaitableObject = swapChain->GetFrameLatencyWaitableObject();
6920 }
6921 if (dcompVisual) {
6922 hr = dcompVisual->SetContent(swapChain);
6923 if (SUCCEEDED(hr)) {
6924 hr = dcompTarget->SetRoot(dcompVisual);
6925 if (FAILED(hr)) {
6926 qWarning("Failed to associate Direct Composition visual with the target: %s",
6927 qPrintable(QSystemError::windowsComString(hr)));
6928 }
6929 } else {
6930 qWarning("Failed to set content for Direct Composition visual: %s",
6931 qPrintable(QSystemError::windowsComString(hr)));
6932 }
6933 } else {
6934 // disable Alt+Enter; not relevant when using DirectComposition
6935 rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
6936 }
6937 }
6938 if (FAILED(hr)) {
6939 qWarning("Failed to create D3D12 swapchain: %s"
6940 " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
6941 qPrintable(QSystemError::windowsComString(hr)),
6942 desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
6943 desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
6944 return false;
6945 }
6946
6947 for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
6948 hr = rhiD->dev->CreateFence(0,
6949 D3D12_FENCE_FLAG_NONE,
6950 __uuidof(ID3D12Fence),
6951 reinterpret_cast<void **>(&frameRes[i].fence));
6952 if (FAILED(hr)) {
6953 qWarning("Failed to create fence for swapchain: %s",
6954 qPrintable(QSystemError::windowsComString(hr)));
6955 return false;
6956 }
6957 frameRes[i].fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
6958
6959 frameRes[i].fenceCounter = 0;
6960 }
6961 } else {
6962 releaseBuffers();
6963 hr = swapChain->ResizeBuffers(BUFFER_COUNT,
6964 UINT(pixelSize.width()),
6965 UINT(pixelSize.height()),
6966 colorFormat,
6967 swapChainFlags);
6968 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
6969 qWarning("Device loss detected in ResizeBuffers()");
6970 rhiD->deviceLost = true;
6971 return false;
6972 } else if (FAILED(hr)) {
6973 qWarning("Failed to resize D3D12 swapchain: %s", qPrintable(QSystemError::windowsComString(hr)));
6974 return false;
6975 }
6976 }
6977
6978 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
6979 ID3D12Resource *colorBuffer;
6980 hr = swapChain->GetBuffer(i, __uuidof(ID3D12Resource), reinterpret_cast<void **>(&colorBuffer));
6981 if (FAILED(hr)) {
6982 qWarning("Failed to get buffer %u for D3D12 swapchain: %s",
6983 i, qPrintable(QSystemError::windowsComString(hr)));
6984 return false;
6985 }
6986 colorBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, colorBuffer, D3D12_RESOURCE_STATE_PRESENT);
6987 rtvs[i] = rhiD->rtvPool.allocate(1);
6988 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
6989 rtvDesc.Format = srgbAdjustedColorFormat;
6990 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
6991 rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvs[i].cpuHandle);
6992
6993 if (stereo) {
6994 rtvsRight[i] = rhiD->rtvPool.allocate(1);
6995 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
6996 rtvDesc.Format = srgbAdjustedColorFormat;
6997 rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
6998 rtvDesc.Texture2DArray.ArraySize = 1;
6999 rtvDesc.Texture2DArray.FirstArraySlice = 1;
7000 rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvsRight[i].cpuHandle);
7001 }
7002 }
7003
7004 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
7005 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
7006 m_depthStencil->sampleCount(), m_sampleCount);
7007 }
7008 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
7009 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
7010 m_depthStencil->setPixelSize(pixelSize);
7011 if (!m_depthStencil->create())
7012 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
7013 pixelSize.width(), pixelSize.height());
7014 } else {
7015 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
7016 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
7017 pixelSize.width(), pixelSize.height());
7018 }
7019 }
7020
7021 ds = m_depthStencil ? QRHI_RES(QD3D12RenderBuffer, m_depthStencil) : nullptr;
7022
7023 if (sampleDesc.Count > 1) {
7024 for (UINT i = 0; i < BUFFER_COUNT; ++i) {
7025 D3D12_RESOURCE_DESC resourceDesc = {};
7026 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
7027 resourceDesc.Width = UINT64(pixelSize.width());
7028 resourceDesc.Height = UINT(pixelSize.height());
7029 resourceDesc.DepthOrArraySize = 1;
7030 resourceDesc.MipLevels = 1;
7031 resourceDesc.Format = srgbAdjustedColorFormat;
7032 resourceDesc.SampleDesc = sampleDesc;
7033 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
7034 resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
7035 D3D12_CLEAR_VALUE clearValue = {};
7036 clearValue.Format = colorFormat;
7037 ID3D12Resource *resource = nullptr;
7038 D3D12MA::Allocation *allocation = nullptr;
7039 HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
7040 &resourceDesc,
7041 D3D12_RESOURCE_STATE_RENDER_TARGET,
7042 &clearValue,
7043 &allocation,
7044 __uuidof(ID3D12Resource),
7045 reinterpret_cast<void **>(&resource));
7046 if (FAILED(hr)) {
7047 qWarning("Failed to create MSAA color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
7048 return false;
7049 }
7050 msaaBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
7051 msaaRtvs[i] = rhiD->rtvPool.allocate(1);
7052 if (!msaaRtvs[i].isValid())
7053 return false;
7054 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
7055 rtvDesc.Format = srgbAdjustedColorFormat;
7056 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
7057 : D3D12_RTV_DIMENSION_TEXTURE2D;
7058 rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, msaaRtvs[i].cpuHandle);
7059 }
7060 }
7061
7062 currentBackBufferIndex = swapChain->GetCurrentBackBufferIndex();
7063 currentFrameSlot = 0;
7064 lastFrameLatencyWaitSlot = -1; // wait already in the first frame, as instructed in the dxgi docs
7065
7066 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
7067 QD3D12SwapChainRenderTarget *rtD = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapper);
7068 rtD->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
7069 rtD->d.pixelSize = pixelSize;
7070 rtD->d.dpr = float(window->devicePixelRatio());
7071 rtD->d.sampleCount = int(sampleDesc.Count);
7072 rtD->d.colorAttCount = 1;
7073 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
7074
7075 rtWrapperRight.setRenderPassDescriptor(m_renderPassDesc);
7076 QD3D12SwapChainRenderTarget *rtDr = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapperRight);
7077 rtDr->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
7078 rtDr->d.pixelSize = pixelSize;
7079 rtDr->d.dpr = float(window->devicePixelRatio());
7080 rtDr->d.sampleCount = int(sampleDesc.Count);
7081 rtDr->d.colorAttCount = 1;
7082 rtDr->d.dsAttCount = m_depthStencil ? 1 : 0;
7083
7084 QDxgiVSyncService::instance()->registerWindow(window);
7085
7086 if (needsRegistration || !rhiD->swapchains.contains(this))
7087 rhiD->swapchains.insert(this);
7088
7089 rhiD->registerResource(this);
7090
7091 return true;
7092}
7093
7094QT_END_NAMESPACE
7095
7096#endif // __ID3D12Device2_INTERFACE_DEFINED__
#define __has_include(x)