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