Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrhid3d12_p.h
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// Qt-Security score:significant reason:default
4
5#ifndef QRHID3D12_P_H
6#define QRHID3D12_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include "qrhi_p.h"
20#include <QWindow>
21#include <QBitArray>
22
23#include <optional>
24#include <array>
25
26#include <d3d12.h>
27#include <d3d12sdklayers.h>
28#include <dxgi1_6.h>
29#include <dcomp.h>
30
31#include "D3D12MemAlloc.h"
32
33// ID3D12Device2 and ID3D12GraphicsCommandList1 and types and enums introduced
34// with those are hard requirements now. These should be declared in any
35// moderately recent d3d12.h, but if it is an SDK from before Windows 10
36// version 1703 then these types could be missing. In the absence of other
37// options, handle this by skipping all the code and making QRhi::create() fail
38// in such builds.
39#ifdef __ID3D12Device2_INTERFACE_DEFINED__
40#define QRHI_D3D12_AVAILABLE
41
42// Will use ID3D12GraphicsCommandList5 as long as the d3d12.h is new enough.
43// Otherwise, some features (VRS) will not be available.
44#ifdef __ID3D12GraphicsCommandList5_INTERFACE_DEFINED__
45#define QRHI_D3D12_CL5_AVAILABLE
46using D3D12GraphicsCommandList = ID3D12GraphicsCommandList5;
47#else
48using D3D12GraphicsCommandList = ID3D12GraphicsCommandList1;
49#endif
50
51#ifdef __ID3D12InfoQueue1_INTERFACE_DEFINED__
52#define QRHI_D3D12_INFOQUEUE1_AVAILABLE
53#endif
54
55#ifdef __ID3D12PipelineLibrary1_INTERFACE_DEFINED__
56#define QRHI_D3D12_PIPELINE_LIBRARY_AVAILABLE
57#endif
58
59QT_BEGIN_NAMESPACE
60
61static const int QD3D12_FRAMES_IN_FLIGHT = 2;
62
63class QRhiD3D12;
64
65struct QD3D12Descriptor
66{
67 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {};
68 D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {};
69
70 bool isValid() const { return cpuHandle.ptr != 0; }
71};
72
73struct QD3D12ReleaseQueue;
74
75struct QD3D12DescriptorHeap
76{
77 bool isValid() const { return heap && capacity; }
78 bool create(ID3D12Device *device,
79 quint32 descriptorCount,
80 D3D12_DESCRIPTOR_HEAP_TYPE heapType,
81 D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags);
82 void createWithExisting(const QD3D12DescriptorHeap &other,
83 quint32 offsetInDescriptors,
84 quint32 descriptorCount);
85 void destroy();
86 void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
87
88 QD3D12Descriptor get(quint32 count);
89 QD3D12Descriptor at(quint32 index) const;
90 quint32 remainingCapacity() const { return capacity - head; }
91
92 QD3D12Descriptor incremented(const QD3D12Descriptor &descriptor, quint32 offsetInDescriptors) const
93 {
94 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = descriptor.cpuHandle;
95 cpuHandle.ptr += offsetInDescriptors * descriptorByteSize;
96 D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = descriptor.gpuHandle;
97 if (gpuHandle.ptr)
98 gpuHandle.ptr += offsetInDescriptors * descriptorByteSize;
99 return { cpuHandle, gpuHandle };
100 }
101
102 ID3D12DescriptorHeap *heap = nullptr;
103 quint32 capacity = 0;
104 QD3D12Descriptor heapStart;
105 quint32 head = 0;
106 quint32 descriptorByteSize = 0;
107 D3D12_DESCRIPTOR_HEAP_TYPE heapType;
108 D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags;
109};
110
111struct QD3D12CpuDescriptorPool
112{
113 bool isValid() const { return !heaps.isEmpty(); }
114 bool create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE heapType, const char *debugName = "");
115 void destroy();
116
117 QD3D12Descriptor allocate(quint32 count);
118 void release(const QD3D12Descriptor &descriptor, quint32 count);
119
120 static const int DESCRIPTORS_PER_HEAP = 256;
121
122 struct HeapWithMap {
123 QD3D12DescriptorHeap heap;
124 QBitArray map;
125 static HeapWithMap init(const QD3D12DescriptorHeap &heap, quint32 descriptorCount) {
126 HeapWithMap result;
127 result.heap = heap;
128 result.map.resize(descriptorCount);
129 return result;
130 }
131 };
132
133 ID3D12Device *device;
134 quint32 descriptorByteSize;
135 QVector<HeapWithMap> heaps;
136 const char *debugName;
137};
138
139struct QD3D12QueryHeap
140{
141 bool isValid() const { return heap && capacity; }
142 bool create(ID3D12Device *device,
143 quint32 queryCount,
144 D3D12_QUERY_HEAP_TYPE heapType);
145 void destroy();
146
147 ID3D12QueryHeap *heap = nullptr;
148 quint32 capacity = 0;
149};
150
151struct QD3D12StagingArea
152{
153 static const quint32 ALIGNMENT = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; // 512 so good enough both for cb and texdata
154
155 struct Allocation {
156 quint8 *p = nullptr;
157 D3D12_GPU_VIRTUAL_ADDRESS gpuAddr = 0;
158 ID3D12Resource *buffer = nullptr;
159 quint32 bufferOffset = 0;
160 bool isValid() const { return p != nullptr; }
161 };
162
163 bool isValid() const { return allocation && mem.isValid(); }
164 bool create(QRhiD3D12 *rhi, quint32 capacity, D3D12_HEAP_TYPE heapType);
165 void destroy();
166 void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
167
168 Allocation get(quint32 byteSize);
169
170 quint32 remainingCapacity() const
171 {
172 return capacity - head;
173 }
174
175 static quint32 allocSizeForArray(quint32 size, int count = 1)
176 {
177 return count * ((size + ALIGNMENT - 1) & ~(ALIGNMENT - 1));
178 }
179
180 Allocation mem;
181 ID3D12Resource *resource = nullptr;
182 D3D12MA::Allocation *allocation = nullptr;
183 quint32 head;
184 quint32 capacity;
185};
186
187struct QD3D12ObjectHandle
188{
189 quint32 index = 0;
190 quint32 generation = 0;
191
192 // the default, null handle is guaranteed to give ObjectPool::isValid() == false
193 bool isNull() const { return index == 0 && generation == 0; }
194};
195
196inline bool operator==(const QD3D12ObjectHandle &a, const QD3D12ObjectHandle &b) noexcept
197{
198 return a.index == b.index && a.generation == b.generation;
199}
200
201inline bool operator!=(const QD3D12ObjectHandle &a, const QD3D12ObjectHandle &b) noexcept
202{
203 return !(a == b);
204}
205
206template<typename T>
207struct QD3D12ObjectPool
208{
209 void create(const char *debugName = "")
210 {
211 this->debugName = debugName;
212 Q_ASSERT(data.isEmpty());
213 data.append(Data()); // index 0 is always invalid
214 }
215
216 void destroy() {
217 int leakCount = 0; // will nicely destroy everything here, but warn about it if enabled
218 for (Data &d : data) {
219 if (d.object.has_value()) {
220 leakCount += 1;
221 d.object->releaseResources();
222 }
223 }
224 data.clear();
225#ifndef QT_NO_DEBUG
226 // debug builds: just do it always
227 static bool leakCheck = true;
228#else
229 // release builds: opt-in
230 static bool leakCheck = qEnvironmentVariableIntValue("QT_RHI_LEAK_CHECK");
231#endif
232 if (leakCheck) {
233 if (leakCount > 0) {
234 qWarning("QD3D12ObjectPool::destroy(): Pool %p '%s' had %d unreleased objects",
235 this, debugName, leakCount);
236 }
237 }
238 }
239
240 bool isValid(const QD3D12ObjectHandle &handle) const
241 {
242 return handle.index > 0
243 && handle.index < quint32(data.count())
244 && handle.generation > 0
245 && handle.generation == data[handle.index].generation
246 && data[handle.index].object.has_value();
247 }
248
249 T lookup(const QD3D12ObjectHandle &handle) const
250 {
251 return isValid(handle) ? *data[handle.index].object : T();
252 }
253
254 const T *lookupRef(const QD3D12ObjectHandle &handle) const
255 {
256 return isValid(handle) ? &*data[handle.index].object : nullptr;
257 }
258
259 T *lookupRef(const QD3D12ObjectHandle &handle)
260 {
261 return isValid(handle) ? &*data[handle.index].object : nullptr;
262 }
263
264 QD3D12ObjectHandle add(const T &object)
265 {
266 Q_ASSERT(!data.isEmpty());
267 const quint32 count = quint32(data.count());
268 quint32 index = 1; // index 0 is always invalid
269 for (; index < count; ++index) {
270 if (!data[index].object.has_value())
271 break;
272 }
273 if (index < count) {
274 data[index].object = object;
275 quint32 &generation = data[index].generation;
276 generation += 1u;
277 return { index, generation };
278 } else {
279 data.append({ object, 1 });
280 return { count, 1 };
281 }
282 }
283
284 void remove(const QD3D12ObjectHandle &handle)
285 {
286 if (T *object = lookupRef(handle)) {
287 object->releaseResources();
288 data[handle.index].object.reset();
289 }
290 }
291
292 const char *debugName;
293 struct Data {
294 std::optional<T> object;
295 quint32 generation = 0;
296 };
297 QVector<Data> data;
298};
299
300struct QD3D12Resource
301{
302 ID3D12Resource *resource;
303 D3D12_RESOURCE_STATES state;
304 D3D12_RESOURCE_DESC desc;
305 D3D12MA::Allocation *allocation;
306 void *cpuMapPtr;
307 enum { UavUsageRead = 0x01, UavUsageWrite = 0x02 };
308 int uavUsage;
309 bool owns;
310
311 // note that this assumes the allocation (if there is one) and the resource
312 // are separately releaseable, see D3D12MemAlloc docs
313 static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12Resource> *pool,
314 ID3D12Resource *resource,
315 D3D12_RESOURCE_STATES state,
316 D3D12MA::Allocation *allocation = nullptr,
317 void *cpuMapPtr = nullptr)
318 {
319 Q_ASSERT(resource);
320 return pool->add({ resource, state, resource->GetDesc(), allocation, cpuMapPtr, 0, true });
321 }
322
323 // for QRhiTexture::createFrom() where the ID3D12Resource is not owned by us
324 static QD3D12ObjectHandle addNonOwningToPool(QD3D12ObjectPool<QD3D12Resource> *pool,
325 ID3D12Resource *resource,
326 D3D12_RESOURCE_STATES state)
327 {
328 Q_ASSERT(resource);
329 return pool->add({ resource, state, resource->GetDesc(), nullptr, nullptr, 0, false });
330 }
331
332 void releaseResources()
333 {
334 if (owns) {
335 // order matters: resource first, then the allocation
336 resource->Release();
337 if (allocation)
338 allocation->Release();
339 }
340 }
341};
342
343struct QD3D12Pipeline
344{
345 enum Type {
346 Graphics,
347 Compute
348 };
349 Type type;
350 ID3D12PipelineState *pso;
351
352 static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12Pipeline> *pool,
353 Type type,
354 ID3D12PipelineState *pso)
355 {
356 return pool->add({ type, pso });
357 }
358
359 void releaseResources()
360 {
361 pso->Release();
362 }
363};
364
365struct QD3D12RootSignature
366{
367 ID3D12RootSignature *rootSig;
368
369 static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12RootSignature> *pool,
370 ID3D12RootSignature *rootSig)
371 {
372 return pool->add({ rootSig });
373 }
374
375 void releaseResources()
376 {
377 rootSig->Release();
378 }
379};
380
381struct QD3D12ReleaseQueue
382{
383 void create(QD3D12ObjectPool<QD3D12Resource> *resourcePool,
384 QD3D12ObjectPool<QD3D12Pipeline> *pipelinePool,
385 QD3D12ObjectPool<QD3D12RootSignature> *rootSignaturePool)
386 {
387 this->resourcePool = resourcePool;
388 this->pipelinePool = pipelinePool;
389 this->rootSignaturePool = rootSignaturePool;
390 }
391
392 void deferredReleaseResource(const QD3D12ObjectHandle &handle);
393 void deferredReleaseResourceWithViews(const QD3D12ObjectHandle &handle,
394 QD3D12CpuDescriptorPool *pool,
395 const QD3D12Descriptor &viewsStart,
396 int viewCount);
397 void deferredReleasePipeline(const QD3D12ObjectHandle &handle);
398 void deferredReleaseRootSignature(const QD3D12ObjectHandle &handle);
399 void deferredReleaseCallback(std::function<void(void*)> callback, void *userData);
400 void deferredReleaseResourceAndAllocation(ID3D12Resource *resource,
401 D3D12MA::Allocation *allocation);
402 void deferredReleaseDescriptorHeap(ID3D12DescriptorHeap *heap);
403 void deferredReleaseViews(QD3D12CpuDescriptorPool *pool,
404 const QD3D12Descriptor &viewsStart,
405 int viewCount);
406
407 void activatePendingDeferredReleaseRequests(int frameSlot);
408 void executeDeferredReleases(int frameSlot, bool forced = false);
409 void releaseAll();
410
411 struct DeferredReleaseEntry {
412 enum Type {
413 Resource,
414 Pipeline,
415 RootSignature,
416 Callback,
417 ResourceAndAllocation,
418 DescriptorHeap,
419 Views
420 };
421 Type type = Resource;
422 std::optional<int> frameSlotToBeReleasedIn;
423 QD3D12ObjectHandle handle;
424 QD3D12CpuDescriptorPool *poolForViews = nullptr;
425 QD3D12Descriptor viewsStart;
426 int viewCount = 0;
427 std::function<void(void*)> callback = nullptr;
428 void *callbackUserData = nullptr;
429 std::pair<ID3D12Resource *, D3D12MA::Allocation *> resourceAndAllocation = {};
430 ID3D12DescriptorHeap *descriptorHeap = nullptr;
431 };
432 QVector<DeferredReleaseEntry> queue;
433 QD3D12ObjectPool<QD3D12Resource> *resourcePool = nullptr;
434 QD3D12ObjectPool<QD3D12Pipeline> *pipelinePool = nullptr;
435 QD3D12ObjectPool<QD3D12RootSignature> *rootSignaturePool = nullptr;
436};
437
438struct QD3D12CommandBuffer;
439
440struct QD3D12ResourceBarrierGenerator
441{
442 static const int PREALLOC = 16;
443
444 void create(QD3D12ObjectPool<QD3D12Resource> *resourcePool)
445 {
446 this->resourcePool = resourcePool;
447 }
448
449 void addTransitionBarrier(const QD3D12ObjectHandle &resourceHandle, D3D12_RESOURCE_STATES stateAfter);
450 void enqueueBufferedTransitionBarriers(QD3D12CommandBuffer *cbD);
451 void enqueueSubresourceTransitionBarrier(QD3D12CommandBuffer *cbD,
452 const QD3D12ObjectHandle &resourceHandle,
453 UINT subresource,
454 D3D12_RESOURCE_STATES stateBefore,
455 D3D12_RESOURCE_STATES stateAfter);
456 void enqueueUavBarrier(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &resourceHandle);
457
458 struct TransitionResourceBarrier {
459 QD3D12ObjectHandle resourceHandle;
460 D3D12_RESOURCE_STATES stateBefore;
461 D3D12_RESOURCE_STATES stateAfter;
462 };
463 QVarLengthArray<TransitionResourceBarrier, PREALLOC> transitionResourceBarriers;
464 QD3D12ObjectPool<QD3D12Resource> *resourcePool = nullptr;
465};
466
467struct QD3D12ShaderBytecode
468{
469 QD3D12ShaderBytecode() = default;
470 QD3D12ShaderBytecode(const QByteArray &bytecode, const QShader::NativeResourceBindingMap &rbm,
471 int pushConstantRegister = -1, quint32 pushConstantSize = 0)
472 : bytecode(bytecode), nativeResourceBindingMap(rbm),
473 pushConstantRegister(pushConstantRegister), pushConstantSize(pushConstantSize)
474 { }
475 QByteArray bytecode;
476 QShader::NativeResourceBindingMap nativeResourceBindingMap;
477 int pushConstantRegister = -1;
478 quint32 pushConstantSize = 0;
479};
480
481inline quint32 qrhiBinaryCacheEntryByteSize(const QD3D12ShaderBytecode &s)
482{
483 return quint32(s.bytecode.size());
484}
485
486using QD3D12ShaderBytecodeCache = QRhiBinaryCache<QRhiShaderStage, QD3D12ShaderBytecode>;
487
488struct QD3D12ShaderVisibleDescriptorHeap
489{
490 bool create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE type, quint32 perFrameDescriptorCount);
491 void destroy();
492 void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
493
494 QD3D12DescriptorHeap heap;
495 QD3D12DescriptorHeap perFrameHeapSlice[QD3D12_FRAMES_IN_FLIGHT];
496};
497
498// wrap foreign struct so we can legally supply equality operators and qHash:
499struct Q_D3D12_SAMPLER_DESC
500{
501 D3D12_SAMPLER_DESC desc;
502
503 friend bool operator==(const Q_D3D12_SAMPLER_DESC &lhs, const Q_D3D12_SAMPLER_DESC &rhs) noexcept
504 {
505 return lhs.desc.Filter == rhs.desc.Filter
506 && lhs.desc.AddressU == rhs.desc.AddressU
507 && lhs.desc.AddressV == rhs.desc.AddressV
508 && lhs.desc.AddressW == rhs.desc.AddressW
509 && lhs.desc.MipLODBias == rhs.desc.MipLODBias
510 && lhs.desc.MaxAnisotropy == rhs.desc.MaxAnisotropy
511 && lhs.desc.ComparisonFunc == rhs.desc.ComparisonFunc
512 // BorderColor is never used, skip it
513 && lhs.desc.MinLOD == rhs.desc.MinLOD
514 && lhs.desc.MaxLOD == rhs.desc.MaxLOD;
515 }
516
517 friend bool operator!=(const Q_D3D12_SAMPLER_DESC &lhs, const Q_D3D12_SAMPLER_DESC &rhs) noexcept
518 {
519 return !(lhs == rhs);
520 }
521
522 friend size_t qHash(const Q_D3D12_SAMPLER_DESC &key, size_t seed = 0) noexcept
523 {
524 QtPrivate::QHashCombine hash(seed);
525 seed = hash(seed, key.desc.Filter);
526 seed = hash(seed, key.desc.AddressU);
527 seed = hash(seed, key.desc.AddressV);
528 seed = hash(seed, key.desc.AddressW);
529 seed = hash(seed, key.desc.MipLODBias);
530 seed = hash(seed, key.desc.MaxAnisotropy);
531 seed = hash(seed, key.desc.ComparisonFunc);
532 // BorderColor is never used, skip it
533 seed = hash(seed, key.desc.MinLOD);
534 seed = hash(seed, key.desc.MaxLOD);
535 return seed;
536 }
537};
538
539// A list of sampler descriptions, used as the key for looking up the
540// contiguous descriptor run that backs an array of samplers.
541struct Q_D3D12_SAMPLER_DESC_LIST
542{
543 QVarLengthArray<Q_D3D12_SAMPLER_DESC, 8> descs;
544
545 friend bool operator==(const Q_D3D12_SAMPLER_DESC_LIST &lhs, const Q_D3D12_SAMPLER_DESC_LIST &rhs) noexcept
546 {
547 return lhs.descs == rhs.descs;
548 }
549
550 friend bool operator!=(const Q_D3D12_SAMPLER_DESC_LIST &lhs, const Q_D3D12_SAMPLER_DESC_LIST &rhs) noexcept
551 {
552 return !(lhs == rhs);
553 }
554
555 friend size_t qHash(const Q_D3D12_SAMPLER_DESC_LIST &key, size_t seed = 0) noexcept
556 {
557 return qHashRange(key.descs.cbegin(), key.descs.cend(), seed);
558 }
559};
560
561struct QD3D12SamplerManager
562{
563 const quint32 MAX_SAMPLERS = 512;
564
565 bool create(ID3D12Device *device);
566 void destroy();
567
568 QD3D12Descriptor getShaderVisibleDescriptor(const D3D12_SAMPLER_DESC &desc);
569 // For an array of samplers: returns the start of a run of count
570 // consecutive descriptors, so that the array can be bound as one
571 // descriptor range.
572 QD3D12Descriptor getShaderVisibleDescriptors(const QVarLengthArray<Q_D3D12_SAMPLER_DESC, 8> &descs);
573
574 ID3D12Device *device = nullptr;
575 QD3D12ShaderVisibleDescriptorHeap shaderVisibleSamplerHeap;
576 // Nothing is ever pruned from these: the heap is allocated from linearly
577 // and a descriptor (run) is kept until the QRhi goes away. This relies on
578 // the number of distinct sampler descriptions an application uses being
579 // small, which holds in practice because the descriptions are generated
580 // from the handful of filtering and addressing modes QRhiSampler exposes.
581 // The assumption is weaker for gpuArrayMap, where the key is a list, so
582 // combinations of even a few distinct samplers can multiply, and each
583 // entry takes count descriptors out of MAX_SAMPLERS. Should that ever
584 // become a problem in practice, the runs would need reference counting and
585 // deferred release, not just a bump allocator.
586 QHash<Q_D3D12_SAMPLER_DESC, QD3D12Descriptor> gpuMap;
587 QHash<Q_D3D12_SAMPLER_DESC_LIST, QD3D12Descriptor> gpuArrayMap;
588};
589
590enum QD3D12Stage { VS = 0, HS, DS, GS, PS, CS };
591
592static inline QD3D12Stage qd3d12_stage(QRhiShaderStage::Type type)
593{
594 switch (type) {
595 case QRhiShaderStage::Vertex:
596 return VS;
597 case QRhiShaderStage::TessellationControl:
598 return HS;
599 case QRhiShaderStage::TessellationEvaluation:
600 return DS;
601 case QRhiShaderStage::Geometry:
602 return GS;
603 case QRhiShaderStage::Fragment:
604 return PS;
605 case QRhiShaderStage::Compute:
606 return CS;
607 }
608 Q_UNREACHABLE_RETURN(VS);
609}
610
611static inline D3D12_SHADER_VISIBILITY qd3d12_stageToVisibility(QD3D12Stage s)
612{
613 switch (s) {
614 case VS:
615 return D3D12_SHADER_VISIBILITY_VERTEX;
616 case HS:
617 return D3D12_SHADER_VISIBILITY_HULL;
618 case DS:
619 return D3D12_SHADER_VISIBILITY_DOMAIN;
620 case GS:
621 return D3D12_SHADER_VISIBILITY_GEOMETRY;
622 case PS:
623 return D3D12_SHADER_VISIBILITY_PIXEL;
624 case CS:
625 return D3D12_SHADER_VISIBILITY_ALL;
626 }
627 Q_UNREACHABLE_RETURN(D3D12_SHADER_VISIBILITY_ALL);
628}
629
630static inline QRhiShaderResourceBinding::StageFlag qd3d12_stageToSrb(QD3D12Stage s)
631{
632 switch (s) {
633 case VS:
634 return QRhiShaderResourceBinding::VertexStage;
635 case HS:
636 return QRhiShaderResourceBinding::TessellationControlStage;
637 case DS:
638 return QRhiShaderResourceBinding::TessellationEvaluationStage;
639 case GS:
640 return QRhiShaderResourceBinding::GeometryStage;
641 case PS:
642 return QRhiShaderResourceBinding::FragmentStage;
643 case CS:
644 return QRhiShaderResourceBinding::ComputeStage;
645 }
646 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::VertexStage);
647}
648
649struct QD3D12ShaderStageData
650{
651 bool valid = false; // to allow simple arrays where unused stages are indicated by !valid
652 QD3D12Stage stage = VS;
653 QShader::NativeResourceBindingMap nativeResourceBindingMap;
654 int pushConstantRegister = -1;
655 quint32 pushConstantSize = 0;
656};
657
658struct QD3D12ShaderResourceBindings;
659
660struct QD3D12ShaderResourceVisitor
661{
662 enum StorageOp { Load = 0, Store, LoadStore };
663
664 QD3D12ShaderResourceVisitor(const QD3D12ShaderResourceBindings *srb,
665 const QD3D12ShaderStageData *stageData,
666 int stageCount)
667 : srb(srb),
668 stageData(stageData),
669 stageCount(stageCount)
670 {
671 }
672
673 std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::UniformBufferData &, int, int)> uniformBuffer = nullptr;
674 // Textures and samplers are visited per binding, not per array element: a
675 // shader declares an array of textures or samplers as a single descriptor
676 // range, and D3D12 will not accept a pipeline where such a range is only
677 // partially bound (a series of one-descriptor ranges does not qualify), so
678 // the whole array has to be seen at once. (arguments: stage, elements,
679 // element count, base shader register)
680 std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::TextureAndSampler *, int, int)> textures = nullptr;
681 std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::TextureAndSampler *, int, int)> samplers = nullptr;
682 std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::StorageImageData &, StorageOp, int)> storageImage = nullptr;
683 std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::StorageBufferData &, StorageOp, int)> storageBuffer = nullptr;
684
685 void visit();
686
687 const QD3D12ShaderResourceBindings *srb;
688 const QD3D12ShaderStageData *stageData;
689 int stageCount;
690};
691
692struct QD3D12Readback
693{
694 // common
695 int frameSlot = -1;
696 QRhiReadbackResult *result = nullptr;
697 QD3D12StagingArea staging;
698 quint32 byteSize = 0;
699 // textures
700 quint32 bytesPerLine = 0;
701 QSize pixelSize;
702 QRhiTexture::Format format = QRhiTexture::UnknownFormat;
703 quint32 stagingRowPitch = 0;
704 // Only used when format is D24S8 or D32FS8: the stencil plane is copied
705 // into the same staging buffer as the depth plane (see
706 // enqueueResourceUpdates()), at this byte offset and with its own row
707 // pitch, since D3D12 exposes combined depth-stencil resources as two
708 // separately addressable planes.
709 quint32 stencilStagingOffset = 0;
710 quint32 stencilStagingRowPitch = 0;
711 // The staging buffer can hold more than the result, in which case the
712 // result starts at this position in it.
713 QPoint cropOrigin;
714 quint32 bytesPerPixel = 0;
715};
716
717struct QD3D12MipmapGenerator
718{
719 void create(QRhiD3D12 *rhiD);
720 void destroy();
721 void generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle);
722
723 bool ensureCreated();
724 bool buildPipeline();
725
726 QRhiD3D12 *rhiD = nullptr;
727 QD3D12ObjectHandle rootSigHandle;
728 QD3D12ObjectHandle pipelineHandle;
729 bool createFailed = false;
730};
731
732struct QD3D12MipmapGenerator3D
733{
734 void create(QRhiD3D12 *rhiD);
735 void destroy();
736 void generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle);
737
738 bool ensureCreated();
739 bool buildPipeline();
740
741 QRhiD3D12 *rhiD = nullptr;
742 QD3D12ObjectHandle rootSigHandle;
743 QD3D12ObjectHandle pipelineHandle;
744 bool createFailed = false;
745};
746
747struct QD3D12MemoryAllocator
748{
749 bool create(ID3D12Device *device, IDXGIAdapter1 *adapter);
750 void destroy();
751
752 HRESULT createResource(D3D12_HEAP_TYPE heapType,
753 const D3D12_RESOURCE_DESC *resourceDesc,
754 D3D12_RESOURCE_STATES initialState,
755 const D3D12_CLEAR_VALUE *optimizedClearValue,
756 D3D12MA::Allocation **maybeAllocation,
757 REFIID riidResource,
758 void **ppvResource);
759
760 void getBudget(D3D12MA::Budget *localBudget, D3D12MA::Budget *nonLocalBudget);
761
762 bool isUsingD3D12MA() const { return allocator != nullptr; }
763
764 ID3D12Device *device = nullptr;
765 D3D12MA::Allocator *allocator = nullptr;
766};
767
768struct QD3D12Buffer : public QRhiBuffer
769{
770 QD3D12Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
771 ~QD3D12Buffer();
772
773 void destroy() override;
774 bool create() override;
775 QRhiBuffer::NativeBuffer nativeBuffer() override;
776 char *beginFullDynamicBufferUpdateForCurrentFrame() override;
777 void endFullDynamicBufferUpdateForCurrentFrame() override;
778
779 void executeHostWritesForFrameSlot(int frameSlot);
780
781 QD3D12ObjectHandle handles[QD3D12_FRAMES_IN_FLIGHT] = {};
782 struct HostWrite {
783 quint32 offset;
784 QRhiBufferData data;
785 };
786 QVarLengthArray<HostWrite, 16> pendingHostWrites[QD3D12_FRAMES_IN_FLIGHT];
787 uint generation = 0;
788 friend class QRhiD3D12;
789 friend struct QD3D12ShaderResourceBindings;
790};
791
792struct QD3D12RenderBuffer : public QRhiRenderBuffer
793{
794 QD3D12RenderBuffer(QRhiImplementation *rhi,
795 Type type,
796 const QSize &pixelSize,
797 int sampleCount,
798 Flags flags,
799 QRhiTexture::Format backingFormatHint);
800 ~QD3D12RenderBuffer();
801 void destroy() override;
802 bool create() override;
803 QRhiTexture::Format backingFormat() const override;
804
805 static const DXGI_FORMAT DS_FORMAT = DXGI_FORMAT_D24_UNORM_S8_UINT;
806
807 QD3D12ObjectHandle handle;
808 QD3D12Descriptor rtv;
809 QD3D12Descriptor dsv;
810 DXGI_FORMAT dxgiFormat;
811 DXGI_SAMPLE_DESC sampleDesc;
812 uint generation = 0;
813 friend class QRhiD3D12;
814};
815
816struct QD3D12Texture : public QRhiTexture
817{
818 QD3D12Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
819 int arraySize, int sampleCount, Flags flags);
820 ~QD3D12Texture();
821 void destroy() override;
822 bool create() override;
823 bool createFrom(NativeTexture src) override;
824 NativeTexture nativeTexture() override;
825 void setNativeLayout(int layout) override;
826
827 bool prepareCreate(QSize *adjustedSize = nullptr);
828 bool finishCreate();
829
830 QD3D12ObjectHandle handle;
831 QD3D12Descriptor srv;
832 DXGI_FORMAT dxgiFormat;
833 DXGI_FORMAT srvFormat;
834 DXGI_FORMAT rtFormat; // RTV/DSV/UAV
835 uint mipLevelCount;
836 DXGI_SAMPLE_DESC sampleDesc;
837 QBitArray subresourceInitialized;
838 uint generation = 0;
839 friend class QRhiD3D12;
840 friend struct QD3D12ShaderResourceBindings;
841};
842
843struct QD3D12Sampler : public QRhiSampler
844{
845 QD3D12Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
846 AddressMode u, AddressMode v, AddressMode w);
847 ~QD3D12Sampler();
848 void destroy() override;
849 bool create() override;
850
851 QD3D12Descriptor lookupOrCreateShaderVisibleDescriptor();
852
853 D3D12_SAMPLER_DESC desc = {};
854 QD3D12Descriptor shaderVisibleDescriptor;
855 uint generation = 0;
856 friend class QRhiD3D12;
857};
858
859struct QD3D12ShadingRateMap : public QRhiShadingRateMap
860{
861 QD3D12ShadingRateMap(QRhiImplementation *rhi);
862 ~QD3D12ShadingRateMap();
863 void destroy() override;
864 bool createFrom(QRhiTexture *src) override;
865
866 QD3D12ObjectHandle handle; // just copied from the texture
867 friend class QRhiD3D12;
868};
869
870struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
871{
872 QD3D12RenderPassDescriptor(QRhiImplementation *rhi);
873 ~QD3D12RenderPassDescriptor();
874 void destroy() override;
875 bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
876 QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
877 QVector<quint32> serializedFormat() const override;
878
879 void updateSerializedFormat();
880
881 static const int MAX_COLOR_ATTACHMENTS = 8;
882 int colorAttachmentCount = 0;
883 bool hasDepthStencil = false;
884 int colorFormat[MAX_COLOR_ATTACHMENTS];
885 int dsFormat;
886 bool hasShadingRateMap = false;
887 QVector<quint32> serializedFormatData;
888};
889
890struct QD3D12RenderTargetData
891{
892 QD3D12RenderTargetData(QRhiImplementation *) { }
893
894 QD3D12RenderPassDescriptor *rp = nullptr;
895 QSize pixelSize;
896 float dpr = 1;
897 int sampleCount = 1;
898 int colorAttCount = 0;
899 int dsAttCount = 0;
900 QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
901 static const int MAX_COLOR_ATTACHMENTS = QD3D12RenderPassDescriptor::MAX_COLOR_ATTACHMENTS;
902 D3D12_CPU_DESCRIPTOR_HANDLE rtv[MAX_COLOR_ATTACHMENTS];
903 D3D12_CPU_DESCRIPTOR_HANDLE dsv;
904};
905
906struct QD3D12SwapChainRenderTarget : public QRhiSwapChainRenderTarget
907{
908 QD3D12SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
909 ~QD3D12SwapChainRenderTarget();
910 void destroy() override;
911
912 QSize pixelSize() const override;
913 float devicePixelRatio() const override;
914 int sampleCount() const override;
915
916 QD3D12RenderTargetData d;
917};
918
919struct QD3D12TextureRenderTarget : public QRhiTextureRenderTarget
920{
921 QD3D12TextureRenderTarget(QRhiImplementation *rhi,
922 const QRhiTextureRenderTargetDescription &desc,
923 Flags flags);
924 ~QD3D12TextureRenderTarget();
925 void destroy() override;
926
927 QSize pixelSize() const override;
928 float devicePixelRatio() const override;
929 int sampleCount() const override;
930
931 QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
932 bool create() override;
933
934 QD3D12RenderTargetData d;
935 bool ownsRtv[QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS];
936 QD3D12Descriptor rtv[QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS];
937 bool ownsDsv = false;
938 QD3D12Descriptor dsv;
939 friend class QRhiD3D12;
940};
941
942struct QD3D12GraphicsPipeline;
943struct QD3D12ComputePipeline;
944
945struct QD3D12ShaderResourceBindings : public QRhiShaderResourceBindings
946{
947 QD3D12ShaderResourceBindings(QRhiImplementation *rhi);
948 ~QD3D12ShaderResourceBindings();
949 void destroy() override;
950 bool create() override;
951 void updateResources(UpdateFlags flags) override;
952
953 QD3D12ObjectHandle createRootSignature(const QD3D12ShaderStageData *stageData, int stageCount);
954
955 // Set by createRootSignature() for the pipeline that asked for it.
956 int pushConstantRootParamIndex = -1;
957
958 struct VisitorData {
959 QVarLengthArray<D3D12_ROOT_PARAMETER1, 2> cbParams[6];
960
961 D3D12_ROOT_PARAMETER1 srvTables[6] = {};
962 QVarLengthArray<D3D12_DESCRIPTOR_RANGE1, 4> srvRanges[6];
963 quint32 currentSrvRangeOffset[6] = {};
964
965 QVarLengthArray<D3D12_ROOT_PARAMETER1, 4> samplerTables[6];
966 std::array<D3D12_DESCRIPTOR_RANGE1, 16> samplerRanges[6] = {};
967 int samplerRangeHeads[6] = {};
968
969 D3D12_ROOT_PARAMETER1 uavTables[6] = {};
970 QVarLengthArray<D3D12_DESCRIPTOR_RANGE1, 4> uavRanges[6];
971 quint32 currentUavRangeOffset[6] = {};
972 } visitorData;
973
974
975 void visitUniformBuffer(QD3D12Stage s,
976 const QRhiShaderResourceBinding::Data::UniformBufferData &d,
977 int shaderRegister,
978 int binding);
979 void visitTextures(QD3D12Stage s,
980 const QRhiShaderResourceBinding::TextureAndSampler *d,
981 int count,
982 int baseShaderRegister);
983 void visitSamplers(QD3D12Stage s,
984 const QRhiShaderResourceBinding::TextureAndSampler *d,
985 int count,
986 int baseShaderRegister);
987 void visitStorageBuffer(QD3D12Stage s,
988 const QRhiShaderResourceBinding::Data::StorageBufferData &d,
989 QD3D12ShaderResourceVisitor::StorageOp op,
990 int shaderRegister);
991 void visitStorageImage(QD3D12Stage s,
992 const QRhiShaderResourceBinding::Data::StorageImageData &d,
993 QD3D12ShaderResourceVisitor::StorageOp op,
994 int shaderRegister);
995
996 // The translation of the QRhi-level bindings into what has to be handed to
997 // the command list. This depends both on the srb's own contents and on the
998 // per-stage native resource binding maps of the pipeline, and is rebuilt
999 // whenever either changes - but not on every setShaderResources().
1000 struct BindingCache {
1001 // The frame slot's resource and any dynamic offset are only known when
1002 // binding, so keep the QRhiBuffer and resolve there.
1003 struct CBuf {
1004 QD3D12Buffer *buf;
1005 quint32 offset;
1006 int binding;
1007 };
1008 QVarLengthArray<CBuf, 2> cbufs[6];
1009 QVarLengthArray<D3D12_CPU_DESCRIPTOR_HANDLE, 8> srvs[6];
1010 QVarLengthArray<D3D12_GPU_DESCRIPTOR_HANDLE, 8> samplers[6];
1011 struct Uav {
1012 QD3D12ObjectHandle handle;
1013 D3D12_UNORDERED_ACCESS_VIEW_DESC desc;
1014 };
1015 QVarLengthArray<Uav, 4> uavs[6];
1016 quint32 srvUavCount = 0;
1017
1018 void reset() {
1019 for (int s = 0; s < 6; ++s) {
1020 cbufs[s].clear();
1021 srvs[s].clear();
1022 samplers[s].clear();
1023 uavs[s].clear();
1024 }
1025 srvUavCount = 0;
1026 }
1027 } bindingCache;
1028
1029 bool bindingCacheValid = false;
1030 uint bindingCacheGeneration = 0;
1031
1032 void rebuildBindingCache(const QD3D12ShaderStageData *stageData, int stageCount);
1033
1034 // Unlike the visit* functions above, these run at setShaderResources() time
1035 // and so are free to look at the bound QRhiBuffer/Texture/Sampler objects.
1036 void cacheUniformBuffer(QD3D12Stage s,
1037 const QRhiShaderResourceBinding::Data::UniformBufferData &d,
1038 int shaderRegister,
1039 int binding);
1040 void cacheTextures(QD3D12Stage s,
1041 const QRhiShaderResourceBinding::TextureAndSampler *d,
1042 int count,
1043 int baseShaderRegister);
1044 void cacheSamplers(QD3D12Stage s,
1045 const QRhiShaderResourceBinding::TextureAndSampler *d,
1046 int count,
1047 int baseShaderRegister);
1048 void cacheStorageBuffer(QD3D12Stage s,
1049 const QRhiShaderResourceBinding::Data::StorageBufferData &d,
1050 QD3D12ShaderResourceVisitor::StorageOp op,
1051 int shaderRegister);
1052 void cacheStorageImage(QD3D12Stage s,
1053 const QRhiShaderResourceBinding::Data::StorageImageData &d,
1054 QD3D12ShaderResourceVisitor::StorageOp op,
1055 int shaderRegister);
1056
1057 struct BoundUniformBufferData {
1058 quint64 id;
1059 uint generation;
1060 };
1061 struct BoundSampledTextureData {
1062 struct Entry {
1063 quint64 texId = 0;
1064 uint texGeneration = 0;
1065 quint64 samplerId = 0;
1066 uint samplerGeneration = 0;
1067 };
1068 QVarLengthArray<Entry, 1> d;
1069 };
1070 struct BoundStorageImageData {
1071 quint64 id;
1072 uint generation;
1073 };
1074 struct BoundStorageBufferData {
1075 quint64 id;
1076 uint generation;
1077 };
1078 struct BoundResourceData {
1079 BoundUniformBufferData ubuf = {};
1080 BoundSampledTextureData stex;
1081 BoundStorageImageData simage = {};
1082 BoundStorageBufferData sbuf = {};
1083 };
1084 QVarLengthArray<BoundResourceData, 8> boundResourceData;
1085
1086 bool hasDynamicOffset = false;
1087 uint generation = 0;
1088 QD3D12GraphicsPipeline *lastUsedGraphicsPipeline = nullptr;
1089 QD3D12ComputePipeline *lastUsedComputePipeline = nullptr;
1090 uint lastUsedPipelineGeneration = 0;
1091
1092 friend class QRhiD3D12;
1093 friend struct QD3D12ShaderResourceVisitor;
1094};
1095
1096struct QD3D12GraphicsPipeline : public QRhiGraphicsPipeline
1097{
1098 QD3D12GraphicsPipeline(QRhiImplementation *rhi);
1099 ~QD3D12GraphicsPipeline();
1100 void destroy() override;
1101 bool create() override;
1102
1103 QD3D12ObjectHandle handle;
1104 QD3D12ObjectHandle rootSigHandle;
1105 int pushConstantRootParamIndex = -1;
1106 std::array<QD3D12ShaderStageData, 5> stageData;
1107 D3D12_PRIMITIVE_TOPOLOGY topology;
1108 UINT viewInstanceMask = 0;
1109 uint generation = 0;
1110 friend class QRhiD3D12;
1111};
1112
1113struct QD3D12ComputePipeline : public QRhiComputePipeline
1114{
1115 QD3D12ComputePipeline(QRhiImplementation *rhi);
1116 ~QD3D12ComputePipeline();
1117 void destroy() override;
1118 bool create() override;
1119
1120 QD3D12ObjectHandle handle;
1121 QD3D12ObjectHandle rootSigHandle;
1122 int pushConstantRootParamIndex = -1;
1123 QD3D12ShaderStageData stageData;
1124 uint generation = 0;
1125 friend class QRhiD3D12;
1126};
1127
1128struct QD3D12CommandBuffer : public QRhiCommandBuffer
1129{
1130 QD3D12CommandBuffer(QRhiImplementation *rhi);
1131 ~QD3D12CommandBuffer();
1132 void destroy() override;
1133
1134 const QRhiNativeHandles *nativeHandles();
1135
1136 D3D12GraphicsCommandList *cmdList = nullptr; // not owned
1137 QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct;
1138
1139 enum PassType {
1140 NoPass,
1141 RenderPass,
1142 ComputePass
1143 };
1144
1145 void resetState()
1146 {
1147 recordingPass = NoPass;
1148 currentTarget = nullptr;
1149
1150 resetPerPassState();
1151 }
1152
1153 void resetPerPassState()
1154 {
1155 currentGraphicsPipeline = nullptr;
1156 currentComputePipeline = nullptr;
1157 currentPipelineGeneration = 0;
1158 currentGraphicsSrb = nullptr;
1159 currentComputeSrb = nullptr;
1160 currentSrbGeneration = 0;
1161 currentIndexBuffer = {};
1162 currentIndexOffset = 0;
1163 currentIndexFormat = DXGI_FORMAT_R16_UINT;
1164 currentVertexBuffers = {};
1165 currentVertexOffsets = {};
1166 hasShadingRateSet = false;
1167 hasShadingRateMapSet = false;
1168 hasCustomScissorSet = false;
1169 currentViewport = {};
1170 }
1171
1172 // per-frame
1173 PassType recordingPass;
1174 QRhiRenderTarget *currentTarget;
1175
1176 // per-pass
1177 QD3D12GraphicsPipeline *currentGraphicsPipeline;
1178 QD3D12ComputePipeline *currentComputePipeline;
1179 uint currentPipelineGeneration;
1180 QRhiShaderResourceBindings *currentGraphicsSrb;
1181 QRhiShaderResourceBindings *currentComputeSrb;
1182 uint currentSrbGeneration;
1183 QD3D12ObjectHandle currentIndexBuffer;
1184 quint32 currentIndexOffset;
1185 DXGI_FORMAT currentIndexFormat;
1186 std::array<QD3D12ObjectHandle, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexBuffers;
1187 std::array<quint32, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexOffsets;
1188 bool hasShadingRateSet;
1189 bool hasShadingRateMapSet;
1190 bool hasCustomScissorSet;
1191 QRhiViewport currentViewport;
1192
1193 // global
1194 double lastGpuTime = 0;
1195};
1196
1197struct QD3D12SwapChain : public QRhiSwapChain
1198{
1199 QD3D12SwapChain(QRhiImplementation *rhi);
1200 ~QD3D12SwapChain();
1201 void destroy() override;
1202
1203 QRhiCommandBuffer *currentFrameCommandBuffer() override;
1204 QRhiRenderTarget *currentFrameRenderTarget() override;
1205 QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override;
1206
1207 QSize surfacePixelSize() override;
1208 bool isFormatSupported(Format f) override;
1209 QRhiSwapChainHdrInfo hdrInfo() override;
1210
1211 QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
1212 bool createOrResize() override;
1213
1214 void releaseBuffers();
1215 void waitCommandCompletionForFrameSlot(int frameSlot);
1216 void addCommandCompletionSignalForCurrentFrameSlot();
1217 void chooseFormats();
1218
1219 QWindow *window = nullptr;
1220 IDXGISwapChain1 *sourceSwapChain1 = nullptr;
1221 IDXGISwapChain3 *swapChain = nullptr;
1222 QSize pixelSize;
1223 UINT swapInterval = 1;
1224 UINT swapChainFlags = 0;
1225 BOOL stereo = false;
1226 DXGI_FORMAT colorFormat;
1227 DXGI_FORMAT srgbAdjustedColorFormat;
1228 DXGI_COLOR_SPACE_TYPE hdrColorSpace;
1229 IDCompositionTarget *dcompTarget = nullptr;
1230 IDCompositionVisual *dcompVisual = nullptr;
1231 static const UINT BUFFER_COUNT = 3;
1232 QD3D12ObjectHandle colorBuffers[BUFFER_COUNT];
1233 QD3D12Descriptor rtvs[BUFFER_COUNT];
1234 QD3D12Descriptor rtvsRight[BUFFER_COUNT];
1235 DXGI_SAMPLE_DESC sampleDesc;
1236 QD3D12ObjectHandle msaaBuffers[QD3D12_FRAMES_IN_FLIGHT]; // one per frame slot, not swapchain buffer, is enough
1237 QD3D12Descriptor msaaRtvs[QD3D12_FRAMES_IN_FLIGHT];
1238 QD3D12RenderBuffer *ds = nullptr;
1239 UINT currentBackBufferIndex = 0;
1240 QD3D12SwapChainRenderTarget rtWrapper;
1241 QD3D12SwapChainRenderTarget rtWrapperRight;
1242 QD3D12CommandBuffer cbWrapper;
1243 HANDLE frameLatencyWaitableObject = nullptr;
1244
1245 struct FrameResources {
1246 ID3D12Fence *fence = nullptr;
1247 HANDLE fenceEvent = nullptr;
1248 UINT64 fenceCounter = 0;
1249 D3D12GraphicsCommandList *cmdList = nullptr;
1250 } frameRes[QD3D12_FRAMES_IN_FLIGHT];
1251
1252 int currentFrameSlot = 0; // index in frameRes
1253 int lastFrameLatencyWaitSlot = -1;
1254};
1255
1256template<typename T, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type>
1257struct alignas(void*) QD3D12PipelineStateSubObject
1258{
1259 D3D12_PIPELINE_STATE_SUBOBJECT_TYPE type = Type;
1260 T object = {};
1261};
1262
1263class QD3D12Adapter : public QRhiAdapter
1264{
1265public:
1266 QRhiDriverInfo info() const override;
1267
1268 LUID luid;
1269 QRhiDriverInfo adapterInfo;
1270};
1271
1272class QRhiD3D12 : public QRhiImplementation
1273{
1274public:
1275 // The shared, per-slot staging area starts at 512 KB and grows up to 16 MB.
1276 static constexpr quint32 SMALL_STAGING_AREA_BYTES_PER_FRAME_START = 512 * 1024;
1277 static constexpr quint32 SMALL_STAGING_AREA_BYTES_PER_FRAME_MAX = 16 * 1024 * 1024;
1278 // Only give the memory back once the demand has stayed low for a while,
1279 // so that an alternating pattern does not recreate the area every frame.
1280 static constexpr int SMALL_STAGING_AREA_LOW_DEMAND_FRAMES = 60;
1281
1282 static constexpr quint32 SHADER_VISIBLE_CBV_SRV_UAV_HEAP_PER_FRAME_START_SIZE = 16384;
1283
1284 QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *importDevice = nullptr);
1285
1286 bool create(QRhi::Flags flags) override;
1287 void destroy() override;
1288 QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const override;
1289
1290 QRhiGraphicsPipeline *createGraphicsPipeline() override;
1291 QRhiComputePipeline *createComputePipeline() override;
1292 QRhiShaderResourceBindings *createShaderResourceBindings() override;
1293 QRhiBuffer *createBuffer(QRhiBuffer::Type type,
1294 QRhiBuffer::UsageFlags usage,
1295 quint32 size) override;
1296 QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
1297 const QSize &pixelSize,
1298 int sampleCount,
1299 QRhiRenderBuffer::Flags flags,
1300 QRhiTexture::Format backingFormatHint) override;
1301 QRhiTexture *createTexture(QRhiTexture::Format format,
1302 const QSize &pixelSize,
1303 int depth,
1304 int arraySize,
1305 int sampleCount,
1306 QRhiTexture::Flags flags) override;
1307 QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
1308 QRhiSampler::Filter minFilter,
1309 QRhiSampler::Filter mipmapMode,
1310 QRhiSampler:: AddressMode u,
1311 QRhiSampler::AddressMode v,
1312 QRhiSampler::AddressMode w) override;
1313
1314 QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
1315 QRhiTextureRenderTarget::Flags flags) override;
1316
1317 QRhiShadingRateMap *createShadingRateMap() override;
1318
1319 QRhiSwapChain *createSwapChain() override;
1320 QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
1321 QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
1322 QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
1323 QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
1324 QRhi::FrameOpResult finish() override;
1325
1326 void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
1327
1328 void beginPass(QRhiCommandBuffer *cb,
1329 QRhiRenderTarget *rt,
1330 const QColor &colorClearValue,
1331 const QRhiDepthStencilClearValue &depthStencilClearValue,
1332 QRhiResourceUpdateBatch *resourceUpdates,
1333 QRhiCommandBuffer::BeginPassFlags flags) override;
1334 void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
1335
1336 void setGraphicsPipeline(QRhiCommandBuffer *cb,
1337 QRhiGraphicsPipeline *ps) override;
1338
1339 void setShaderResources(QRhiCommandBuffer *cb,
1340 QRhiShaderResourceBindings *srb,
1341 int dynamicOffsetCount,
1342 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
1343
1344 void setVertexInput(QRhiCommandBuffer *cb,
1345 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1346 QRhiBuffer *indexBuf, quint32 indexOffset,
1347 QRhiCommandBuffer::IndexFormat indexFormat) override;
1348
1349 void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
1350 void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
1351 void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
1352 void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
1353 void setPushConstants(QRhiCommandBuffer *cb, quint32 offset, quint32 size, const void *data) override;
1354 void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
1355
1356 void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
1357 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
1358
1359 void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
1360 quint32 instanceCount, quint32 firstIndex,
1361 qint32 vertexOffset, quint32 firstInstance) override;
1362
1363 void drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
1364 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override;
1365
1366 void drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
1367 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override;
1368
1369 void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
1370 void debugMarkEnd(QRhiCommandBuffer *cb) override;
1371 void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
1372
1373 void beginComputePass(QRhiCommandBuffer *cb,
1374 QRhiResourceUpdateBatch *resourceUpdates,
1375 QRhiCommandBuffer::BeginPassFlags flags) override;
1376 void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
1377 void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
1378 void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
1379 void dispatchIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
1380 quint32 indirectBufferOffset) override;
1381
1382 void drawIndirectCount(QRhiCommandBuffer *cb,
1383 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
1384 QRhiBuffer *countBuffer, quint32 countBufferOffset,
1385 quint32 maxDrawCount, quint32 stride) override;
1386 void drawIndexedIndirectCount(QRhiCommandBuffer *cb,
1387 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
1388 QRhiBuffer *countBuffer, quint32 countBufferOffset,
1389 quint32 maxDrawCount, quint32 stride) override;
1390 ID3D12CommandSignature *indirectDrawCommandSignature(bool indexed, quint32 stride);
1391
1392 const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
1393 void beginExternal(QRhiCommandBuffer *cb) override;
1394 void endExternal(QRhiCommandBuffer *cb) override;
1395 double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
1396
1397 QList<int> supportedSampleCounts() const override;
1398 QList<QSize> supportedShadingRates(int sampleCount) const override;
1399 int ubufAlignment() const override;
1400 bool isYUpInFramebuffer() const override;
1401 bool isYUpInNDC() const override;
1402 bool isClipDepthZeroToOne() const override;
1403 QMatrix4x4 clipSpaceCorrMatrix() const override;
1404 bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
1405 bool isFeatureSupported(QRhi::Feature feature) const override;
1406 int resourceLimit(QRhi::ResourceLimit limit) const override;
1407 const QRhiNativeHandles *nativeHandles() override;
1408 QRhiDriverInfo driverInfo() const override;
1409 QRhiStats statistics() override;
1410 bool makeThreadLocalNativeContextCurrent() override;
1411 void setQueueSubmitParams(QRhiNativeHandles *params) override;
1412 void releaseCachedResources() override;
1413 bool isDeviceLost() const override;
1414
1415 QByteArray pipelineCacheData() override;
1416 void setPipelineCacheData(const QByteArray &data) override;
1417
1418 // Must be called for every request made of the per-frame staging area,
1419 // whether or not it ends up fitting: what does not fit is exactly what the
1420 // area should have been big enough for. The point being that if the common
1421 // area is too small, we record the demand for the next frame, and fall back
1422 // to using a temporary, dedicated staging area instead, but in the next
1423 // frame the common one will be big enough, if still needed.
1424 void recordSmallStagingAreaDemand(quint32 byteSize)
1425 {
1426 smallStagingAreaBytesNeeded[currentFrameSlot] += byteSize;
1427 }
1428 void resetAndResizeSmallStagingArea(int frameSlot);
1429
1430 ID3D12PipelineState *loadOrCreatePipelineState(const D3D12_PIPELINE_STATE_STREAM_DESC *streamDesc,
1431 const QByteArray &cacheKey,
1432 const char *what);
1433 bool ensurePipelineLibrary();
1434 bool createPipelineLibrary(const QByteArray &blob);
1435 void destroyPipelineLibrary();
1436
1437 void waitGpu();
1438 DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const;
1439 bool ensureDirectCompositionDevice();
1440 bool startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList);
1441 void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
1442 void finishActiveReadbacks(bool forced = false);
1443 bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
1444 D3D12_DESCRIPTOR_HEAP_TYPE type,
1445 int frameSlot,
1446 quint32 neededDescriptorCount,
1447 bool *gotNew);
1448 void bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD);
1449 void setDefaultScissor(QD3D12CommandBuffer *cbD);
1450
1451 bool debugLayer = false;
1452#ifdef QRHI_D3D12_INFOQUEUE1_AVAILABLE
1453 ID3D12InfoQueue1 *infoQueue1 = nullptr;
1454 DWORD infoQueueCallbackCookie = 0;
1455#endif
1456 UINT maxFrameLatency = 2; // 1-3, use 2 to keep CPU-GPU parallelism while reducing lag compared to tripple buffering
1457 ID3D12Device2 *dev = nullptr;
1458 D3D_FEATURE_LEVEL minimumFeatureLevel = D3D_FEATURE_LEVEL(0);
1459 LUID adapterLuid = {};
1460 bool importedDevice = false;
1461 bool importedCommandQueue = false;
1462 QRhi::Flags rhiFlags;
1463 IDXGIFactory2 *dxgiFactory = nullptr;
1464 bool supportsAllowTearing = false;
1465 IDXGIAdapter1 *activeAdapter = nullptr;
1466 QRhiDriverInfo driverInfoStruct;
1467 QRhiD3D12NativeHandles nativeHandlesStruct;
1468 bool deviceLost = false;
1469 ID3D12CommandQueue *cmdQueue = nullptr;
1470 ID3D12Fence *fullFence = nullptr;
1471 HANDLE fullFenceEvent = nullptr;
1472 UINT64 fullFenceCounter = 0;
1473 ID3D12CommandAllocator *cmdAllocators[QD3D12_FRAMES_IN_FLIGHT] = {};
1474 QD3D12MemoryAllocator vma;
1475 QD3D12CpuDescriptorPool rtvPool;
1476 QD3D12CpuDescriptorPool dsvPool;
1477 QD3D12CpuDescriptorPool cbvSrvUavPool;
1478 QD3D12ObjectPool<QD3D12Resource> resourcePool;
1479 QD3D12ObjectPool<QD3D12Pipeline> pipelinePool;
1480 QD3D12ObjectPool<QD3D12RootSignature> rootSignaturePool;
1481 QD3D12ReleaseQueue releaseQueue;
1482 QD3D12ResourceBarrierGenerator barrierGen;
1483 QD3D12SamplerManager samplerMgr;
1484 QD3D12MipmapGenerator mipmapGen;
1485 QD3D12MipmapGenerator3D mipmapGen3D;
1486 QD3D12StagingArea smallStagingAreas[QD3D12_FRAMES_IN_FLIGHT];
1487 quint32 smallStagingAreaBytesNeeded[QD3D12_FRAMES_IN_FLIGHT] = {};
1488 int smallStagingAreaLowDemandFrames[QD3D12_FRAMES_IN_FLIGHT] = {};
1489 QD3D12ShaderVisibleDescriptorHeap shaderVisibleCbvSrvUavHeap;
1490 UINT64 timestampTicksPerSecond = 0;
1491 QD3D12QueryHeap timestampQueryHeap;
1492 QD3D12StagingArea timestampReadbackArea;
1493 IDCompositionDevice *dcompDevice = nullptr;
1494 QD3D12SwapChain *currentSwapChain = nullptr;
1495 QSet<QD3D12SwapChain *> swapchains;
1496 QD3D12ShaderBytecodeCache shaderBytecodeCache { "Shader bytecode cache" };
1497 QVarLengthArray<QD3D12Readback, 4> activeReadbacks;
1498 bool offscreenActive = false;
1499 QD3D12CommandBuffer *offscreenCb[QD3D12_FRAMES_IN_FLIGHT] = {};
1500 UINT shadingRateImageTileSize = 0;
1501 ID3D12CommandSignature *drawCommandSignature = nullptr;
1502 ID3D12CommandSignature *drawIndexedCommandSignature = nullptr;
1503 ID3D12CommandSignature *dispatchCommandSignature = nullptr;
1504#ifdef QRHI_D3D12_PIPELINE_LIBRARY_AVAILABLE
1505 ID3D12PipelineLibrary1 *pipelineLibrary = nullptr;
1506 // CreatePipelineLibrary() does not copy: the blob must stay alive and
1507 // unmodified for as long as the library does.
1508 QByteArray pipelineLibraryBlob;
1509 // StorePipeline() fails if the name is taken already.
1510 QSet<QByteArray> pipelineLibraryNames;
1511#endif
1512 QHash<quint32, ID3D12CommandSignature *> drawCommandSignaturesByStride;
1513 QHash<quint32, ID3D12CommandSignature *> drawIndexedCommandSignaturesByStride;
1514
1515 struct {
1516 bool multiView = false;
1517 bool textureViewFormat = false;
1518 bool vrs = false;
1519 bool vrsMap = false;
1520 bool vrsAdditionalRates = false;
1521 } caps;
1522};
1523
1524QT_END_NAMESPACE
1525
1526#endif // __ID3D12Device2_INTERFACE_DEFINED__
1527
1528#endif
#define __has_include(x)