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