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