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
qrhid3d11.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 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#include "qrhid3d11_p.h"
6#include "qshader.h"
7#include "vs_test_p.h"
8#include <QWindow>
9#include <qmath.h>
10#include <QtCore/qcryptographichash.h>
11#include <QtCore/private/qsystemerror_p.h>
13
14#include <cstdio>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20/*
21 Direct3D 11 backend. Provides a double-buffered flip model swapchain.
22 Textures and "static" buffers are USAGE_DEFAULT, leaving it to
23 UpdateSubResource to upload the data in any way it sees fit. "Dynamic"
24 buffers are USAGE_DYNAMIC and updating is done by mapping with WRITE_DISCARD.
25 (so here QRhiBuffer keeps a copy of the buffer contents and all of it is
26 memcpy'd every time, leaving the rest (juggling with the memory area Map
27 returns) to the driver).
28*/
29
30/*!
31 \class QRhiD3D11InitParams
32 \inmodule QtGuiPrivate
33 \inheaderfile rhi/qrhi.h
34 \since 6.6
35 \brief Direct3D 11 specific initialization parameters.
36
37 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
38 for details.
39
40 A D3D11-based QRhi needs no special parameters for initialization. If
41 desired, enableDebugLayer can be set to \c true to enable the Direct3D
42 debug layer. This can be useful during development, but should be avoided
43 in production builds.
44
45 \badcode
46 QRhiD3D11InitParams params;
47 params.enableDebugLayer = true;
48 rhi = QRhi::create(QRhi::D3D11, &params);
49 \endcode
50
51 \note QRhiSwapChain should only be used in combination with QWindow
52 instances that have their surface type set to QSurface::Direct3DSurface.
53
54 \section2 Working with existing Direct3D 11 devices
55
56 When interoperating with another graphics engine, it may be necessary to
57 get a QRhi instance that uses the same Direct3D device. This can be
58 achieved by passing a pointer to a QRhiD3D11NativeHandles to
59 QRhi::create(). When the device is set to a non-null value, the device
60 context must be specified as well. QRhi does not take ownership of any of
61 the external objects.
62
63 Sometimes, for example when using QRhi in combination with OpenXR, one will
64 want to specify which adapter to use, and optionally, which feature level
65 to request on the device, while leaving the device creation to QRhi. This
66 is achieved by leaving the device and context pointers set to null, while
67 specifying the adapter LUID and feature level.
68
69 \note QRhi works with immediate contexts only. Deferred contexts are not
70 used in any way.
71
72 \note Regardless of using an imported or a QRhi-created device context, the
73 \c ID3D11DeviceContext1 interface (Direct3D 11.1) must be supported.
74 Initialization will fail otherwise.
75 */
76
77/*!
78 \variable QRhiD3D11InitParams::enableDebugLayer
79
80 When set to true, a debug device is created, assuming the debug layer is
81 available. The default value is false.
82*/
83
84/*!
85 \class QRhiD3D11NativeHandles
86 \inmodule QtGuiPrivate
87 \inheaderfile rhi/qrhi.h
88 \since 6.6
89 \brief Holds the D3D device and device context used by the QRhi.
90
91 \note The class uses \c{void *} as the type since including the COM-based
92 \c{d3d11.h} headers is not acceptable here. The actual types are
93 \c{ID3D11Device *} and \c{ID3D11DeviceContext *}.
94
95 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
96 for details.
97 */
98
99/*!
100 \variable QRhiD3D11NativeHandles::dev
101
102 Points to a
103 \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11device}{ID3D11Device}
104 or left set to \nullptr if no existing device is to be imported.
105
106 \note When importing a device, both the device and the device context must be set to valid objects.
107*/
108
109/*!
110 \variable QRhiD3D11NativeHandles::context
111
112 Points to a \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11devicecontext}{ID3D11DeviceContext}
113 or left set to \nullptr if no existing device context is to be imported.
114
115 \note When importing a device, both the device and the device context must be set to valid objects.
116*/
117
118/*!
119 \variable QRhiD3D11NativeHandles::featureLevel
120
121 Specifies the feature level passed to
122 \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice}{D3D11CreateDevice()}.
123 Relevant only when QRhi creates the device, ignored when importing a device
124 and device context. When not set, the default rules outlined in the D3D
125 documentation apply.
126*/
127
128/*!
129 \variable QRhiD3D11NativeHandles::adapterLuidLow
130
131 The low part of the local identifier (LUID) of the DXGI adapter to use.
132 Relevant only when QRhi creates the device, ignored when importing a device
133 and device context.
134*/
135
136/*!
137 \variable QRhiD3D11NativeHandles::adapterLuidHigh
138
139 The high part of the local identifier (LUID) of the DXGI adapter to use.
140 Relevant only when QRhi creates the device, ignored when importing a device
141 and device context.
142*/
143
144// help mingw with its ancient sdk headers
145#ifndef DXGI_ADAPTER_FLAG_SOFTWARE
146#define DXGI_ADAPTER_FLAG_SOFTWARE 2
147#endif
148
149#ifndef D3D11_1_UAV_SLOT_COUNT
150#define D3D11_1_UAV_SLOT_COUNT 64
151#endif
152
153#ifndef D3D11_VS_INPUT_REGISTER_COUNT
154#define D3D11_VS_INPUT_REGISTER_COUNT 32
155#endif
156
157QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importParams)
158 : ofr(this)
159{
160 debugLayer = params->enableDebugLayer;
161
162 if (importParams) {
163 if (importParams->dev && importParams->context) {
164 dev = reinterpret_cast<ID3D11Device *>(importParams->dev);
165 ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importParams->context);
166 if (SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context)))) {
167 // get rid of the ref added by QueryInterface
168 ctx->Release();
170 } else {
171 qWarning("ID3D11DeviceContext1 not supported by context, cannot import");
172 }
173 }
174 featureLevel = D3D_FEATURE_LEVEL(importParams->featureLevel);
175 adapterLuid.LowPart = importParams->adapterLuidLow;
176 adapterLuid.HighPart = importParams->adapterLuidHigh;
177 }
178}
179
180template <class Int>
181inline Int aligned(Int v, Int byteAlign)
182{
183 return (v + byteAlign - 1) & ~(byteAlign - 1);
184}
185
187{
188 IDXGIFactory1 *result = nullptr;
189 const HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&result));
190 if (FAILED(hr)) {
191 qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s",
192 qPrintable(QSystemError::windowsComString(hr)));
193 result = nullptr;
194 }
195 return result;
196}
197
198bool QRhiD3D11::create(QRhi::Flags flags)
199{
200 rhiFlags = flags;
201
202 uint devFlags = 0;
203 if (debugLayer)
204 devFlags |= D3D11_CREATE_DEVICE_DEBUG;
205
206 dxgiFactory = createDXGIFactory2();
207 if (!dxgiFactory)
208 return false;
209
210 // For a FLIP_* swapchain Present(0, 0) is not necessarily
211 // sufficient to get non-blocking behavior, try using ALLOW_TEARING
212 // when available.
213 supportsAllowTearing = false;
214 IDXGIFactory5 *factory5 = nullptr;
215 if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast<void **>(&factory5)))) {
216 BOOL allowTearing = false;
217 if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
218 supportsAllowTearing = allowTearing;
219 factory5->Release();
220 }
221
222 if (qEnvironmentVariableIntValue("QT_D3D_FLIP_DISCARD"))
223 qWarning("The default swap effect is FLIP_DISCARD, QT_D3D_FLIP_DISCARD is now ignored");
224
225 // Support for flip model swapchains is required now (since we are
226 // targeting Windows 10+), but the option for using the old model is still
227 // there. (some features are not supported then, however)
228 useLegacySwapchainModel = qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
229
231 if (qEnvironmentVariableIsSet("QT_D3D_MAX_FRAME_LATENCY"))
232 maxFrameLatency = UINT(qMax(0, qEnvironmentVariableIntValue("QT_D3D_MAX_FRAME_LATENCY")));
233 } else {
234 maxFrameLatency = 0;
235 }
236
237 qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s, use legacy (non-FLIP) model = %s, max frame latency = %u",
238 supportsAllowTearing ? "true" : "false",
239 useLegacySwapchainModel ? "true" : "false",
240 maxFrameLatency);
241 if (maxFrameLatency == 0)
242 qCDebug(QRHI_LOG_INFO, "Disabling FRAME_LATENCY_WAITABLE_OBJECT usage");
243
244 activeAdapter = nullptr;
245
247 IDXGIAdapter1 *adapter;
248 int requestedAdapterIndex = -1;
249 if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
250 requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
251
252 if (requestedRhiAdapter)
253 adapterLuid = static_cast<QD3D11Adapter *>(requestedRhiAdapter)->luid;
254
255 // importParams or requestedRhiAdapter may specify an adapter by the luid, use that in the absence of an env.var. override.
256 if (requestedAdapterIndex < 0 && (adapterLuid.LowPart || adapterLuid.HighPart)) {
257 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
258 DXGI_ADAPTER_DESC1 desc;
259 adapter->GetDesc1(&desc);
260 adapter->Release();
261 if (desc.AdapterLuid.LowPart == adapterLuid.LowPart
262 && desc.AdapterLuid.HighPart == adapterLuid.HighPart)
263 {
264 requestedAdapterIndex = adapterIndex;
265 break;
266 }
267 }
268 }
269
270 if (requestedAdapterIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
271 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
272 DXGI_ADAPTER_DESC1 desc;
273 adapter->GetDesc1(&desc);
274 adapter->Release();
275 if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
276 requestedAdapterIndex = adapterIndex;
277 break;
278 }
279 }
280 }
281
282 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
283 DXGI_ADAPTER_DESC1 desc;
284 adapter->GetDesc1(&desc);
285 const QString name = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description));
286 qCDebug(QRHI_LOG_INFO, "Adapter %d: '%s' (vendor 0x%X device 0x%X flags 0x%X)",
287 adapterIndex,
288 qPrintable(name),
289 desc.VendorId,
290 desc.DeviceId,
291 desc.Flags);
292 if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
293 activeAdapter = adapter;
294 adapterLuid = desc.AdapterLuid;
295 QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
296 qCDebug(QRHI_LOG_INFO, " using this adapter");
297 } else {
298 adapter->Release();
299 }
300 }
301 if (!activeAdapter) {
302 qWarning("No adapter");
303 return false;
304 }
305
306 // Normally we won't specify a requested feature level list,
307 // except when a level was specified in importParams.
308 QVarLengthArray<D3D_FEATURE_LEVEL, 4> requestedFeatureLevels;
309 bool requestFeatureLevels = false;
310 if (featureLevel) {
311 requestFeatureLevels = true;
312 requestedFeatureLevels.append(featureLevel);
313 }
314
315 ID3D11DeviceContext *ctx = nullptr;
316 HRESULT hr = D3D11CreateDevice(activeAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags,
317 requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr,
318 requestFeatureLevels ? requestedFeatureLevels.count() : 0,
319 D3D11_SDK_VERSION,
320 &dev, &featureLevel, &ctx);
321 // We cannot assume that D3D11_CREATE_DEVICE_DEBUG is always available. Retry without it, if needed.
322 if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING && debugLayer) {
323 qCDebug(QRHI_LOG_INFO, "Debug layer was requested but is not available. "
324 "Attempting to create D3D11 device without it.");
325 devFlags &= ~D3D11_CREATE_DEVICE_DEBUG;
326 hr = D3D11CreateDevice(activeAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags,
327 requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr,
328 requestFeatureLevels ? requestedFeatureLevels.count() : 0,
329 D3D11_SDK_VERSION,
330 &dev, &featureLevel, &ctx);
331 }
332 if (FAILED(hr)) {
333 qWarning("Failed to create D3D11 device and context: %s",
334 qPrintable(QSystemError::windowsComString(hr)));
335 return false;
336 }
337
338 const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context)));
339 ctx->Release();
340 if (!supports11_1) {
341 qWarning("ID3D11DeviceContext1 not supported");
342 return false;
343 }
344
345 // Test if creating a Shader Model 5.0 vertex shader works; we want to
346 // fail already in create() if that's not the case.
347 ID3D11VertexShader *testShader = nullptr;
348 if (SUCCEEDED(dev->CreateVertexShader(g_testVertexShader, sizeof(g_testVertexShader), nullptr, &testShader))) {
349 testShader->Release();
350 } else {
351 static const char *msg = "D3D11 smoke test: Failed to create vertex shader";
352 if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
353 qCDebug(QRHI_LOG_INFO, "%s", msg);
354 else
355 qWarning("%s", msg);
356 return false;
357 }
358
359 D3D11_FEATURE_DATA_D3D11_OPTIONS features = {};
360 if (SUCCEEDED(dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &features, sizeof(features)))) {
361 // The D3D _runtime_ may be 11.1, but the underlying _driver_ may
362 // still not support this D3D_FEATURE_LEVEL_11_1 feature. (e.g.
363 // because it only does 11_0)
364 if (!features.ConstantBufferOffsetting) {
365 static const char *msg = "D3D11 smoke test: Constant buffer offsetting is not supported by the driver";
366 if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
367 qCDebug(QRHI_LOG_INFO, "%s", msg);
368 else
369 qWarning("%s", msg);
370 return false;
371 }
372 } else {
373 static const char *msg = "D3D11 smoke test: Failed to query D3D11_FEATURE_D3D11_OPTIONS";
374 if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
375 qCDebug(QRHI_LOG_INFO, "%s", msg);
376 else
377 qWarning("%s", msg);
378 return false;
379 }
380 } else {
381 Q_ASSERT(dev && context);
382 featureLevel = dev->GetFeatureLevel();
383 IDXGIDevice *dxgiDev = nullptr;
384 if (SUCCEEDED(dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDev)))) {
385 IDXGIAdapter *adapter = nullptr;
386 if (SUCCEEDED(dxgiDev->GetAdapter(&adapter))) {
387 IDXGIAdapter1 *adapter1 = nullptr;
388 if (SUCCEEDED(adapter->QueryInterface(__uuidof(IDXGIAdapter1), reinterpret_cast<void **>(&adapter1)))) {
389 DXGI_ADAPTER_DESC1 desc;
390 adapter1->GetDesc1(&desc);
391 adapterLuid = desc.AdapterLuid;
392 QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
393 activeAdapter = adapter1;
394 }
395 adapter->Release();
396 }
397 dxgiDev->Release();
398 }
399 if (!activeAdapter) {
400 qWarning("Failed to query adapter from imported device");
401 return false;
402 }
403 qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
404 }
405
406 QDxgiVSyncService::instance()->refAdapter(adapterLuid);
407
408 if (FAILED(context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), reinterpret_cast<void **>(&annotations))))
409 annotations = nullptr;
410
411 deviceLost = false;
412
413 nativeHandlesStruct.dev = dev;
414 nativeHandlesStruct.context = context;
415 nativeHandlesStruct.featureLevel = featureLevel;
416 nativeHandlesStruct.adapterLuidLow = adapterLuid.LowPart;
417 nativeHandlesStruct.adapterLuidHigh = adapterLuid.HighPart;
418
419 return true;
420}
421
423{
424 for (Shader &s : m_shaderCache)
425 s.s->Release();
426
427 m_shaderCache.clear();
428}
429
431{
433
435
436 if (ofr.tsDisjointQuery) {
437 ofr.tsDisjointQuery->Release();
438 ofr.tsDisjointQuery = nullptr;
439 }
440 for (int i = 0; i < 2; ++i) {
441 if (ofr.tsQueries[i]) {
442 ofr.tsQueries[i]->Release();
443 ofr.tsQueries[i] = nullptr;
444 }
445 }
446
447 if (annotations) {
448 annotations->Release();
449 annotations = nullptr;
450 }
451
453 if (context) {
454 context->Release();
455 context = nullptr;
456 }
457 if (dev) {
458 dev->Release();
459 dev = nullptr;
460 }
461 }
462
463 if (dcompDevice) {
464 dcompDevice->Release();
465 dcompDevice = nullptr;
466 }
467
468 if (activeAdapter) {
469 activeAdapter->Release();
470 activeAdapter = nullptr;
471 }
472
473 if (dxgiFactory) {
474 dxgiFactory->Release();
475 dxgiFactory = nullptr;
476 }
477
479 adapterLuid = {};
480
481 QDxgiVSyncService::instance()->derefAdapter(adapterLuid);
482}
483
484void QRhiD3D11::reportLiveObjects(ID3D11Device *device)
485{
486 // this works only when params.enableDebugLayer was true
487 ID3D11Debug *debug;
488 if (SUCCEEDED(device->QueryInterface(__uuidof(ID3D11Debug), reinterpret_cast<void **>(&debug)))) {
489 debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
490 debug->Release();
491 }
492}
493
494QRhi::AdapterList QRhiD3D11::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
495{
496 LUID requestedLuid = {};
497 if (nativeHandles) {
498 QRhiD3D11NativeHandles *h = static_cast<QRhiD3D11NativeHandles *>(nativeHandles);
499 const LUID adapterLuid = { h->adapterLuidLow, h->adapterLuidHigh };
500 if (adapterLuid.LowPart || adapterLuid.HighPart)
501 requestedLuid = adapterLuid;
502 }
503
504 IDXGIFactory1 *dxgi = createDXGIFactory2();
505 if (!dxgi)
506 return {};
507
508 QRhi::AdapterList list;
509 IDXGIAdapter1 *adapter;
510 for (int adapterIndex = 0; dxgi->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
511 DXGI_ADAPTER_DESC1 desc;
512 adapter->GetDesc1(&desc);
513 adapter->Release();
514 if (requestedLuid.LowPart || requestedLuid.HighPart) {
515 if (desc.AdapterLuid.LowPart != requestedLuid.LowPart
516 || desc.AdapterLuid.HighPart != requestedLuid.HighPart)
517 {
518 continue;
519 }
520 }
521 QD3D11Adapter *a = new QD3D11Adapter;
522 a->luid = desc.AdapterLuid;
523 QRhiD3D::fillDriverInfo(&a->adapterInfo, desc);
524 list.append(a);
525 }
526
527 dxgi->Release();
528 return list;
529}
530
532{
533 return adapterInfo;
534}
535
537{
538 return { 1, 2, 4, 8 };
539}
540
542{
543 Q_UNUSED(sampleCount);
544 return { QSize(1, 1) };
545}
546
548{
549 DXGI_SAMPLE_DESC desc;
550 desc.Count = 1;
551 desc.Quality = 0;
552
553 const int s = effectiveSampleCount(sampleCount);
554
555 desc.Count = UINT(s);
556 if (s > 1)
557 desc.Quality = UINT(D3D11_STANDARD_MULTISAMPLE_PATTERN);
558 else
559 desc.Quality = 0;
560
561 return desc;
562}
563
564QRhiSwapChain *QRhiD3D11::createSwapChain()
565{
566 return new QD3D11SwapChain(this);
567}
568
569QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
570{
571 return new QD3D11Buffer(this, type, usage, size);
572}
573
575{
576 return 256;
577}
578
580{
581 return false;
582}
583
585{
586 return true;
587}
588
590{
591 return true;
592}
593
595{
596 // Like with Vulkan, but Y is already good.
597
598 static QMatrix4x4 m;
599 if (m.isIdentity()) {
600 // NB the ctor takes row-major
601 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
602 0.0f, 1.0f, 0.0f, 0.0f,
603 0.0f, 0.0f, 0.5f, 0.5f,
604 0.0f, 0.0f, 0.0f, 1.0f);
605 }
606 return m;
607}
608
609bool QRhiD3D11::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
610{
611 Q_UNUSED(flags);
612
613 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12)
614 return false;
615
616 return true;
617}
618
619bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
620{
621 switch (feature) {
622 case QRhi::MultisampleTexture:
623 return true;
624 case QRhi::MultisampleRenderBuffer:
625 return true;
626 case QRhi::DebugMarkers:
627 return annotations != nullptr;
628 case QRhi::Timestamps:
629 return true;
630 case QRhi::Instancing:
631 return true;
632 case QRhi::CustomInstanceStepRate:
633 return true;
634 case QRhi::PrimitiveRestart:
635 return true;
636 case QRhi::NonDynamicUniformBuffers:
637 return false; // because UpdateSubresource cannot deal with this
638 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
639 return true;
640 case QRhi::NPOTTextureRepeat:
641 return true;
642 case QRhi::RedOrAlpha8IsRed:
643 return true;
644 case QRhi::ElementIndexUint:
645 return true;
646 case QRhi::Compute:
647 return true;
648 case QRhi::WideLines:
649 return false;
650 case QRhi::VertexShaderPointSize:
651 return false;
652 case QRhi::BaseVertex:
653 return true;
654 case QRhi::BaseInstance:
655 return true;
656 case QRhi::TriangleFanTopology:
657 return false;
658 case QRhi::ReadBackNonUniformBuffer:
659 return true;
660 case QRhi::ReadBackNonBaseMipLevel:
661 return true;
662 case QRhi::TexelFetch:
663 return true;
664 case QRhi::RenderToNonBaseMipLevel:
665 return true;
666 case QRhi::IntAttributes:
667 return true;
668 case QRhi::ScreenSpaceDerivatives:
669 return true;
670 case QRhi::ReadBackAnyTextureFormat:
671 return true;
672 case QRhi::PipelineCacheDataLoadSave:
673 return true;
674 case QRhi::ImageDataStride:
675 return true;
676 case QRhi::RenderBufferImport:
677 return false;
678 case QRhi::ThreeDimensionalTextures:
679 return true;
680 case QRhi::RenderTo3DTextureSlice:
681 return true;
682 case QRhi::TextureArrays:
683 return true;
684 case QRhi::Tessellation:
685 return true;
686 case QRhi::GeometryShader:
687 return true;
688 case QRhi::TextureArrayRange:
689 return true;
690 case QRhi::NonFillPolygonMode:
691 return true;
692 case QRhi::OneDimensionalTextures:
693 return true;
694 case QRhi::OneDimensionalTextureMipmaps:
695 return true;
696 case QRhi::HalfAttributes:
697 return true;
698 case QRhi::RenderToOneDimensionalTexture:
699 return true;
700 case QRhi::ThreeDimensionalTextureMipmaps:
701 return true;
702 case QRhi::MultiView:
703 return false;
704 case QRhi::TextureViewFormat:
705 return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
706 case QRhi::ResolveDepthStencil:
707 return false;
708 case QRhi::VariableRateShading:
709 return false;
710 case QRhi::VariableRateShadingMap:
711 case QRhi::VariableRateShadingMapWithTexture:
712 return false;
713 case QRhi::PerRenderTargetBlending:
714 case QRhi::SampleVariables:
715 return true;
716 case QRhi::InstanceIndexIncludesBaseInstance:
717 return false;
718 case QRhi::DepthClamp:
719 return true;
720 case QRhi::DrawIndirect:
721 return featureLevel >= D3D_FEATURE_LEVEL_11_0;
722 case QRhi::DrawIndirectMulti:
723 return false;
724 default:
725 Q_UNREACHABLE();
726 return false;
727 }
728}
729
730int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
731{
732 switch (limit) {
733 case QRhi::TextureSizeMin:
734 return 1;
735 case QRhi::TextureSizeMax:
736 return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
737 case QRhi::MaxColorAttachments:
738 return 8;
739 case QRhi::FramesInFlight:
740 // From our perspective. What D3D does internally is another question
741 // (there could be pipelining, helped f.ex. by our MAP_DISCARD based
742 // uniform buffer update strategy), but that's out of our hands and
743 // does not concern us here.
744 return 1;
745 case QRhi::MaxAsyncReadbackFrames:
746 return 1;
747 case QRhi::MaxThreadGroupsPerDimension:
748 return D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
749 case QRhi::MaxThreadsPerThreadGroup:
750 return D3D11_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP;
751 case QRhi::MaxThreadGroupX:
752 return D3D11_CS_THREAD_GROUP_MAX_X;
753 case QRhi::MaxThreadGroupY:
754 return D3D11_CS_THREAD_GROUP_MAX_Y;
755 case QRhi::MaxThreadGroupZ:
756 return D3D11_CS_THREAD_GROUP_MAX_Z;
757 case QRhi::TextureArraySizeMax:
758 return D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
759 case QRhi::MaxUniformBufferRange:
760 return 65536;
761 case QRhi::MaxVertexInputs:
763 case QRhi::MaxVertexOutputs:
764 return D3D11_VS_OUTPUT_REGISTER_COUNT;
765 case QRhi::ShadingRateImageTileSize:
766 return 0;
767 default:
768 Q_UNREACHABLE();
769 return 0;
770 }
771}
772
774{
775 return &nativeHandlesStruct;
776}
777
779{
780 return driverInfoStruct;
781}
782
784{
785 QRhiStats result;
786 result.totalPipelineCreationTime = totalPipelineCreationTime();
787 return result;
788}
789
791{
792 // not applicable
793 return false;
794}
795
796void QRhiD3D11::setQueueSubmitParams(QRhiNativeHandles *)
797{
798 // not applicable
799}
800
802{
804 m_bytecodeCache.clear();
805}
806
808{
809 return deviceLost;
810}
811
813{
816 // no need for driver specifics
819};
820
822{
823 QByteArray data;
824 if (m_bytecodeCache.isEmpty())
825 return data;
826
828 memset(&header, 0, sizeof(header));
829 header.rhiId = pipelineCacheRhiId();
830 header.arch = quint32(sizeof(void*));
831 header.count = m_bytecodeCache.count();
832
833 const size_t dataOffset = sizeof(header);
834 size_t dataSize = 0;
835 for (auto it = m_bytecodeCache.cbegin(), end = m_bytecodeCache.cend(); it != end; ++it) {
836 BytecodeCacheKey key = it.key();
837 QByteArray bytecode = it.value();
838 dataSize +=
839 sizeof(quint32) + key.sourceHash.size()
840 + sizeof(quint32) + key.target.size()
841 + sizeof(quint32) + key.entryPoint.size()
842 + sizeof(quint32) // compileFlags
843 + sizeof(quint32) + bytecode.size();
844 }
845
846 QByteArray buf(dataOffset + dataSize, Qt::Uninitialized);
847 char *p = buf.data() + dataOffset;
848 for (auto it = m_bytecodeCache.cbegin(), end = m_bytecodeCache.cend(); it != end; ++it) {
849 BytecodeCacheKey key = it.key();
850 QByteArray bytecode = it.value();
851
852 quint32 i = key.sourceHash.size();
853 memcpy(p, &i, 4);
854 p += 4;
855 memcpy(p, key.sourceHash.constData(), key.sourceHash.size());
856 p += key.sourceHash.size();
857
858 i = key.target.size();
859 memcpy(p, &i, 4);
860 p += 4;
861 memcpy(p, key.target.constData(), key.target.size());
862 p += key.target.size();
863
864 i = key.entryPoint.size();
865 memcpy(p, &i, 4);
866 p += 4;
867 memcpy(p, key.entryPoint.constData(), key.entryPoint.size());
868 p += key.entryPoint.size();
869
870 quint32 f = key.compileFlags;
871 memcpy(p, &f, 4);
872 p += 4;
873
874 i = bytecode.size();
875 memcpy(p, &i, 4);
876 p += 4;
877 memcpy(p, bytecode.constData(), bytecode.size());
878 p += bytecode.size();
879 }
880 Q_ASSERT(p == buf.data() + dataOffset + dataSize);
881
882 header.dataSize = quint32(dataSize);
883 memcpy(buf.data(), &header, sizeof(header));
884
885 return buf;
886}
887
888void QRhiD3D11::setPipelineCacheData(const QByteArray &data)
889{
890 if (data.isEmpty())
891 return;
892
893 const size_t headerSize = sizeof(QD3D11PipelineCacheDataHeader);
894 if (data.size() < qsizetype(headerSize)) {
895 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
896 return;
897 }
898 const size_t dataOffset = headerSize;
900 memcpy(&header, data.constData(), headerSize);
901
902 const quint32 rhiId = pipelineCacheRhiId();
903 if (header.rhiId != rhiId) {
904 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
905 rhiId, header.rhiId);
906 return;
907 }
908 const quint32 arch = quint32(sizeof(void*));
909 if (header.arch != arch) {
910 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
911 arch, header.arch);
912 return;
913 }
914 if (header.count == 0)
915 return;
916
917 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
918 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
919 return;
920 }
921
922 m_bytecodeCache.clear();
923
924 const char *p = data.constData() + dataOffset;
925 for (quint32 i = 0; i < header.count; ++i) {
926 quint32 len = 0;
927 memcpy(&len, p, 4);
928 p += 4;
929 QByteArray sourceHash(len, Qt::Uninitialized);
930 memcpy(sourceHash.data(), p, len);
931 p += len;
932
933 memcpy(&len, p, 4);
934 p += 4;
935 QByteArray target(len, Qt::Uninitialized);
936 memcpy(target.data(), p, len);
937 p += len;
938
939 memcpy(&len, p, 4);
940 p += 4;
941 QByteArray entryPoint(len, Qt::Uninitialized);
942 memcpy(entryPoint.data(), p, len);
943 p += len;
944
945 quint32 flags;
946 memcpy(&flags, p, 4);
947 p += 4;
948
949 memcpy(&len, p, 4);
950 p += 4;
951 QByteArray bytecode(len, Qt::Uninitialized);
952 memcpy(bytecode.data(), p, len);
953 p += len;
954
955 BytecodeCacheKey cacheKey;
956 cacheKey.sourceHash = sourceHash;
957 cacheKey.target = target;
958 cacheKey.entryPoint = entryPoint;
959 cacheKey.compileFlags = flags;
960
961 m_bytecodeCache.insert(cacheKey, bytecode);
962 }
963
964 qCDebug(QRHI_LOG_INFO, "Seeded bytecode cache with %d shaders", int(m_bytecodeCache.count()));
965}
966
967QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
968 int sampleCount, QRhiRenderBuffer::Flags flags,
969 QRhiTexture::Format backingFormatHint)
970{
971 return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
972}
973
974QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format,
975 const QSize &pixelSize, int depth, int arraySize,
976 int sampleCount, QRhiTexture::Flags flags)
977{
978 return new QD3D11Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
979}
980
981QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
982 QRhiSampler::Filter mipmapMode,
983 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
984{
985 return new QD3D11Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
986}
987
988QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
989 QRhiTextureRenderTarget::Flags flags)
990{
991 return new QD3D11TextureRenderTarget(this, desc, flags);
992}
993
994QRhiShadingRateMap *QRhiD3D11::createShadingRateMap()
995{
996 return nullptr;
997}
998
1000{
1001 return new QD3D11GraphicsPipeline(this);
1002}
1003
1005{
1006 return new QD3D11ComputePipeline(this);
1007}
1008
1010{
1011 return new QD3D11ShaderResourceBindings(this);
1012}
1013
1014void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
1015{
1016 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1019 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
1020
1021 if (pipelineChanged) {
1022 cbD->currentGraphicsPipeline = ps;
1023 cbD->currentComputePipeline = nullptr;
1024 cbD->currentPipelineGeneration = psD->generation;
1025
1026 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1028 cmd.args.bindGraphicsPipeline.ps = psD;
1029 }
1030}
1031
1032static const int RBM_SUPPORTED_STAGES = 6;
1033static const int RBM_VERTEX = 0;
1034static const int RBM_HULL = 1;
1035static const int RBM_DOMAIN = 2;
1036static const int RBM_GEOMETRY = 3;
1037static const int RBM_FRAGMENT = 4;
1038static const int RBM_COMPUTE = 5;
1039
1040void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
1041 int dynamicOffsetCount,
1042 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1043{
1044 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1046 QD3D11GraphicsPipeline *gfxPsD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1047 QD3D11ComputePipeline *compPsD = QRHI_RES(QD3D11ComputePipeline, cbD->currentComputePipeline);
1048
1049 if (!srb) {
1050 if (gfxPsD)
1051 srb = gfxPsD->m_shaderResourceBindings;
1052 else
1053 srb = compPsD->m_shaderResourceBindings;
1054 }
1055
1057
1058 bool pipelineChanged = false;
1059 if (gfxPsD) {
1060 pipelineChanged = srbD->lastUsedGraphicsPipeline != gfxPsD;
1061 srbD->lastUsedGraphicsPipeline = gfxPsD;
1062 } else {
1063 pipelineChanged = srbD->lastUsedComputePipeline != compPsD;
1064 srbD->lastUsedComputePipeline = compPsD;
1065 }
1066
1067 bool srbUpdate = false;
1068 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1069 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1070 QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
1071 switch (b->type) {
1072 case QRhiShaderResourceBinding::UniformBuffer:
1073 {
1074 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
1075 // NonDynamicUniformBuffers is not supported by this backend
1076 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic && bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1077 sanityCheckResourceOwnership(bufD);
1078
1080
1081 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1082 srbUpdate = true;
1083 bd.ubuf.id = bufD->m_id;
1084 bd.ubuf.generation = bufD->generation;
1085 }
1086 }
1087 break;
1088 case QRhiShaderResourceBinding::SampledTexture:
1089 case QRhiShaderResourceBinding::Texture:
1090 case QRhiShaderResourceBinding::Sampler:
1091 {
1092 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1093 if (bd.stex.count != data->count) {
1094 bd.stex.count = data->count;
1095 srbUpdate = true;
1096 }
1097 for (int elem = 0; elem < data->count; ++elem) {
1098 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
1099 QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
1100 // We use the same code path for both combined and separate
1101 // images and samplers, so tex or sampler (but not both) can be
1102 // null here.
1103 Q_ASSERT(texD || samplerD);
1104 sanityCheckResourceOwnership(texD);
1105 sanityCheckResourceOwnership(samplerD);
1106 const quint64 texId = texD ? texD->m_id : 0;
1107 const uint texGen = texD ? texD->generation : 0;
1108 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1109 const uint samplerGen = samplerD ? samplerD->generation : 0;
1110 if (texGen != bd.stex.d[elem].texGeneration
1111 || texId != bd.stex.d[elem].texId
1112 || samplerGen != bd.stex.d[elem].samplerGeneration
1113 || samplerId != bd.stex.d[elem].samplerId)
1114 {
1115 srbUpdate = true;
1116 bd.stex.d[elem].texId = texId;
1117 bd.stex.d[elem].texGeneration = texGen;
1118 bd.stex.d[elem].samplerId = samplerId;
1119 bd.stex.d[elem].samplerGeneration = samplerGen;
1120 }
1121 }
1122 }
1123 break;
1124 case QRhiShaderResourceBinding::ImageLoad:
1125 case QRhiShaderResourceBinding::ImageStore:
1126 case QRhiShaderResourceBinding::ImageLoadStore:
1127 {
1128 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
1129 sanityCheckResourceOwnership(texD);
1130 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1131 srbUpdate = true;
1132 bd.simage.id = texD->m_id;
1133 bd.simage.generation = texD->generation;
1134 }
1135 }
1136 break;
1137 case QRhiShaderResourceBinding::BufferLoad:
1138 case QRhiShaderResourceBinding::BufferStore:
1139 case QRhiShaderResourceBinding::BufferLoadStore:
1140 {
1141 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
1142 sanityCheckResourceOwnership(bufD);
1143 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1144 srbUpdate = true;
1145 bd.sbuf.id = bufD->m_id;
1146 bd.sbuf.generation = bufD->generation;
1147 }
1148 }
1149 break;
1150 default:
1151 Q_UNREACHABLE();
1152 break;
1153 }
1154 }
1155
1156 if (srbUpdate || pipelineChanged) {
1157 const QShader::NativeResourceBindingMap *resBindMaps[RBM_SUPPORTED_STAGES];
1158 memset(resBindMaps, 0, sizeof(resBindMaps));
1159 if (gfxPsD) {
1160 resBindMaps[RBM_VERTEX] = &gfxPsD->vs.nativeResourceBindingMap;
1161 resBindMaps[RBM_HULL] = &gfxPsD->hs.nativeResourceBindingMap;
1162 resBindMaps[RBM_DOMAIN] = &gfxPsD->ds.nativeResourceBindingMap;
1163 resBindMaps[RBM_GEOMETRY] = &gfxPsD->gs.nativeResourceBindingMap;
1164 resBindMaps[RBM_FRAGMENT] = &gfxPsD->fs.nativeResourceBindingMap;
1165 } else {
1166 resBindMaps[RBM_COMPUTE] = &compPsD->cs.nativeResourceBindingMap;
1167 }
1168 updateShaderResourceBindings(srbD, resBindMaps);
1169 }
1170
1171 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
1172 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1173
1174 if (pipelineChanged || srbChanged || srbRebuilt || srbUpdate || srbD->hasDynamicOffset) {
1175 if (gfxPsD) {
1176 cbD->currentGraphicsSrb = srb;
1177 cbD->currentComputeSrb = nullptr;
1178 } else {
1179 cbD->currentGraphicsSrb = nullptr;
1180 cbD->currentComputeSrb = srb;
1181 }
1182 cbD->currentSrbGeneration = srbD->generation;
1183
1184 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1186 cmd.args.bindShaderResources.resourceBatchesIndex = cbD->retainResourceBatches(srbD->resourceBatches);
1187 // dynamic offsets have to be applied at the time of executing the bind
1188 // operations, not here
1189 cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbRebuilt && !srbUpdate && srbD->hasDynamicOffset;
1190 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
1191 if (srbD->hasDynamicOffset) {
1192 if (dynamicOffsetCount < QD3D11CommandBuffer::MAX_DYNAMIC_OFFSET_COUNT) {
1193 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
1194 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
1195 for (int i = 0; i < dynamicOffsetCount; ++i) {
1196 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1197 const uint binding = uint(dynOfs.first);
1198 Q_ASSERT(aligned(dynOfs.second, 256u) == dynOfs.second);
1199 const quint32 offsetInConstants = dynOfs.second / 16;
1200 *p++ = binding;
1201 *p++ = offsetInConstants;
1202 }
1203 } else {
1204 qWarning("Too many dynamic offsets (%d, max is %d)",
1206 }
1207 }
1208 }
1209}
1210
1211void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb,
1212 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1213 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1214{
1215 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1217
1218 bool needsBindVBuf = false;
1219 for (int i = 0; i < bindingCount; ++i) {
1220 const int inputSlot = startBinding + i;
1221 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
1222 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
1223 if (bufD->m_type == QRhiBuffer::Dynamic)
1225
1226 if (cbD->currentVertexBuffers[inputSlot] != bufD->buffer
1227 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
1228 {
1229 needsBindVBuf = true;
1230 cbD->currentVertexBuffers[inputSlot] = bufD->buffer;
1231 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
1232 }
1233 }
1234
1235 if (needsBindVBuf) {
1236 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1238 cmd.args.bindVertexBuffers.startSlot = startBinding;
1240 qWarning("Too many vertex buffer bindings (%d, max is %d)",
1243 }
1244 cmd.args.bindVertexBuffers.slotCount = bindingCount;
1245 QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1246 const QRhiVertexInputLayout &inputLayout(psD->m_vertexInputLayout);
1247 const int inputBindingCount = inputLayout.cendBindings() - inputLayout.cbeginBindings();
1248 for (int i = 0, ie = qMin(bindingCount, inputBindingCount); i != ie; ++i) {
1249 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
1250 cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer;
1251 cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second;
1252 cmd.args.bindVertexBuffers.strides[i] = inputLayout.bindingAt(i)->stride();
1253 }
1254 }
1255
1256 if (indexBuf) {
1257 QD3D11Buffer *ibufD = QRHI_RES(QD3D11Buffer, indexBuf);
1258 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
1259 if (ibufD->m_type == QRhiBuffer::Dynamic)
1261
1262 const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
1263 : DXGI_FORMAT_R32_UINT;
1264 if (cbD->currentIndexBuffer != ibufD->buffer
1265 || cbD->currentIndexOffset != indexOffset
1266 || cbD->currentIndexFormat != dxgiFormat)
1267 {
1268 cbD->currentIndexBuffer = ibufD->buffer;
1269 cbD->currentIndexOffset = indexOffset;
1270 cbD->currentIndexFormat = dxgiFormat;
1271
1272 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1274 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
1275 cmd.args.bindIndexBuffer.offset = indexOffset;
1276 cmd.args.bindIndexBuffer.format = dxgiFormat;
1277 }
1278 }
1279}
1280
1281void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
1282{
1283 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1285 Q_ASSERT(cbD->currentTarget);
1286 const QSize outputSize = cbD->currentTarget->pixelSize();
1287
1288 // d3d expects top-left, QRhiViewport is bottom-left
1289 float x, y, w, h;
1290 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1291 return;
1292
1293 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1295 cmd.args.viewport.x = x;
1296 cmd.args.viewport.y = y;
1297 cmd.args.viewport.w = w;
1298 cmd.args.viewport.h = h;
1299 cmd.args.viewport.d0 = viewport.minDepth();
1300 cmd.args.viewport.d1 = viewport.maxDepth();
1301}
1302
1303void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
1304{
1305 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1307 Q_ASSERT(cbD->currentTarget);
1308 const QSize outputSize = cbD->currentTarget->pixelSize();
1309
1310 // d3d expects top-left, QRhiScissor is bottom-left
1311 int x, y, w, h;
1312 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1313 return;
1314
1315 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1317 cmd.args.scissor.x = x;
1318 cmd.args.scissor.y = y;
1319 cmd.args.scissor.w = w;
1320 cmd.args.scissor.h = h;
1321}
1322
1323void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
1324{
1325 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1327
1328 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1330 cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1331 cmd.args.blendConstants.c[0] = float(c.redF());
1332 cmd.args.blendConstants.c[1] = float(c.greenF());
1333 cmd.args.blendConstants.c[2] = float(c.blueF());
1334 cmd.args.blendConstants.c[3] = float(c.alphaF());
1335}
1336
1337void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
1338{
1339 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1341
1342 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1344 cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1345 cmd.args.stencilRef.ref = refValue;
1346}
1347
1348void QRhiD3D11::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
1349{
1350 Q_UNUSED(cb);
1351 Q_UNUSED(coarsePixelSize);
1352}
1353
1354void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
1355 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1356{
1357 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1359
1360 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1362 cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1363 cmd.args.draw.vertexCount = vertexCount;
1364 cmd.args.draw.instanceCount = instanceCount;
1365 cmd.args.draw.firstVertex = firstVertex;
1366 cmd.args.draw.firstInstance = firstInstance;
1367}
1368
1369void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
1370 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
1371{
1372 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1374
1375 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1377 cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1378 cmd.args.drawIndexed.indexCount = indexCount;
1379 cmd.args.drawIndexed.instanceCount = instanceCount;
1380 cmd.args.drawIndexed.firstIndex = firstIndex;
1381 cmd.args.drawIndexed.vertexOffset = vertexOffset;
1382 cmd.args.drawIndexed.firstInstance = firstInstance;
1383}
1384
1385void QRhiD3D11::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
1386 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
1387{
1388 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1390
1391 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1393 cmd.args.drawIndirect.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1394 cmd.args.drawIndirect.indirectBuffer = QRHI_RES(QD3D11Buffer, indirectBuffer);
1395 cmd.args.drawIndirect.indirectBufferOffset = indirectBufferOffset;
1396 cmd.args.drawIndirect.drawCount = drawCount;
1397 cmd.args.drawIndirect.stride = stride;
1398}
1399
1400void QRhiD3D11::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
1401 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
1402{
1403 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1405
1406 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1408 cmd.args.drawIndexedIndirect.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
1409 cmd.args.drawIndexedIndirect.indirectBuffer = QRHI_RES(QD3D11Buffer, indirectBuffer);
1410 cmd.args.drawIndexedIndirect.indirectBufferOffset = indirectBufferOffset;
1411 cmd.args.drawIndexedIndirect.drawCount = drawCount;
1412 cmd.args.drawIndexedIndirect.stride = stride;
1413}
1414
1415void QRhiD3D11::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
1416{
1417 if (!debugMarkers || !annotations)
1418 return;
1419
1420 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1421 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1423 qstrncpy(cmd.args.debugMark.s, name.constData(), sizeof(cmd.args.debugMark.s));
1424}
1425
1426void QRhiD3D11::debugMarkEnd(QRhiCommandBuffer *cb)
1427{
1428 if (!debugMarkers || !annotations)
1429 return;
1430
1431 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1432 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1434}
1435
1436void QRhiD3D11::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
1437{
1438 if (!debugMarkers || !annotations)
1439 return;
1440
1441 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1442 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1444 qstrncpy(cmd.args.debugMark.s, msg.constData(), sizeof(cmd.args.debugMark.s));
1445}
1446
1447const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
1448{
1449 Q_UNUSED(cb);
1450 return nullptr;
1451}
1452
1453void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
1454{
1455 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1458}
1459
1460void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
1461{
1462 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1463 Q_ASSERT(cbD->commands.isEmpty());
1465 if (cbD->currentTarget) { // could be compute, no rendertarget then
1466 QD3D11CommandBuffer::Command &fbCmd(cbD->commands.get());
1468 fbCmd.args.setRenderTarget.rt = cbD->currentTarget;
1469 }
1470}
1471
1472double QRhiD3D11::lastCompletedGpuTime(QRhiCommandBuffer *cb)
1473{
1474 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1475 return cbD->lastGpuTime;
1476}
1477
1478static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
1479{
1480 switch (rt->resourceType()) {
1481 case QRhiResource::SwapChainRenderTarget:
1482 return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d;
1483 case QRhiResource::TextureRenderTarget:
1484 return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d;
1485 default:
1486 Q_UNREACHABLE();
1487 return nullptr;
1488 }
1489}
1490
1491QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1492{
1493 Q_UNUSED(flags);
1494
1495 QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
1496 contextState.currentSwapChain = swapChainD;
1497 const int currentFrameSlot = swapChainD->currentFrameSlot;
1498
1499 // if we have a waitable object, now is the time to wait on it
1500 if (swapChainD->frameLatencyWaitableObject) {
1501 // only wait when endFrame() called Present(), otherwise this would become a 1 sec timeout
1502 if (swapChainD->lastFrameLatencyWaitSlot != currentFrameSlot) {
1503 WaitForSingleObjectEx(swapChainD->frameLatencyWaitableObject, 1000, true);
1504 swapChainD->lastFrameLatencyWaitSlot = currentFrameSlot;
1505 }
1506 }
1507
1508 swapChainD->cb.resetState();
1509
1510 swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
1511 swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
1512 swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
1513
1515
1516 if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
1517 double elapsedSec = 0;
1518 if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, context, &elapsedSec))
1519 swapChainD->cb.lastGpuTime = elapsedSec;
1520 }
1521
1522 ID3D11Query *tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
1523 ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex];
1524 const bool recordTimestamps = tsStart && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
1525
1526 QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get());
1528 cmd.args.beginFrame.tsQuery = recordTimestamps ? tsStart : nullptr;
1529 cmd.args.beginFrame.tsDisjointQuery = recordTimestamps ? tsDisjoint : nullptr;
1530 cmd.args.beginFrame.swapchainData = rtData(&swapChainD->rt);
1531
1532 QDxgiVSyncService::instance()->beginFrame(adapterLuid);
1533
1534 return QRhi::FrameOpSuccess;
1535}
1536
1537QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1538{
1539 QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
1540 Q_ASSERT(contextState.currentSwapChain = swapChainD);
1541 const int currentFrameSlot = swapChainD->currentFrameSlot;
1542
1543 QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get());
1545 cmd.args.endFrame.tsQuery = nullptr; // done later manually, see below
1546 cmd.args.endFrame.tsDisjointQuery = nullptr;
1547
1548 // send all commands to the context
1549 executeCommandBuffer(&swapChainD->cb);
1550
1551 if (swapChainD->sampleDesc.Count > 1) {
1552 context->ResolveSubresource(swapChainD->backBufferTex, 0,
1553 swapChainD->msaaTex[currentFrameSlot], 0,
1554 swapChainD->colorFormat);
1555 }
1556
1557 // this is here because we want to include the time spent on the ResolveSubresource as well
1558 ID3D11Query *tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
1559 ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex];
1560 const bool recordTimestamps = tsEnd && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
1561 if (recordTimestamps) {
1562 context->End(tsEnd);
1563 context->End(tsDisjoint);
1564 swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
1566 }
1567
1568 if (!flags.testFlag(QRhi::SkipPresent)) {
1569 UINT presentFlags = 0;
1570 if (swapChainD->swapInterval == 0 && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
1571 presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
1572 if (!swapChainD->swapChain) {
1573 qWarning("Failed to present: IDXGISwapChain is unavailable");
1574 return QRhi::FrameOpError;
1575 }
1576 HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
1577 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
1578 qWarning("Device loss detected in Present()");
1579 deviceLost = true;
1580 return QRhi::FrameOpDeviceLost;
1581 } else if (FAILED(hr)) {
1582 qWarning("Failed to present: %s",
1583 qPrintable(QSystemError::windowsComString(hr)));
1584 return QRhi::FrameOpError;
1585 }
1586
1587 if (dcompDevice && swapChainD->dcompTarget && swapChainD->dcompVisual)
1588 dcompDevice->Commit();
1589
1590 // move on to the next buffer
1592 } else {
1593 context->Flush();
1594 }
1595
1596 swapChainD->frameCount += 1;
1597 contextState.currentSwapChain = nullptr;
1598
1599 return QRhi::FrameOpSuccess;
1600}
1601
1602QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
1603{
1604 Q_UNUSED(flags);
1605 ofr.active = true;
1606
1607 ofr.cbWrapper.resetState();
1608 *cb = &ofr.cbWrapper;
1609
1610 if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
1611 D3D11_QUERY_DESC queryDesc = {};
1612 if (!ofr.tsDisjointQuery) {
1613 queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
1614 HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsDisjointQuery);
1615 if (FAILED(hr)) {
1616 qWarning("Failed to create timestamp disjoint query: %s",
1617 qPrintable(QSystemError::windowsComString(hr)));
1618 return QRhi::FrameOpError;
1619 }
1620 }
1621 queryDesc.Query = D3D11_QUERY_TIMESTAMP;
1622 for (int i = 0; i < 2; ++i) {
1623 if (!ofr.tsQueries[i]) {
1624 HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsQueries[i]);
1625 if (FAILED(hr)) {
1626 qWarning("Failed to create timestamp query: %s",
1627 qPrintable(QSystemError::windowsComString(hr)));
1628 return QRhi::FrameOpError;
1629 }
1630 }
1631 }
1632 }
1633
1634 QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get());
1636 cmd.args.beginFrame.tsQuery = ofr.tsQueries[0] ? ofr.tsQueries[0] : nullptr;
1637 cmd.args.beginFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr;
1638 cmd.args.beginFrame.swapchainData = nullptr;
1639
1640 return QRhi::FrameOpSuccess;
1641}
1642
1643QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
1644{
1645 Q_UNUSED(flags);
1646 ofr.active = false;
1647
1648 QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get());
1650 cmd.args.endFrame.tsQuery = ofr.tsQueries[1] ? ofr.tsQueries[1] : nullptr;
1651 cmd.args.endFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr;
1652
1653 executeCommandBuffer(&ofr.cbWrapper);
1654 context->Flush();
1655
1657
1658 if (ofr.tsQueries[0]) {
1659 quint64 timestamps[2];
1660 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
1661 HRESULT hr;
1662 bool ok = true;
1663 do {
1664 hr = context->GetData(ofr.tsDisjointQuery, &dj, sizeof(dj), 0);
1665 } while (hr == S_FALSE);
1666 ok &= hr == S_OK;
1667 do {
1668 hr = context->GetData(ofr.tsQueries[1], &timestamps[1], sizeof(quint64), 0);
1669 } while (hr == S_FALSE);
1670 ok &= hr == S_OK;
1671 do {
1672 hr = context->GetData(ofr.tsQueries[0], &timestamps[0], sizeof(quint64), 0);
1673 } while (hr == S_FALSE);
1674 ok &= hr == S_OK;
1675 if (ok) {
1676 if (!dj.Disjoint && dj.Frequency) {
1677 const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
1678 ofr.cbWrapper.lastGpuTime = elapsedMs / 1000.0;
1679 }
1680 }
1681 }
1682
1683 return QRhi::FrameOpSuccess;
1684}
1685
1686static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
1687{
1688 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1689 switch (format) {
1690 case QRhiTexture::RGBA8:
1691 return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
1692 case QRhiTexture::BGRA8:
1693 return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
1694 case QRhiTexture::R8:
1695 return DXGI_FORMAT_R8_UNORM;
1696 case QRhiTexture::R8SI:
1697 return DXGI_FORMAT_R8_SINT;
1698 case QRhiTexture::R8UI:
1699 return DXGI_FORMAT_R8_UINT;
1700 case QRhiTexture::RG8:
1701 return DXGI_FORMAT_R8G8_UNORM;
1702 case QRhiTexture::R16:
1703 return DXGI_FORMAT_R16_UNORM;
1704 case QRhiTexture::RG16:
1705 return DXGI_FORMAT_R16G16_UNORM;
1706 case QRhiTexture::RED_OR_ALPHA8:
1707 return DXGI_FORMAT_R8_UNORM;
1708
1709 case QRhiTexture::RGBA16F:
1710 return DXGI_FORMAT_R16G16B16A16_FLOAT;
1711 case QRhiTexture::RGBA32F:
1712 return DXGI_FORMAT_R32G32B32A32_FLOAT;
1713 case QRhiTexture::R16F:
1714 return DXGI_FORMAT_R16_FLOAT;
1715 case QRhiTexture::R32F:
1716 return DXGI_FORMAT_R32_FLOAT;
1717
1718 case QRhiTexture::RGB10A2:
1719 return DXGI_FORMAT_R10G10B10A2_UNORM;
1720
1721 case QRhiTexture::R32SI:
1722 return DXGI_FORMAT_R32_SINT;
1723 case QRhiTexture::R32UI:
1724 return DXGI_FORMAT_R32_UINT;
1725 case QRhiTexture::RG32SI:
1726 return DXGI_FORMAT_R32G32_SINT;
1727 case QRhiTexture::RG32UI:
1728 return DXGI_FORMAT_R32G32_UINT;
1729 case QRhiTexture::RGBA32SI:
1730 return DXGI_FORMAT_R32G32B32A32_SINT;
1731 case QRhiTexture::RGBA32UI:
1732 return DXGI_FORMAT_R32G32B32A32_UINT;
1733
1734 case QRhiTexture::D16:
1735 return DXGI_FORMAT_R16_TYPELESS;
1736 case QRhiTexture::D24:
1737 return DXGI_FORMAT_R24G8_TYPELESS;
1738 case QRhiTexture::D24S8:
1739 return DXGI_FORMAT_R24G8_TYPELESS;
1740 case QRhiTexture::D32F:
1741 return DXGI_FORMAT_R32_TYPELESS;
1742 case QRhiTexture::D32FS8:
1743 return DXGI_FORMAT_R32G8X24_TYPELESS;
1744
1745 case QRhiTexture::BC1:
1746 return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
1747 case QRhiTexture::BC2:
1748 return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
1749 case QRhiTexture::BC3:
1750 return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
1751 case QRhiTexture::BC4:
1752 return DXGI_FORMAT_BC4_UNORM;
1753 case QRhiTexture::BC5:
1754 return DXGI_FORMAT_BC5_UNORM;
1755 case QRhiTexture::BC6H:
1756 return DXGI_FORMAT_BC6H_UF16;
1757 case QRhiTexture::BC7:
1758 return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
1759
1760 case QRhiTexture::ETC2_RGB8:
1761 case QRhiTexture::ETC2_RGB8A1:
1762 case QRhiTexture::ETC2_RGBA8:
1763 qWarning("QRhiD3D11 does not support ETC2 textures");
1764 return DXGI_FORMAT_R8G8B8A8_UNORM;
1765
1766 case QRhiTexture::ASTC_4x4:
1767 case QRhiTexture::ASTC_5x4:
1768 case QRhiTexture::ASTC_5x5:
1769 case QRhiTexture::ASTC_6x5:
1770 case QRhiTexture::ASTC_6x6:
1771 case QRhiTexture::ASTC_8x5:
1772 case QRhiTexture::ASTC_8x6:
1773 case QRhiTexture::ASTC_8x8:
1774 case QRhiTexture::ASTC_10x5:
1775 case QRhiTexture::ASTC_10x6:
1776 case QRhiTexture::ASTC_10x8:
1777 case QRhiTexture::ASTC_10x10:
1778 case QRhiTexture::ASTC_12x10:
1779 case QRhiTexture::ASTC_12x12:
1780 qWarning("QRhiD3D11 does not support ASTC textures");
1781 return DXGI_FORMAT_R8G8B8A8_UNORM;
1782
1783 default:
1784 Q_UNREACHABLE();
1785 return DXGI_FORMAT_R8G8B8A8_UNORM;
1786 }
1787}
1788
1789static inline QRhiTexture::Format swapchainReadbackTextureFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
1790{
1791 switch (format) {
1792 case DXGI_FORMAT_R8G8B8A8_UNORM:
1793 return QRhiTexture::RGBA8;
1794 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
1795 if (flags)
1796 (*flags) |= QRhiTexture::sRGB;
1797 return QRhiTexture::RGBA8;
1798 case DXGI_FORMAT_B8G8R8A8_UNORM:
1799 return QRhiTexture::BGRA8;
1800 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
1801 if (flags)
1802 (*flags) |= QRhiTexture::sRGB;
1803 return QRhiTexture::BGRA8;
1804 case DXGI_FORMAT_R16G16B16A16_FLOAT:
1805 return QRhiTexture::RGBA16F;
1806 case DXGI_FORMAT_R32G32B32A32_FLOAT:
1807 return QRhiTexture::RGBA32F;
1808 case DXGI_FORMAT_R10G10B10A2_UNORM:
1809 return QRhiTexture::RGB10A2;
1810 default:
1811 qWarning("DXGI_FORMAT %d cannot be read back", format);
1812 break;
1813 }
1814 return QRhiTexture::UnknownFormat;
1815}
1816
1817static inline bool isDepthTextureFormat(QRhiTexture::Format format)
1818{
1819 switch (format) {
1820 case QRhiTexture::Format::D16:
1821 case QRhiTexture::Format::D24:
1822 case QRhiTexture::Format::D24S8:
1823 case QRhiTexture::Format::D32F:
1824 case QRhiTexture::Format::D32FS8:
1825 return true;
1826
1827 default:
1828 return false;
1829 }
1830}
1831
1833{
1834 if (inFrame) {
1835 if (ofr.active) {
1836 Q_ASSERT(!contextState.currentSwapChain);
1837 Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass);
1838 executeCommandBuffer(&ofr.cbWrapper);
1839 ofr.cbWrapper.resetCommands();
1840 } else {
1841 Q_ASSERT(contextState.currentSwapChain);
1842 Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
1844 contextState.currentSwapChain->cb.resetCommands();
1845 }
1846 }
1847
1849
1850 return QRhi::FrameOpSuccess;
1851}
1852
1854 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
1855{
1856 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
1857 UINT subres = D3D11CalcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount);
1858 D3D11_BOX box;
1859 box.front = is3D ? UINT(layer) : 0u;
1860 // back, right, bottom are exclusive
1861 box.back = box.front + 1;
1862 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1864 cmd.args.updateSubRes.dst = texD->textureResource();
1865 cmd.args.updateSubRes.dstSubRes = subres;
1866
1867 const QPoint dp = subresDesc.destinationTopLeft();
1868 if (!subresDesc.image().isNull()) {
1869 QImage img = subresDesc.image();
1870 QSize size = img.size();
1871 int bpl = img.bytesPerLine();
1872 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
1873 const QPoint sp = subresDesc.sourceTopLeft();
1874 if (!subresDesc.sourceSize().isEmpty())
1875 size = subresDesc.sourceSize();
1876 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
1877 if (img.depth() == 32) {
1878 const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4;
1879 cmd.args.updateSubRes.src = cbD->retainImage(img) + offset;
1880 } else {
1881 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
1882 bpl = img.bytesPerLine();
1883 cmd.args.updateSubRes.src = cbD->retainImage(img);
1884 }
1885 } else {
1886 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
1887 cmd.args.updateSubRes.src = cbD->retainImage(img);
1888 }
1889 box.left = UINT(dp.x());
1890 box.top = UINT(dp.y());
1891 box.right = UINT(dp.x() + size.width());
1892 box.bottom = UINT(dp.y() + size.height());
1893 cmd.args.updateSubRes.hasDstBox = true;
1894 cmd.args.updateSubRes.dstBox = box;
1895 cmd.args.updateSubRes.srcRowPitch = UINT(bpl);
1896 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
1897 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1898 : subresDesc.sourceSize();
1899 quint32 bpl = 0;
1900 QSize blockDim;
1901 compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim);
1902 // Everything must be a multiple of the block width and
1903 // height, so e.g. a mip level of size 2x2 will be 4x4 when it
1904 // comes to the actual data.
1905 box.left = UINT(aligned(dp.x(), blockDim.width()));
1906 box.top = UINT(aligned(dp.y(), blockDim.height()));
1907 box.right = UINT(aligned(dp.x() + size.width(), blockDim.width()));
1908 box.bottom = UINT(aligned(dp.y() + size.height(), blockDim.height()));
1909 cmd.args.updateSubRes.hasDstBox = true;
1910 cmd.args.updateSubRes.dstBox = box;
1911 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1912 cmd.args.updateSubRes.srcRowPitch = bpl;
1913 } else if (!subresDesc.data().isEmpty()) {
1914 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1915 : subresDesc.sourceSize();
1916 quint32 bpl = 0;
1917 if (subresDesc.dataStride())
1918 bpl = subresDesc.dataStride();
1919 else
1920 textureFormatInfo(texD->m_format, size, &bpl, nullptr, nullptr);
1921 box.left = UINT(dp.x());
1922 box.top = UINT(dp.y());
1923 box.right = UINT(dp.x() + size.width());
1924 box.bottom = UINT(dp.y() + size.height());
1925 cmd.args.updateSubRes.hasDstBox = true;
1926 cmd.args.updateSubRes.dstBox = box;
1927 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1928 cmd.args.updateSubRes.srcRowPitch = bpl;
1929 } else {
1930 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1931 cbD->commands.unget();
1932 }
1933}
1934
1935void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1936{
1937 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1939
1940 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
1941 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
1943 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1944 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1945 memcpy(bufD->dynBuf + u.offset, u.data.constData(), size_t(u.data.size()));
1946 bufD->hasPendingDynamicUpdates = true;
1948 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1949 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1950 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
1951 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1953 cmd.args.updateSubRes.dst = bufD->buffer;
1954 cmd.args.updateSubRes.dstSubRes = 0;
1955 cmd.args.updateSubRes.src = cbD->retainBufferData(u.data);
1956 cmd.args.updateSubRes.srcRowPitch = 0;
1957 // Specify the region (even when offset is 0 and all data is provided)
1958 // since the ID3D11Buffer's size is rounded up to be a multiple of 256
1959 // while the data we have has the original size.
1960 D3D11_BOX box;
1961 box.left = u.offset;
1962 box.top = box.front = 0;
1963 box.back = box.bottom = 1;
1964 box.right = u.offset + u.data.size(); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
1965 cmd.args.updateSubRes.hasDstBox = true;
1966 cmd.args.updateSubRes.dstBox = box;
1968 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1969 if (bufD->m_type == QRhiBuffer::Dynamic) {
1970 u.result->data.resize(u.readSize);
1971 memcpy(u.result->data.data(), bufD->dynBuf + u.offset, size_t(u.readSize));
1972 if (u.result->completed)
1973 u.result->completed();
1974 } else {
1975 BufferReadback readback;
1976 readback.result = u.result;
1977 readback.byteSize = u.readSize;
1978
1979 D3D11_BUFFER_DESC desc = {};
1980 desc.ByteWidth = readback.byteSize;
1981 desc.Usage = D3D11_USAGE_STAGING;
1982 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1983 HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf);
1984 if (FAILED(hr)) {
1985 qWarning("Failed to create buffer: %s",
1986 qPrintable(QSystemError::windowsComString(hr)));
1987 continue;
1988 }
1989
1990 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1992 cmd.args.copySubRes.dst = readback.stagingBuf;
1993 cmd.args.copySubRes.dstSubRes = 0;
1994 cmd.args.copySubRes.dstX = 0;
1995 cmd.args.copySubRes.dstY = 0;
1996 cmd.args.copySubRes.dstZ = 0;
1997 cmd.args.copySubRes.src = bufD->buffer;
1998 cmd.args.copySubRes.srcSubRes = 0;
1999 cmd.args.copySubRes.hasSrcBox = true;
2000 D3D11_BOX box;
2001 box.left = u.offset;
2002 box.top = box.front = 0;
2003 box.back = box.bottom = 1;
2004 box.right = u.offset + u.readSize;
2005 cmd.args.copySubRes.srcBox = box;
2006
2007 activeBufferReadbacks.append(readback);
2008 }
2009 }
2010 }
2011 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2012 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2014 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
2015 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2016 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2017 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2018 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
2019 }
2020 }
2022 Q_ASSERT(u.src && u.dst);
2023 QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
2024 QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
2025 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2026 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2027 UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), srcIs3D ? 0u : UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
2028 UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), dstIs3D ? 0u : UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
2029 const QPoint dp = u.desc.destinationTopLeft();
2030 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2031 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2032 const QPoint sp = u.desc.sourceTopLeft();
2033 D3D11_BOX srcBox;
2034 srcBox.left = UINT(sp.x());
2035 srcBox.top = UINT(sp.y());
2036 srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
2037 // back, right, bottom are exclusive
2038 srcBox.right = srcBox.left + UINT(copySize.width());
2039 srcBox.bottom = srcBox.top + UINT(copySize.height());
2040 srcBox.back = srcBox.front + 1;
2041 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2043 cmd.args.copySubRes.dst = dstD->textureResource();
2044 cmd.args.copySubRes.dstSubRes = dstSubRes;
2045 cmd.args.copySubRes.dstX = UINT(dp.x());
2046 cmd.args.copySubRes.dstY = UINT(dp.y());
2047 cmd.args.copySubRes.dstZ = dstIs3D ? UINT(u.desc.destinationLayer()) : 0u;
2048 cmd.args.copySubRes.src = srcD->textureResource();
2049 cmd.args.copySubRes.srcSubRes = srcSubRes;
2050 cmd.args.copySubRes.hasSrcBox = true;
2051 cmd.args.copySubRes.srcBox = srcBox;
2053 TextureReadback readback;
2054 readback.desc = u.rb;
2055 readback.result = u.result;
2056
2057 ID3D11Resource *src;
2058 DXGI_FORMAT dxgiFormat;
2059 QRect rect;
2060 QRhiTexture::Format format;
2061 UINT subres = 0;
2062 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.rb.texture());
2063 QD3D11SwapChain *swapChainD = nullptr;
2064 bool is3D = false;
2065
2066 if (texD) {
2067 if (texD->sampleDesc.Count > 1) {
2068 qWarning("Multisample texture cannot be read back");
2069 continue;
2070 }
2071 src = texD->textureResource();
2072 dxgiFormat = texD->dxgiFormat;
2073 if (u.rb.rect().isValid())
2074 rect = u.rb.rect();
2075 else
2076 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2077 format = texD->m_format;
2078 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2079 subres = D3D11CalcSubresource(UINT(u.rb.level()), UINT(is3D ? 0 : u.rb.layer()), texD->mipLevelCount);
2080 } else {
2081 Q_ASSERT(contextState.currentSwapChain);
2082 swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
2083 if (swapChainD->sampleDesc.Count > 1) {
2084 // Unlike with textures, reading back a multisample swapchain image
2085 // has to be supported. Insert a resolve.
2086 QD3D11CommandBuffer::Command &rcmd(cbD->commands.get());
2088 rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
2089 rcmd.args.resolveSubRes.dstSubRes = 0;
2090 rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
2091 rcmd.args.resolveSubRes.srcSubRes = 0;
2092 rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
2093 }
2094 src = swapChainD->backBufferTex;
2095 dxgiFormat = swapChainD->colorFormat;
2096 if (u.rb.rect().isValid())
2097 rect = u.rb.rect();
2098 else
2099 rect = QRect({0, 0}, swapChainD->pixelSize);
2100 format = swapchainReadbackTextureFormat(dxgiFormat, nullptr);
2101 if (format == QRhiTexture::UnknownFormat)
2102 continue;
2103 }
2104 quint32 byteSize = 0;
2105 quint32 bpl = 0;
2106 textureFormatInfo(format, rect.size(), &bpl, &byteSize, nullptr);
2107
2108 D3D11_TEXTURE2D_DESC desc = {};
2109 desc.Width = UINT(rect.width());
2110 desc.Height = UINT(rect.height());
2111 desc.MipLevels = 1;
2112 desc.ArraySize = 1;
2113 desc.Format = dxgiFormat;
2114 desc.SampleDesc.Count = 1;
2115 desc.Usage = D3D11_USAGE_STAGING;
2116 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
2117 ID3D11Texture2D *stagingTex;
2118 HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
2119 if (FAILED(hr)) {
2120 qWarning("Failed to create readback staging texture: %s",
2121 qPrintable(QSystemError::windowsComString(hr)));
2122 return;
2123 }
2124
2125 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2127 cmd.args.copySubRes.dst = stagingTex;
2128 cmd.args.copySubRes.dstSubRes = 0;
2129 cmd.args.copySubRes.dstX = 0;
2130 cmd.args.copySubRes.dstY = 0;
2131 cmd.args.copySubRes.dstZ = 0;
2132 cmd.args.copySubRes.src = src;
2133 cmd.args.copySubRes.srcSubRes = subres;
2134
2135 D3D11_BOX srcBox = {};
2136 srcBox.left = UINT(rect.left());
2137 srcBox.top = UINT(rect.top());
2138 srcBox.front = is3D ? UINT(u.rb.layer()) : 0u;
2139 // back, right, bottom are exclusive
2140 srcBox.right = srcBox.left + desc.Width;
2141 srcBox.bottom = srcBox.top + desc.Height;
2142 srcBox.back = srcBox.front + 1;
2143 cmd.args.copySubRes.hasSrcBox = true;
2144 cmd.args.copySubRes.srcBox = srcBox;
2145
2146 readback.stagingTex = stagingTex;
2147 readback.byteSize = byteSize;
2148 readback.bpl = bpl;
2149 readback.pixelSize = rect.size();
2150 readback.format = format;
2151
2152 activeTextureReadbacks.append(readback);
2154 Q_ASSERT(u.dst->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
2155 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2157 cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.dst)->srv;
2158 }
2159 }
2160
2161 ud->free();
2162}
2163
2165{
2166 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
2167
2168 for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
2169 const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]);
2170 readback.result->format = readback.format;
2171 readback.result->pixelSize = readback.pixelSize;
2172
2173 D3D11_MAPPED_SUBRESOURCE mp;
2174 HRESULT hr = context->Map(readback.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
2175 if (SUCCEEDED(hr)) {
2176 readback.result->data.resize(int(readback.byteSize));
2177 // nothing says the rows are tightly packed in the texture, must take
2178 // the stride into account
2179 char *dst = readback.result->data.data();
2180 char *src = static_cast<char *>(mp.pData);
2181 for (int y = 0, h = readback.pixelSize.height(); y != h; ++y) {
2182 memcpy(dst, src, readback.bpl);
2183 dst += readback.bpl;
2184 src += mp.RowPitch;
2185 }
2186 context->Unmap(readback.stagingTex, 0);
2187 } else {
2188 qWarning("Failed to map readback staging texture: %s",
2189 qPrintable(QSystemError::windowsComString(hr)));
2190 }
2191
2192 readback.stagingTex->Release();
2193
2194 if (readback.result->completed)
2195 completedCallbacks.append(readback.result->completed);
2196
2197 activeTextureReadbacks.removeLast();
2198 }
2199
2200 for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) {
2201 const QRhiD3D11::BufferReadback &readback(activeBufferReadbacks[i]);
2202
2203 D3D11_MAPPED_SUBRESOURCE mp;
2204 HRESULT hr = context->Map(readback.stagingBuf, 0, D3D11_MAP_READ, 0, &mp);
2205 if (SUCCEEDED(hr)) {
2206 readback.result->data.resize(int(readback.byteSize));
2207 memcpy(readback.result->data.data(), mp.pData, readback.byteSize);
2208 context->Unmap(readback.stagingBuf, 0);
2209 } else {
2210 qWarning("Failed to map readback staging texture: %s",
2211 qPrintable(QSystemError::windowsComString(hr)));
2212 }
2213
2214 readback.stagingBuf->Release();
2215
2216 if (readback.result->completed)
2217 completedCallbacks.append(readback.result->completed);
2218
2219 activeBufferReadbacks.removeLast();
2220 }
2221
2222 for (auto f : completedCallbacks)
2223 f();
2224}
2225
2226void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2227{
2228 Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass);
2229
2230 enqueueResourceUpdates(cb, resourceUpdates);
2231}
2232
2233void QRhiD3D11::beginPass(QRhiCommandBuffer *cb,
2234 QRhiRenderTarget *rt,
2235 const QColor &colorClearValue,
2236 const QRhiDepthStencilClearValue &depthStencilClearValue,
2237 QRhiResourceUpdateBatch *resourceUpdates,
2239{
2240 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2242
2243 if (resourceUpdates)
2244 enqueueResourceUpdates(cb, resourceUpdates);
2245
2246 bool wantsColorClear = true;
2247 bool wantsDsClear = true;
2249 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
2251 wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
2252 wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
2253 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D11Texture, QD3D11RenderBuffer>(rtTex->description(), rtD->currentResIdList))
2254 rtTex->create();
2255 }
2256
2258
2259 QD3D11CommandBuffer::Command &fbCmd(cbD->commands.get());
2261 fbCmd.args.setRenderTarget.rt = rt;
2262
2263 QD3D11CommandBuffer::Command &clearCmd(cbD->commands.get());
2265 clearCmd.args.clear.rt = rt;
2266 clearCmd.args.clear.mask = 0;
2267 if (rtD->colorAttCount && wantsColorClear)
2268 clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color;
2269 if (rtD->dsAttCount && wantsDsClear)
2271
2272 clearCmd.args.clear.c[0] = colorClearValue.redF();
2273 clearCmd.args.clear.c[1] = colorClearValue.greenF();
2274 clearCmd.args.clear.c[2] = colorClearValue.blueF();
2275 clearCmd.args.clear.c[3] = colorClearValue.alphaF();
2276 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
2277 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
2278
2280 cbD->currentTarget = rt;
2281
2283}
2284
2285void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2286{
2287 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2289
2290 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
2291 QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
2292 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2293 it != itEnd; ++it)
2294 {
2295 const QRhiColorAttachment &colorAtt(*it);
2296 if (!colorAtt.resolveTexture())
2297 continue;
2298
2299 QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture());
2300 QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture());
2301 QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer());
2302 Q_ASSERT(srcTexD || srcRbD);
2303 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2305 cmd.args.resolveSubRes.dst = dstTexD->textureResource();
2306 cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()),
2307 UINT(colorAtt.resolveLayer()),
2308 dstTexD->mipLevelCount);
2309 if (srcTexD) {
2310 cmd.args.resolveSubRes.src = srcTexD->textureResource();
2311 if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
2312 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2313 int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
2314 cbD->commands.unget();
2315 continue;
2316 }
2317 if (srcTexD->sampleDesc.Count <= 1) {
2318 qWarning("Cannot resolve a non-multisample texture");
2319 cbD->commands.unget();
2320 continue;
2321 }
2322 if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
2323 qWarning("Resolve source and destination sizes do not match");
2324 cbD->commands.unget();
2325 continue;
2326 }
2327 } else {
2328 cmd.args.resolveSubRes.src = srcRbD->tex;
2329 if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
2330 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2331 int(srcRbD->dxgiFormat), int(dstTexD->dxgiFormat));
2332 cbD->commands.unget();
2333 continue;
2334 }
2335 if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
2336 qWarning("Resolve source and destination sizes do not match");
2337 cbD->commands.unget();
2338 continue;
2339 }
2340 }
2341 cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1);
2342 cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
2343 }
2344 if (rtTex->m_desc.depthResolveTexture())
2345 qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
2346 }
2347
2349 cbD->currentTarget = nullptr;
2350
2351 if (resourceUpdates)
2352 enqueueResourceUpdates(cb, resourceUpdates);
2353}
2354
2355void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb,
2356 QRhiResourceUpdateBatch *resourceUpdates,
2358{
2359 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2361
2362 if (resourceUpdates)
2363 enqueueResourceUpdates(cb, resourceUpdates);
2364
2365 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2367
2369
2371}
2372
2373void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2374{
2375 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2377
2379
2380 if (resourceUpdates)
2381 enqueueResourceUpdates(cb, resourceUpdates);
2382}
2383
2384void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
2385{
2386 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2389 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
2390
2391 if (pipelineChanged) {
2392 cbD->currentGraphicsPipeline = nullptr;
2393 cbD->currentComputePipeline = psD;
2394 cbD->currentPipelineGeneration = psD->generation;
2395
2396 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2398 cmd.args.bindComputePipeline.ps = psD;
2399 }
2400}
2401
2402void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
2403{
2404 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2406
2407 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2409 cmd.args.dispatch.x = UINT(x);
2410 cmd.args.dispatch.y = UINT(y);
2411 cmd.args.dispatch.z = UINT(z);
2412}
2413
2414static inline std::pair<int, int> mapBinding(int binding,
2415 int stageIndex,
2416 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
2417{
2418 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
2419 if (!map || map->isEmpty())
2420 return { binding, binding }; // assume 1:1 mapping
2421
2422 auto it = map->constFind(binding);
2423 if (it != map->cend())
2424 return *it;
2425
2426 // Hitting this path is normal too. It is not given that the resource is
2427 // present in the shaders for all the stages specified by the visibility
2428 // mask in the QRhiShaderResourceBinding.
2429 return { -1, -1 };
2430}
2431
2433 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
2434{
2435 srbD->resourceBatches.clear();
2436
2437 struct Stage {
2438 struct Buffer {
2439 int binding; // stored and sent along in XXorigbindings just for applyDynamicOffsets()
2440 int breg; // b0, b1, ...
2441 ID3D11Buffer *buffer;
2442 uint offsetInConstants;
2443 uint sizeInConstants;
2444 };
2445 struct Texture {
2446 int treg; // t0, t1, ...
2447 ID3D11ShaderResourceView *srv;
2448 };
2449 struct Sampler {
2450 int sreg; // s0, s1, ...
2451 ID3D11SamplerState *sampler;
2452 };
2453 struct Uav {
2454 int ureg;
2455 ID3D11UnorderedAccessView *uav;
2456 };
2457 QVarLengthArray<Buffer, 8> buffers;
2458 QVarLengthArray<Texture, 8> textures;
2459 QVarLengthArray<Sampler, 8> samplers;
2460 QVarLengthArray<Uav, 8> uavs;
2461 void buildBufferBatches(QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches) const
2462 {
2463 for (const Buffer &buf : buffers) {
2464 batches.ubufs.feed(buf.breg, buf.buffer);
2465 batches.ubuforigbindings.feed(buf.breg, UINT(buf.binding));
2466 batches.ubufoffsets.feed(buf.breg, buf.offsetInConstants);
2467 batches.ubufsizes.feed(buf.breg, buf.sizeInConstants);
2468 }
2469 batches.finish();
2470 }
2471 void buildSamplerBatches(QD3D11ShaderResourceBindings::StageSamplerBatches &batches) const
2472 {
2473 for (const Texture &t : textures)
2474 batches.shaderresources.feed(t.treg, t.srv);
2475 for (const Sampler &s : samplers)
2476 batches.samplers.feed(s.sreg, s.sampler);
2477 batches.finish();
2478 }
2479 void buildUavBatches(QD3D11ShaderResourceBindings::StageUavBatches &batches) const
2480 {
2481 for (const Stage::Uav &u : uavs)
2482 batches.uavs.feed(u.ureg, u.uav);
2483 batches.finish();
2484 }
2485 } res[RBM_SUPPORTED_STAGES];
2486
2487 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
2488 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
2489 QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
2490 switch (b->type) {
2491 case QRhiShaderResourceBinding::UniformBuffer:
2492 {
2493 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
2494 Q_ASSERT(aligned(b->u.ubuf.offset, 256u) == b->u.ubuf.offset);
2495 bd.ubuf.id = bufD->m_id;
2496 bd.ubuf.generation = bufD->generation;
2497 // Dynamic ubuf offsets are not considered here, those are baked in
2498 // at a later stage, which is good as vsubufoffsets and friends are
2499 // per-srb, not per-setShaderResources call. Other backends (GL,
2500 // Metal) are different in this respect since those do not store
2501 // per-srb vsubufoffsets etc. data so life's a bit easier for them.
2502 // But here we have to defer baking in the dynamic offset.
2503 const quint32 offsetInConstants = b->u.ubuf.offset / 16;
2504 // size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
2505 // We can round up if needed since the buffers's actual size
2506 // (ByteWidth) is always a multiple of 256.
2507 const quint32 sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256u) / 16;
2508 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
2509 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
2510 if (nativeBinding.first >= 0)
2511 res[RBM_VERTEX].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2512 }
2513 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
2514 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
2515 if (nativeBinding.first >= 0)
2516 res[RBM_HULL].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2517 }
2518 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
2519 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
2520 if (nativeBinding.first >= 0)
2521 res[RBM_DOMAIN].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2522 }
2523 if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
2524 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
2525 if (nativeBinding.first >= 0)
2526 res[RBM_GEOMETRY].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2527 }
2528 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2529 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2530 if (nativeBinding.first >= 0)
2531 res[RBM_FRAGMENT].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2532 }
2533 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2534 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2535 if (nativeBinding.first >= 0)
2536 res[RBM_COMPUTE].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2537 }
2538 }
2539 break;
2540 case QRhiShaderResourceBinding::SampledTexture:
2541 case QRhiShaderResourceBinding::Texture:
2542 case QRhiShaderResourceBinding::Sampler:
2543 {
2544 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
2545 bd.stex.count = data->count;
2546 const std::pair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
2547 const std::pair<int, int> nativeBindingHull = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
2548 const std::pair<int, int> nativeBindingDomain = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
2549 const std::pair<int, int> nativeBindingGeom = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
2550 const std::pair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2551 const std::pair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2552 // if SPIR-V binding b is mapped to tN and sN in HLSL, and it
2553 // is an array, then it will use tN, tN+1, tN+2, ..., and sN,
2554 // sN+1, sN+2, ...
2555 for (int elem = 0; elem < data->count; ++elem) {
2556 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
2557 QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
2558 bd.stex.d[elem].texId = texD ? texD->m_id : 0;
2559 bd.stex.d[elem].texGeneration = texD ? texD->generation : 0;
2560 bd.stex.d[elem].samplerId = samplerD ? samplerD->m_id : 0;
2561 bd.stex.d[elem].samplerGeneration = samplerD ? samplerD->generation : 0;
2562 // Must handle all three cases (combined, separate, separate):
2563 // first = texture binding, second = sampler binding
2564 // first = texture binding
2565 // first = sampler binding
2566 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
2567 const int samplerBinding = texD && samplerD ? nativeBindingVert.second
2568 : (samplerD ? nativeBindingVert.first : -1);
2569 if (nativeBindingVert.first >= 0 && texD)
2570 res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv });
2571 if (samplerBinding >= 0)
2572 res[RBM_VERTEX].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2573 }
2574 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
2575 const int samplerBinding = texD && samplerD ? nativeBindingHull.second
2576 : (samplerD ? nativeBindingHull.first : -1);
2577 if (nativeBindingHull.first >= 0 && texD)
2578 res[RBM_HULL].textures.append({ nativeBindingHull.first + elem, texD->srv });
2579 if (samplerBinding >= 0)
2580 res[RBM_HULL].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2581 }
2582 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
2583 const int samplerBinding = texD && samplerD ? nativeBindingDomain.second
2584 : (samplerD ? nativeBindingDomain.first : -1);
2585 if (nativeBindingDomain.first >= 0 && texD)
2586 res[RBM_DOMAIN].textures.append({ nativeBindingDomain.first + elem, texD->srv });
2587 if (samplerBinding >= 0)
2588 res[RBM_DOMAIN].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2589 }
2590 if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
2591 const int samplerBinding = texD && samplerD ? nativeBindingGeom.second
2592 : (samplerD ? nativeBindingGeom.first : -1);
2593 if (nativeBindingGeom.first >= 0 && texD)
2594 res[RBM_GEOMETRY].textures.append({ nativeBindingGeom.first + elem, texD->srv });
2595 if (samplerBinding >= 0)
2596 res[RBM_GEOMETRY].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2597 }
2598 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2599 const int samplerBinding = texD && samplerD ? nativeBindingFrag.second
2600 : (samplerD ? nativeBindingFrag.first : -1);
2601 if (nativeBindingFrag.first >= 0 && texD)
2602 res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
2603 if (samplerBinding >= 0)
2604 res[RBM_FRAGMENT].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2605 }
2606 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2607 const int samplerBinding = texD && samplerD ? nativeBindingComp.second
2608 : (samplerD ? nativeBindingComp.first : -1);
2609 if (nativeBindingComp.first >= 0 && texD)
2610 res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
2611 if (samplerBinding >= 0)
2612 res[RBM_COMPUTE].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2613 }
2614 }
2615 }
2616 break;
2617 case QRhiShaderResourceBinding::ImageLoad:
2618 case QRhiShaderResourceBinding::ImageStore:
2619 case QRhiShaderResourceBinding::ImageLoadStore:
2620 {
2621 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
2622 bd.simage.id = texD->m_id;
2623 bd.simage.generation = texD->generation;
2624 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2625 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2626 if (nativeBinding.first >= 0) {
2627 ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
2628 if (uav)
2629 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
2630 }
2631 } else if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2632 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2633 if (nativeBinding.first >= 0) {
2634 ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
2635 if (uav)
2636 res[RBM_FRAGMENT].uavs.append({ nativeBinding.first, uav });
2637 }
2638 } else {
2639 qWarning("Unordered access only supported at fragment/compute stage");
2640 }
2641 }
2642 break;
2643 case QRhiShaderResourceBinding::BufferLoad:
2644 case QRhiShaderResourceBinding::BufferStore:
2645 case QRhiShaderResourceBinding::BufferLoadStore:
2646 {
2647 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
2648 bd.sbuf.id = bufD->m_id;
2649 bd.sbuf.generation = bufD->generation;
2650 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2651 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2652 if (nativeBinding.first >= 0) {
2653 ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView(b->u.sbuf.offset);
2654 if (uav)
2655 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
2656 }
2657 } else {
2658 qWarning("Unordered access only supported at compute stage");
2659 }
2660 }
2661 break;
2662 default:
2663 Q_UNREACHABLE();
2664 break;
2665 }
2666 }
2667
2668 // QRhiBatchedBindings works with the native bindings and expects
2669 // sorted input. The pre-sorted QRhiShaderResourceBinding list (based
2670 // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
2671 // have to sort here every time.
2672 for (int stage = 0; stage < RBM_SUPPORTED_STAGES; ++stage) {
2673 std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) {
2674 return a.breg < b.breg;
2675 });
2676 std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) {
2677 return a.treg < b.treg;
2678 });
2679 std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) {
2680 return a.sreg < b.sreg;
2681 });
2682 std::sort(res[stage].uavs.begin(), res[stage].uavs.end(), [](const Stage::Uav &a, const Stage::Uav &b) {
2683 return a.ureg < b.ureg;
2684 });
2685 }
2686
2687 res[RBM_VERTEX].buildBufferBatches(srbD->resourceBatches.vsUniformBufferBatches);
2688 res[RBM_HULL].buildBufferBatches(srbD->resourceBatches.hsUniformBufferBatches);
2689 res[RBM_DOMAIN].buildBufferBatches(srbD->resourceBatches.dsUniformBufferBatches);
2690 res[RBM_GEOMETRY].buildBufferBatches(srbD->resourceBatches.gsUniformBufferBatches);
2691 res[RBM_FRAGMENT].buildBufferBatches(srbD->resourceBatches.fsUniformBufferBatches);
2692 res[RBM_COMPUTE].buildBufferBatches(srbD->resourceBatches.csUniformBufferBatches);
2693
2694 res[RBM_VERTEX].buildSamplerBatches(srbD->resourceBatches.vsSamplerBatches);
2695 res[RBM_HULL].buildSamplerBatches(srbD->resourceBatches.hsSamplerBatches);
2696 res[RBM_DOMAIN].buildSamplerBatches(srbD->resourceBatches.dsSamplerBatches);
2697 res[RBM_GEOMETRY].buildSamplerBatches(srbD->resourceBatches.gsSamplerBatches);
2698 res[RBM_FRAGMENT].buildSamplerBatches(srbD->resourceBatches.fsSamplerBatches);
2699 res[RBM_COMPUTE].buildSamplerBatches(srbD->resourceBatches.csSamplerBatches);
2700
2701 res[RBM_FRAGMENT].buildUavBatches(srbD->resourceBatches.fsUavBatches);
2702 res[RBM_COMPUTE].buildUavBatches(srbD->resourceBatches.csUavBatches);
2703}
2704
2706{
2707 if (!bufD->hasPendingDynamicUpdates || bufD->m_size < 1)
2708 return;
2709
2710 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2711 bufD->hasPendingDynamicUpdates = false;
2712 D3D11_MAPPED_SUBRESOURCE mp;
2713 HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
2714 if (SUCCEEDED(hr)) {
2715 memcpy(mp.pData, bufD->dynBuf, bufD->m_size);
2716 context->Unmap(bufD->buffer, 0);
2717 } else {
2718 qWarning("Failed to map buffer: %s",
2719 qPrintable(QSystemError::windowsComString(hr)));
2720 }
2721}
2722
2723static void applyDynamicOffsets(UINT *offsets,
2724 int batchIndex,
2725 const QRhiBatchedBindings<UINT> *originalBindings,
2726 const QRhiBatchedBindings<UINT> *staticOffsets,
2727 const uint *dynOfsPairs, int dynOfsPairCount)
2728{
2729 const int count = staticOffsets->batches[batchIndex].resources.count();
2730 // Make a copy of the offset list, the entries that have no corresponding
2731 // dynamic offset will continue to use the existing offset value.
2732 for (int b = 0; b < count; ++b) {
2733 offsets[b] = staticOffsets->batches[batchIndex].resources[b];
2734 for (int di = 0; di < dynOfsPairCount; ++di) {
2735 const uint binding = dynOfsPairs[2 * di];
2736 // binding is the SPIR-V style binding point here, nothing to do
2737 // with the native one.
2738 if (binding == originalBindings->batches[batchIndex].resources[b]) {
2739 const uint offsetInConstants = dynOfsPairs[2 * di + 1];
2740 offsets[b] = offsetInConstants;
2741 break;
2742 }
2743 }
2744 }
2745}
2746
2747static inline uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType)
2748{
2749 if (startSlot + countSlots > maxSlots) {
2750 qWarning("Not enough D3D11 %s slots to bind %d resources starting at slot %d, max slots is %d",
2751 resType, countSlots, startSlot, maxSlots);
2752 countSlots = maxSlots > startSlot ? maxSlots - startSlot : 0;
2753 }
2754 return countSlots;
2755}
2756
2757#define SETUBUFBATCH(stagePrefixL, stagePrefixU)
2758 if (allResourceBatches.stagePrefixL##UniformBufferBatches.present) {
2759 const QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches(allResourceBatches.stagePrefixL##UniformBufferBatches);
2760 for (int i = 0, ie = batches.ubufs.batches.count(); i != ie; ++i) {
2761 const uint count = clampedResourceCount(batches.ubufs.batches[i].startBinding,
2762 batches.ubufs.batches[i].resources.count(),
2763 D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
2764 #stagePrefixU " cbuf");
2765 if (count) {
2766 if (!dynOfsPairCount) {
2767 context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding,
2768 count,
2769 batches.ubufs.batches[i].resources.constData(),
2770 batches.ubufoffsets.batches[i].resources.constData(),
2771 batches.ubufsizes.batches[i].resources.constData());
2772 } else {
2773 applyDynamicOffsets(offsets, i,
2774 &batches.ubuforigbindings, &batches.ubufoffsets,
2775 dynOfsPairs, dynOfsPairCount);
2776 context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding,
2777 count,
2778 batches.ubufs.batches[i].resources.constData(),
2779 offsets,
2780 batches.ubufsizes.batches[i].resources.constData());
2781 }
2782 }
2783 }
2784 }
2785
2786#define SETSAMPLERBATCH(stagePrefixL, stagePrefixU)
2787 if (allResourceBatches.stagePrefixL##SamplerBatches.present) {
2788 for (const auto &batch : allResourceBatches.stagePrefixL##SamplerBatches.samplers.batches) {
2789 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2790 D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, #stagePrefixU " sampler");
2791 if (count)
2792 context->stagePrefixU##SetSamplers(batch.startBinding, count, batch.resources.constData());
2793 }
2794 for (const auto &batch : allResourceBatches.stagePrefixL##SamplerBatches.shaderresources.batches) {
2795 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2796 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, #stagePrefixU " SRV");
2797 if (count) {
2798 context->stagePrefixU##SetShaderResources(batch.startBinding, count, batch.resources.constData());
2799 contextState.stagePrefixL##HighestActiveSrvBinding = qMax(contextState.stagePrefixL##HighestActiveSrvBinding,
2800 int(batch.startBinding + count) - 1);
2801 }
2802 }
2803 }
2804
2805#define SETUAVBATCH(stagePrefixL, stagePrefixU)
2806 if (allResourceBatches.stagePrefixL##UavBatches.present) {
2807 for (const auto &batch : allResourceBatches.stagePrefixL##UavBatches.uavs.batches) {
2808 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2809 D3D11_1_UAV_SLOT_COUNT, #stagePrefixU " UAV");
2810 if (count) {
2811 context->stagePrefixU##SetUnorderedAccessViews(batch.startBinding,
2812 count,
2813 batch.resources.constData(),
2814 nullptr);
2815 contextState.stagePrefixL##HighestActiveUavBinding = qMax(contextState.stagePrefixL##HighestActiveUavBinding,
2816 int(batch.startBinding + count) - 1);
2817 }
2818 }
2819 }
2820
2821void QRhiD3D11::bindShaderResources(const QD3D11ShaderResourceBindings::ResourceBatches &allResourceBatches,
2822 const uint *dynOfsPairs, int dynOfsPairCount,
2823 bool offsetOnlyChange,
2825 RenderTargetUavUpdateState &rtUavState)
2826{
2828
2829 SETUBUFBATCH(vs, VS)
2830 SETUBUFBATCH(hs, HS)
2831 SETUBUFBATCH(ds, DS)
2832 SETUBUFBATCH(gs, GS)
2833 SETUBUFBATCH(fs, PS)
2834 SETUBUFBATCH(cs, CS)
2835
2836 if (!offsetOnlyChange) {
2837 SETSAMPLERBATCH(vs, VS)
2838 SETSAMPLERBATCH(hs, HS)
2839 SETSAMPLERBATCH(ds, DS)
2840 SETSAMPLERBATCH(gs, GS)
2841 SETSAMPLERBATCH(fs, PS)
2842 SETSAMPLERBATCH(cs, CS)
2843
2844 SETUAVBATCH(cs, CS)
2845
2846 if (allResourceBatches.fsUavBatches.present) {
2847 for (const auto &batch : allResourceBatches.fsUavBatches.uavs.batches) {
2848 const uint count = qMin(clampedResourceCount(batch.startBinding, batch.resources.count(),
2849 D3D11_1_UAV_SLOT_COUNT, "fs UAV"),
2850 uint(QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS));
2851 if (count) {
2852 if (rtUavState.update(rtD, batch.resources.constData(), count)) {
2853 context->OMSetRenderTargetsAndUnorderedAccessViews(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv,
2854 UINT(rtD->colorAttCount), count, batch.resources.constData(), nullptr);
2855 }
2856 contextState.fsHighestActiveUavBinding = qMax(contextState.fsHighestActiveUavBinding,
2857 int(batch.startBinding + count) - 1);
2858 }
2859 }
2860 }
2861 }
2862}
2863
2865 RenderTargetUavUpdateState &rtUavState)
2866{
2867 // Output cannot be bound on input etc.
2868
2869 if (contextState.vsHasIndexBufferBound) {
2870 context->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
2871 contextState.vsHasIndexBufferBound = false;
2872 }
2873
2874 if (contextState.vsHighestActiveVertexBufferBinding >= 0) {
2875 const int count = contextState.vsHighestActiveVertexBufferBinding + 1;
2876 QVarLengthArray<ID3D11Buffer *, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullbufs(count);
2877 for (int i = 0; i < count; ++i)
2878 nullbufs[i] = nullptr;
2879 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullstrides(count);
2880 for (int i = 0; i < count; ++i)
2881 nullstrides[i] = 0;
2882 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nulloffsets(count);
2883 for (int i = 0; i < count; ++i)
2884 nulloffsets[i] = 0;
2885 context->IASetVertexBuffers(0, UINT(count), nullbufs.constData(), nullstrides.constData(), nulloffsets.constData());
2886 contextState.vsHighestActiveVertexBufferBinding = -1;
2887 }
2888
2889 int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
2890 nullsrvCount = qMax(nullsrvCount, contextState.hsHighestActiveSrvBinding);
2891 nullsrvCount = qMax(nullsrvCount, contextState.dsHighestActiveSrvBinding);
2892 nullsrvCount = qMax(nullsrvCount, contextState.gsHighestActiveSrvBinding);
2893 nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
2894 nullsrvCount += 1;
2895 if (nullsrvCount > 0) {
2896 QVarLengthArray<ID3D11ShaderResourceView *,
2897 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nullsrvs(nullsrvCount);
2898 for (int i = 0; i < nullsrvs.count(); ++i)
2899 nullsrvs[i] = nullptr;
2900 if (contextState.vsHighestActiveSrvBinding >= 0) {
2901 context->VSSetShaderResources(0, UINT(contextState.vsHighestActiveSrvBinding + 1), nullsrvs.constData());
2902 contextState.vsHighestActiveSrvBinding = -1;
2903 }
2904 if (contextState.hsHighestActiveSrvBinding >= 0) {
2905 context->HSSetShaderResources(0, UINT(contextState.hsHighestActiveSrvBinding + 1), nullsrvs.constData());
2906 contextState.hsHighestActiveSrvBinding = -1;
2907 }
2908 if (contextState.dsHighestActiveSrvBinding >= 0) {
2909 context->DSSetShaderResources(0, UINT(contextState.dsHighestActiveSrvBinding + 1), nullsrvs.constData());
2910 contextState.dsHighestActiveSrvBinding = -1;
2911 }
2912 if (contextState.gsHighestActiveSrvBinding >= 0) {
2913 context->GSSetShaderResources(0, UINT(contextState.gsHighestActiveSrvBinding + 1), nullsrvs.constData());
2914 contextState.gsHighestActiveSrvBinding = -1;
2915 }
2916 if (contextState.fsHighestActiveSrvBinding >= 0) {
2917 context->PSSetShaderResources(0, UINT(contextState.fsHighestActiveSrvBinding + 1), nullsrvs.constData());
2918 contextState.fsHighestActiveSrvBinding = -1;
2919 }
2920 if (contextState.csHighestActiveSrvBinding >= 0) {
2921 context->CSSetShaderResources(0, UINT(contextState.csHighestActiveSrvBinding + 1), nullsrvs.constData());
2922 contextState.csHighestActiveSrvBinding = -1;
2923 }
2924 }
2925
2926 if (contextState.fsHighestActiveUavBinding >= 0) {
2927 rtUavState.update(rtD);
2928 context->OMSetRenderTargetsAndUnorderedAccessViews(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv, 0, 0, nullptr, nullptr);
2929 contextState.fsHighestActiveUavBinding = -1;
2930 }
2931 if (contextState.csHighestActiveUavBinding >= 0) {
2932 const int nulluavCount = contextState.csHighestActiveUavBinding + 1;
2933 QVarLengthArray<ID3D11UnorderedAccessView *,
2934 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nulluavs(nulluavCount);
2935 for (int i = 0; i < nulluavCount; ++i)
2936 nulluavs[i] = nullptr;
2937 context->CSSetUnorderedAccessViews(0, UINT(nulluavCount), nulluavs.constData(), nullptr);
2938 contextState.csHighestActiveUavBinding = -1;
2939 }
2940}
2941
2942#define SETSHADER(StageL, StageU)
2943 if (psD->StageL.shader) {
2944 context->StageU##SetShader(psD->StageL.shader, nullptr, 0);
2945 currentShaderMask |= StageU##MaskBit;
2946 } else if (currentShaderMask & StageU##MaskBit) {
2947 context->StageU##SetShader(nullptr, nullptr, 0);
2948 currentShaderMask &= ~StageU##MaskBit;
2949 }
2950
2952{
2953 quint32 stencilRef = 0;
2954 float blendConstants[] = { 1, 1, 1, 1 };
2955 enum ActiveShaderMask {
2956 VSMaskBit = 0x01,
2957 HSMaskBit = 0x02,
2958 DSMaskBit = 0x04,
2959 GSMaskBit = 0x08,
2960 PSMaskBit = 0x10
2961 };
2962 int currentShaderMask = 0xFF;
2963
2964 // Track render target and uav updates during executeCommandBuffer.
2965 // Prevents multiple identical OMSetRenderTargetsAndUnorderedAccessViews calls.
2966 RenderTargetUavUpdateState rtUavState;
2967
2968 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
2969 const QD3D11CommandBuffer::Command &cmd(*it);
2970 switch (cmd.cmd) {
2971 case QD3D11CommandBuffer::Command::BeginFrame:
2972 if (cmd.args.beginFrame.tsDisjointQuery)
2973 context->Begin(cmd.args.beginFrame.tsDisjointQuery);
2974 if (cmd.args.beginFrame.tsQuery) {
2975 if (cmd.args.beginFrame.swapchainData) {
2976 // The timestamps seem to include vsync time with Present(1), except
2977 // when running on a non-primary gpu. This is not ideal. So try working
2978 // it around by issuing a semi-fake OMSetRenderTargets early and
2979 // writing the first timestamp only afterwards.
2980 QD3D11RenderTargetData *rtD = cmd.args.beginFrame.swapchainData;
2981 rtUavState.update(rtD);
2982 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
2983 cbD->prevRtD = rtD;
2984 }
2985 context->End(cmd.args.beginFrame.tsQuery); // no Begin() for D3D11_QUERY_TIMESTAMP
2986 }
2987 break;
2988 case QD3D11CommandBuffer::Command::EndFrame:
2989 if (cmd.args.endFrame.tsQuery)
2990 context->End(cmd.args.endFrame.tsQuery);
2991 if (cmd.args.endFrame.tsDisjointQuery)
2992 context->End(cmd.args.endFrame.tsDisjointQuery);
2993 break;
2996 break;
2998 {
2999 QD3D11RenderTargetData *rtD = rtData(cmd.args.setRenderTarget.rt);
3000 if (rtUavState.update(rtD))
3001 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
3002 cbD->prevRtD = rtD;
3003 }
3004 break;
3006 {
3007 QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt);
3008 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) {
3009 for (int i = 0; i < rtD->colorAttCount; ++i)
3010 context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c);
3011 }
3012 uint ds = 0;
3013 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth)
3014 ds |= D3D11_CLEAR_DEPTH;
3015 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil)
3016 ds |= D3D11_CLEAR_STENCIL;
3017 if (ds)
3018 context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, UINT8(cmd.args.clear.s));
3019 }
3020 break;
3022 {
3023 D3D11_VIEWPORT v;
3024 v.TopLeftX = cmd.args.viewport.x;
3025 v.TopLeftY = cmd.args.viewport.y;
3026 v.Width = cmd.args.viewport.w;
3027 v.Height = cmd.args.viewport.h;
3028 v.MinDepth = cmd.args.viewport.d0;
3029 v.MaxDepth = cmd.args.viewport.d1;
3030 context->RSSetViewports(1, &v);
3031 }
3032 break;
3034 {
3035 D3D11_RECT r;
3036 r.left = cmd.args.scissor.x;
3037 r.top = cmd.args.scissor.y;
3038 // right and bottom are exclusive
3039 r.right = cmd.args.scissor.x + cmd.args.scissor.w;
3040 r.bottom = cmd.args.scissor.y + cmd.args.scissor.h;
3041 context->RSSetScissorRects(1, &r);
3042 }
3043 break;
3045 contextState.vsHighestActiveVertexBufferBinding = qMax<int>(
3047 cmd.args.bindVertexBuffers.startSlot + cmd.args.bindVertexBuffers.slotCount - 1);
3048 context->IASetVertexBuffers(UINT(cmd.args.bindVertexBuffers.startSlot),
3049 UINT(cmd.args.bindVertexBuffers.slotCount),
3050 cmd.args.bindVertexBuffers.buffers,
3051 cmd.args.bindVertexBuffers.strides,
3052 cmd.args.bindVertexBuffers.offsets);
3053 break;
3055 contextState.vsHasIndexBufferBound = true;
3056 context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer,
3057 cmd.args.bindIndexBuffer.format,
3058 cmd.args.bindIndexBuffer.offset);
3059 break;
3061 {
3062 QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
3063 SETSHADER(vs, VS)
3064 SETSHADER(hs, HS)
3065 SETSHADER(ds, DS)
3066 SETSHADER(gs, GS)
3067 SETSHADER(fs, PS)
3068 context->IASetPrimitiveTopology(psD->d3dTopology);
3069 context->IASetInputLayout(psD->inputLayout); // may be null, that's ok
3070 context->OMSetDepthStencilState(psD->dsState, stencilRef);
3071 context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff);
3072 context->RSSetState(psD->rastState);
3073 }
3074 break;
3075 case QD3D11CommandBuffer::Command::BindShaderResources:
3076 bindShaderResources(cbD->resourceBatchRetainPool[cmd.args.bindShaderResources.resourceBatchesIndex],
3077 cmd.args.bindShaderResources.dynamicOffsetPairs,
3078 cmd.args.bindShaderResources.dynamicOffsetCount,
3079 cmd.args.bindShaderResources.offsetOnlyChange,
3080 cbD->prevRtD,
3081 rtUavState);
3082 break;
3084 stencilRef = cmd.args.stencilRef.ref;
3085 context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef);
3086 break;
3088 memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float));
3089 context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff);
3090 break;
3092 if (cmd.args.draw.ps) {
3093 if (cmd.args.draw.instanceCount == 1 && cmd.args.draw.firstInstance == 0)
3094 context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex);
3095 else
3096 context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
3097 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
3098 } else {
3099 qWarning("No graphics pipeline active for draw; ignored");
3100 }
3101 break;
3103 if (cmd.args.drawIndexed.ps) {
3104 if (cmd.args.drawIndexed.instanceCount == 1 && cmd.args.drawIndexed.firstInstance == 0)
3105 context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex,
3106 cmd.args.drawIndexed.vertexOffset);
3107 else
3108 context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
3109 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
3110 cmd.args.drawIndexed.firstInstance);
3111 } else {
3112 qWarning("No graphics pipeline active for drawIndexed; ignored");
3113 }
3114 break;
3116 if (cmd.args.drawIndirect.ps) {
3117 ID3D11Buffer *pBufferForArgs = cmd.args.drawIndirect.indirectBuffer->buffer;
3118 UINT alignedByteOffsetForArgs = cmd.args.drawIndirect.indirectBufferOffset;
3119 const UINT stride = cmd.args.drawIndirect.stride;
3120 for (quint32 i = 0; i < cmd.args.drawIndirect.drawCount; ++i) {
3121 context->DrawInstancedIndirect(pBufferForArgs, alignedByteOffsetForArgs);
3122 alignedByteOffsetForArgs += stride;
3123 }
3124 } else {
3125 qWarning("No graphics pipeline active for drawIndirect; ignored");
3126 }
3127 break;
3129 if (cmd.args.drawIndexedIndirect.ps) {
3130 ID3D11Buffer *pBufferForArgs = cmd.args.drawIndexedIndirect.indirectBuffer->buffer;
3131 UINT alignedByteOffsetForArgs = cmd.args.drawIndexedIndirect.indirectBufferOffset;
3132 const UINT stride = cmd.args.drawIndexedIndirect.stride;
3133 for (quint32 i = 0; i < cmd.args.drawIndexedIndirect.drawCount; ++i) {
3134 context->DrawIndexedInstancedIndirect(pBufferForArgs, alignedByteOffsetForArgs);
3135 alignedByteOffsetForArgs += stride;
3136 }
3137 } else {
3138 qWarning("No graphics pipeline active for drawIndexedIndirect; ignored");
3139 }
3140 break;
3141 case QD3D11CommandBuffer::Command::UpdateSubRes:
3142 context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes,
3143 cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr,
3144 cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0);
3145 break;
3146 case QD3D11CommandBuffer::Command::CopySubRes:
3147 context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
3148 cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, cmd.args.copySubRes.dstZ,
3149 cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
3150 cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
3151 break;
3152 case QD3D11CommandBuffer::Command::ResolveSubRes:
3153 context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes,
3154 cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
3155 cmd.args.resolveSubRes.format);
3156 break;
3157 case QD3D11CommandBuffer::Command::GenMip:
3158 context->GenerateMips(cmd.args.genMip.srv);
3159 break;
3160 case QD3D11CommandBuffer::Command::DebugMarkBegin:
3161 annotations->BeginEvent(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
3162 break;
3163 case QD3D11CommandBuffer::Command::DebugMarkEnd:
3164 annotations->EndEvent();
3165 break;
3166 case QD3D11CommandBuffer::Command::DebugMarkMsg:
3167 annotations->SetMarker(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
3168 break;
3169 case QD3D11CommandBuffer::Command::BindComputePipeline:
3170 context->CSSetShader(cmd.args.bindComputePipeline.ps->cs.shader, nullptr, 0);
3171 break;
3172 case QD3D11CommandBuffer::Command::Dispatch:
3173 context->Dispatch(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
3174 break;
3175 default:
3176 break;
3177 }
3178 }
3179}
3180
3181QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
3183{
3184}
3185
3190
3192{
3193 if (!buffer)
3194 return;
3195
3196 buffer->Release();
3197 buffer = nullptr;
3198
3199 delete[] dynBuf;
3200 dynBuf = nullptr;
3201
3202 for (auto it = uavs.begin(), end = uavs.end(); it != end; ++it)
3203 it.value()->Release();
3204 uavs.clear();
3205
3206 QRHI_RES_RHI(QRhiD3D11);
3207 if (rhiD)
3208 rhiD->unregisterResource(this);
3209}
3210
3211static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
3212{
3213 int u = 0;
3214 if (usage.testFlag(QRhiBuffer::VertexBuffer))
3215 u |= D3D11_BIND_VERTEX_BUFFER;
3216 if (usage.testFlag(QRhiBuffer::IndexBuffer))
3217 u |= D3D11_BIND_INDEX_BUFFER;
3218 if (usage.testFlag(QRhiBuffer::UniformBuffer))
3219 u |= D3D11_BIND_CONSTANT_BUFFER;
3220 if (usage.testFlag(QRhiBuffer::StorageBuffer))
3221 u |= D3D11_BIND_UNORDERED_ACCESS;
3222 return uint(u);
3223}
3224
3226{
3227 if (buffer)
3228 destroy();
3229
3230 if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
3231 qWarning("UniformBuffer must always be combined with Dynamic on D3D11");
3232 return false;
3233 }
3234
3235 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3236 qWarning("StorageBuffer cannot be combined with Dynamic");
3237 return false;
3238 }
3239
3240 if (m_usage.testFlag(QRhiBuffer::IndirectBuffer) && m_type == Dynamic) {
3241 qWarning("IndirectBuffer cannot be combined with Dynamic on D3D11");
3242 return false;
3243 }
3244
3245 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3246 const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
3247
3248 D3D11_BUFFER_DESC desc = {};
3249 desc.ByteWidth = roundedSize;
3250 desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
3251 desc.BindFlags = toD3DBufferUsage(m_usage);
3252 desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
3253 desc.MiscFlags = m_usage.testFlag(QRhiBuffer::StorageBuffer) ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0;
3254 if (m_usage.testFlag(QRhiBuffer::IndirectBuffer))
3255 desc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
3256
3257 QRHI_RES_RHI(QRhiD3D11);
3258 HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
3259 if (FAILED(hr)) {
3260 qWarning("Failed to create buffer: %s",
3261 qPrintable(QSystemError::windowsComString(hr)));
3262 return false;
3263 }
3264
3265 if (m_type == Dynamic) {
3266 dynBuf = new char[nonZeroSize];
3268 }
3269
3270 if (!m_objectName.isEmpty())
3271 buffer->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3272
3273 generation += 1;
3274 rhiD->registerResource(this);
3275 return true;
3276}
3277
3279{
3280 if (m_type == Dynamic) {
3281 QRHI_RES_RHI(QRhiD3D11);
3283 }
3284 return { { &buffer }, 1 };
3285}
3286
3288{
3289 // Shortcut the entire buffer update mechanism and allow the client to do
3290 // the host writes directly to the buffer. This will lead to unexpected
3291 // results when combined with QRhiResourceUpdateBatch-based updates for the
3292 // buffer, since dynBuf is left untouched and out of sync, but provides a
3293 // fast path for dynamic buffers that have all their content changed in
3294 // every frame.
3295 Q_ASSERT(m_type == Dynamic);
3296 D3D11_MAPPED_SUBRESOURCE mp;
3297 QRHI_RES_RHI(QRhiD3D11);
3298 HRESULT hr = rhiD->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
3299 if (FAILED(hr)) {
3300 qWarning("Failed to map buffer: %s",
3301 qPrintable(QSystemError::windowsComString(hr)));
3302 return nullptr;
3303 }
3304 return static_cast<char *>(mp.pData);
3305}
3306
3308{
3309 QRHI_RES_RHI(QRhiD3D11);
3310 rhiD->context->Unmap(buffer, 0);
3311}
3312
3314{
3315 auto it = uavs.find(offset);
3316 if (it != uavs.end())
3317 return it.value();
3318
3319 // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
3320 D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
3321 desc.Format = DXGI_FORMAT_R32_TYPELESS;
3322 desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
3323 desc.Buffer.FirstElement = offset / 4u;
3324 desc.Buffer.NumElements = aligned(m_size - offset, 4u) / 4u;
3325 desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
3326
3327 QRHI_RES_RHI(QRhiD3D11);
3328 ID3D11UnorderedAccessView *uav = nullptr;
3329 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav);
3330 if (FAILED(hr)) {
3331 qWarning("Failed to create UAV: %s",
3332 qPrintable(QSystemError::windowsComString(hr)));
3333 return nullptr;
3334 }
3335
3336 uavs[offset] = uav;
3337 return uav;
3338}
3339
3340QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
3341 int sampleCount, QRhiRenderBuffer::Flags flags,
3342 QRhiTexture::Format backingFormatHint)
3344{
3345}
3346
3351
3353{
3354 if (!tex)
3355 return;
3356
3357 if (dsv) {
3358 dsv->Release();
3359 dsv = nullptr;
3360 }
3361
3362 if (rtv) {
3363 rtv->Release();
3364 rtv = nullptr;
3365 }
3366
3367 tex->Release();
3368 tex = nullptr;
3369
3370 QRHI_RES_RHI(QRhiD3D11);
3371 if (rhiD)
3372 rhiD->unregisterResource(this);
3373}
3374
3376{
3377 if (tex)
3378 destroy();
3379
3380 if (m_pixelSize.isEmpty())
3381 return false;
3382
3383 QRHI_RES_RHI(QRhiD3D11);
3384 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
3385
3386 D3D11_TEXTURE2D_DESC desc = {};
3387 desc.Width = UINT(m_pixelSize.width());
3388 desc.Height = UINT(m_pixelSize.height());
3389 desc.MipLevels = 1;
3390 desc.ArraySize = 1;
3391 desc.SampleDesc = sampleDesc;
3392 desc.Usage = D3D11_USAGE_DEFAULT;
3393
3394 if (m_type == Color) {
3395 dxgiFormat = m_backingFormatHint == QRhiTexture::UnknownFormat ? DXGI_FORMAT_R8G8B8A8_UNORM
3396 : toD3DTextureFormat(m_backingFormatHint, {});
3397 desc.Format = dxgiFormat;
3398 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
3399 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3400 if (FAILED(hr)) {
3401 qWarning("Failed to create color renderbuffer: %s",
3402 qPrintable(QSystemError::windowsComString(hr)));
3403 return false;
3404 }
3405 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
3406 rtvDesc.Format = dxgiFormat;
3407 rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS
3408 : D3D11_RTV_DIMENSION_TEXTURE2D;
3409 hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv);
3410 if (FAILED(hr)) {
3411 qWarning("Failed to create rtv: %s",
3412 qPrintable(QSystemError::windowsComString(hr)));
3413 return false;
3414 }
3415 } else if (m_type == DepthStencil) {
3416 dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
3417 desc.Format = dxgiFormat;
3418 desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
3419 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3420 if (FAILED(hr)) {
3421 qWarning("Failed to create depth-stencil buffer: %s",
3422 qPrintable(QSystemError::windowsComString(hr)));
3423 return false;
3424 }
3425 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
3426 dsvDesc.Format = dxgiFormat;
3427 dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
3428 : D3D11_DSV_DIMENSION_TEXTURE2D;
3429 hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv);
3430 if (FAILED(hr)) {
3431 qWarning("Failed to create dsv: %s",
3432 qPrintable(QSystemError::windowsComString(hr)));
3433 return false;
3434 }
3435 } else {
3436 return false;
3437 }
3438
3439 if (!m_objectName.isEmpty())
3440 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3441
3442 generation += 1;
3443 rhiD->registerResource(this);
3444 return true;
3445}
3446
3448{
3449 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3450 return m_backingFormatHint;
3451 else
3452 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3453}
3454
3455QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
3456 int arraySize, int sampleCount, Flags flags)
3458{
3459 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3460 perLevelViews[i] = nullptr;
3461}
3462
3467
3469{
3470 if (!tex && !tex3D && !tex1D)
3471 return;
3472
3473 if (srv) {
3474 srv->Release();
3475 srv = nullptr;
3476 }
3477
3478 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3479 if (perLevelViews[i]) {
3480 perLevelViews[i]->Release();
3481 perLevelViews[i] = nullptr;
3482 }
3483 }
3484
3485 if (owns) {
3486 if (tex)
3487 tex->Release();
3488 if (tex3D)
3489 tex3D->Release();
3490 if (tex1D)
3491 tex1D->Release();
3492 }
3493
3494 tex = nullptr;
3495 tex3D = nullptr;
3496 tex1D = nullptr;
3497
3498 QRHI_RES_RHI(QRhiD3D11);
3499 if (rhiD)
3500 rhiD->unregisterResource(this);
3501}
3502
3503static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
3504{
3505 switch (format) {
3506 case QRhiTexture::Format::D16:
3507 return DXGI_FORMAT_R16_FLOAT;
3508 case QRhiTexture::Format::D24:
3509 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
3510 case QRhiTexture::Format::D24S8:
3511 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
3512 case QRhiTexture::Format::D32F:
3513 return DXGI_FORMAT_R32_FLOAT;
3514 case QRhiTexture::Format::D32FS8:
3515 return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
3516 default:
3517 Q_UNREACHABLE();
3518 return DXGI_FORMAT_R32_FLOAT;
3519 }
3520}
3521
3522static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
3523{
3524 switch (format) {
3525 case QRhiTexture::Format::D16:
3526 return DXGI_FORMAT_D16_UNORM;
3527 case QRhiTexture::Format::D24:
3528 return DXGI_FORMAT_D24_UNORM_S8_UINT;
3529 case QRhiTexture::Format::D24S8:
3530 return DXGI_FORMAT_D24_UNORM_S8_UINT;
3531 case QRhiTexture::Format::D32F:
3532 return DXGI_FORMAT_D32_FLOAT;
3533 case QRhiTexture::Format::D32FS8:
3534 return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
3535 default:
3536 Q_UNREACHABLE();
3537 return DXGI_FORMAT_D32_FLOAT;
3538 }
3539}
3540
3541bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
3542{
3543 if (tex || tex3D || tex1D)
3544 destroy();
3545
3546 QRHI_RES_RHI(QRhiD3D11);
3547 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
3548 return false;
3549
3550 const bool isDepth = isDepthTextureFormat(m_format);
3551 const bool isCube = m_flags.testFlag(CubeMap);
3552 const bool is3D = m_flags.testFlag(ThreeDimensional);
3553 const bool isArray = m_flags.testFlag(TextureArray);
3554 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3555 const bool is1D = m_flags.testFlag(OneDimensional);
3556
3557 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3558 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3559
3560 dxgiFormat = toD3DTextureFormat(m_format, m_flags);
3561 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
3562 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
3563 if (sampleDesc.Count > 1) {
3564 if (isCube) {
3565 qWarning("Cubemap texture cannot be multisample");
3566 return false;
3567 }
3568 if (is3D) {
3569 qWarning("3D texture cannot be multisample");
3570 return false;
3571 }
3572 if (hasMipMaps) {
3573 qWarning("Multisample texture cannot have mipmaps");
3574 return false;
3575 }
3576 }
3577 if (isDepth && hasMipMaps) {
3578 qWarning("Depth texture cannot have mipmaps");
3579 return false;
3580 }
3581 if (isCube && is3D) {
3582 qWarning("Texture cannot be both cube and 3D");
3583 return false;
3584 }
3585 if (isArray && is3D) {
3586 qWarning("Texture cannot be both array and 3D");
3587 return false;
3588 }
3589 if (isCube && is1D) {
3590 qWarning("Texture cannot be both cube and 1D");
3591 return false;
3592 }
3593 if (is1D && is3D) {
3594 qWarning("Texture cannot be both 1D and 3D");
3595 return false;
3596 }
3597 if (m_depth > 1 && !is3D) {
3598 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
3599 return false;
3600 }
3601 if (m_arraySize > 0 && !isArray) {
3602 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
3603 return false;
3604 }
3605 if (m_arraySize < 1 && isArray) {
3606 qWarning("Texture is an array but array size is %d", m_arraySize);
3607 return false;
3608 }
3609
3610 if (adjustedSize)
3611 *adjustedSize = size;
3612
3613 return true;
3614}
3615
3617{
3618 QRHI_RES_RHI(QRhiD3D11);
3619 const bool isDepth = isDepthTextureFormat(m_format);
3620 const bool isCube = m_flags.testFlag(CubeMap);
3621 const bool is3D = m_flags.testFlag(ThreeDimensional);
3622 const bool isArray = m_flags.testFlag(TextureArray);
3623 const bool is1D = m_flags.testFlag(OneDimensional);
3624
3625 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
3626 srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
3627 if (isCube) {
3628 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
3629 srvDesc.TextureCube.MipLevels = mipLevelCount;
3630 } else {
3631 if (is1D) {
3632 if (isArray) {
3633 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
3634 srvDesc.Texture1DArray.MipLevels = mipLevelCount;
3635 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3636 srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
3637 srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
3638 } else {
3639 srvDesc.Texture1DArray.FirstArraySlice = 0;
3640 srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
3641 }
3642 } else {
3643 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
3644 srvDesc.Texture1D.MipLevels = mipLevelCount;
3645 }
3646 } else if (isArray) {
3647 if (sampleDesc.Count > 1) {
3648 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
3649 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3650 srvDesc.Texture2DMSArray.FirstArraySlice = UINT(m_arrayRangeStart);
3651 srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
3652 } else {
3653 srvDesc.Texture2DMSArray.FirstArraySlice = 0;
3654 srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
3655 }
3656 } else {
3657 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
3658 srvDesc.Texture2DArray.MipLevels = mipLevelCount;
3659 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3660 srvDesc.Texture2DArray.FirstArraySlice = UINT(m_arrayRangeStart);
3661 srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
3662 } else {
3663 srvDesc.Texture2DArray.FirstArraySlice = 0;
3664 srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
3665 }
3666 }
3667 } else {
3668 if (sampleDesc.Count > 1) {
3669 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
3670 } else if (is3D) {
3671 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
3672 srvDesc.Texture3D.MipLevels = mipLevelCount;
3673 } else {
3674 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
3675 srvDesc.Texture2D.MipLevels = mipLevelCount;
3676 }
3677 }
3678 }
3679
3680 HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv);
3681 if (FAILED(hr)) {
3682 qWarning("Failed to create srv: %s",
3683 qPrintable(QSystemError::windowsComString(hr)));
3684 return false;
3685 }
3686
3687 generation += 1;
3688 return true;
3689}
3690
3692{
3693 QSize size;
3694 if (!prepareCreate(&size))
3695 return false;
3696
3697 const bool isDepth = isDepthTextureFormat(m_format);
3698 const bool isCube = m_flags.testFlag(CubeMap);
3699 const bool is3D = m_flags.testFlag(ThreeDimensional);
3700 const bool isArray = m_flags.testFlag(TextureArray);
3701 const bool is1D = m_flags.testFlag(OneDimensional);
3702
3703 uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
3704 uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
3705 if (m_flags.testFlag(RenderTarget)) {
3706 if (isDepth)
3707 bindFlags |= D3D11_BIND_DEPTH_STENCIL;
3708 else
3709 bindFlags |= D3D11_BIND_RENDER_TARGET;
3710 }
3711 if (m_flags.testFlag(UsedWithGenerateMips)) {
3712 if (isDepth) {
3713 qWarning("Depth texture cannot have mipmaps generated");
3714 return false;
3715 }
3716 bindFlags |= D3D11_BIND_RENDER_TARGET;
3717 miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
3718 }
3719 if (m_flags.testFlag(UsedWithLoadStore))
3720 bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
3721
3722 QRHI_RES_RHI(QRhiD3D11);
3723 if (is1D) {
3724 D3D11_TEXTURE1D_DESC desc = {};
3725 desc.Width = UINT(size.width());
3726 desc.MipLevels = mipLevelCount;
3727 desc.ArraySize = isArray ? UINT(qMax(0, m_arraySize)) : 1;
3728 desc.Format = dxgiFormat;
3729 desc.Usage = D3D11_USAGE_DEFAULT;
3730 desc.BindFlags = bindFlags;
3731 desc.MiscFlags = miscFlags;
3732
3733 HRESULT hr = rhiD->dev->CreateTexture1D(&desc, nullptr, &tex1D);
3734 if (FAILED(hr)) {
3735 qWarning("Failed to create 1D texture: %s",
3736 qPrintable(QSystemError::windowsComString(hr)));
3737 return false;
3738 }
3739 if (!m_objectName.isEmpty())
3740 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()),
3741 m_objectName.constData());
3742 } else if (!is3D) {
3743 D3D11_TEXTURE2D_DESC desc = {};
3744 desc.Width = UINT(size.width());
3745 desc.Height = UINT(size.height());
3746 desc.MipLevels = mipLevelCount;
3747 desc.ArraySize = isCube ? 6 : (isArray ? UINT(qMax(0, m_arraySize)) : 1);
3748 desc.Format = dxgiFormat;
3749 desc.SampleDesc = sampleDesc;
3750 desc.Usage = D3D11_USAGE_DEFAULT;
3751 desc.BindFlags = bindFlags;
3752 desc.MiscFlags = miscFlags;
3753
3754 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3755 if (FAILED(hr)) {
3756 qWarning("Failed to create 2D texture: %s",
3757 qPrintable(QSystemError::windowsComString(hr)));
3758 return false;
3759 }
3760 if (!m_objectName.isEmpty())
3761 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3762 } else {
3763 D3D11_TEXTURE3D_DESC desc = {};
3764 desc.Width = UINT(size.width());
3765 desc.Height = UINT(size.height());
3766 desc.Depth = UINT(qMax(1, m_depth));
3767 desc.MipLevels = mipLevelCount;
3768 desc.Format = dxgiFormat;
3769 desc.Usage = D3D11_USAGE_DEFAULT;
3770 desc.BindFlags = bindFlags;
3771 desc.MiscFlags = miscFlags;
3772
3773 HRESULT hr = rhiD->dev->CreateTexture3D(&desc, nullptr, &tex3D);
3774 if (FAILED(hr)) {
3775 qWarning("Failed to create 3D texture: %s",
3776 qPrintable(QSystemError::windowsComString(hr)));
3777 return false;
3778 }
3779 if (!m_objectName.isEmpty())
3780 tex3D->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3781 }
3782
3783 if (!finishCreate())
3784 return false;
3785
3786 owns = true;
3787 rhiD->registerResource(this);
3788 return true;
3789}
3790
3791bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
3792{
3793 if (!src.object)
3794 return false;
3795
3796 if (!prepareCreate())
3797 return false;
3798
3799 if (m_flags.testFlag(ThreeDimensional))
3800 tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
3801 else if (m_flags.testFlags(OneDimensional))
3802 tex1D = reinterpret_cast<ID3D11Texture1D *>(src.object);
3803 else
3804 tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
3805
3806 if (!finishCreate())
3807 return false;
3808
3809 owns = false;
3810 QRHI_RES_RHI(QRhiD3D11);
3811 rhiD->registerResource(this);
3812 return true;
3813}
3814
3816{
3817 return { quint64(textureResource()), 0 };
3818}
3819
3821{
3822 if (perLevelViews[level])
3823 return perLevelViews[level];
3824
3825 const bool isCube = m_flags.testFlag(CubeMap);
3826 const bool isArray = m_flags.testFlag(TextureArray);
3827 const bool is3D = m_flags.testFlag(ThreeDimensional);
3828 D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
3829 desc.Format = dxgiFormat;
3830 if (isCube) {
3831 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
3832 desc.Texture2DArray.MipSlice = UINT(level);
3833 desc.Texture2DArray.FirstArraySlice = 0;
3834 desc.Texture2DArray.ArraySize = 6;
3835 } else if (isArray) {
3836 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
3837 desc.Texture2DArray.MipSlice = UINT(level);
3838 desc.Texture2DArray.FirstArraySlice = 0;
3839 desc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
3840 } else if (is3D) {
3841 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
3842 desc.Texture3D.MipSlice = UINT(level);
3843 desc.Texture3D.WSize = UINT(m_depth);
3844 } else {
3845 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
3846 desc.Texture2D.MipSlice = UINT(level);
3847 }
3848
3849 QRHI_RES_RHI(QRhiD3D11);
3850 ID3D11UnorderedAccessView *uav = nullptr;
3851 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav);
3852 if (FAILED(hr)) {
3853 qWarning("Failed to create UAV: %s",
3854 qPrintable(QSystemError::windowsComString(hr)));
3855 return nullptr;
3856 }
3857
3858 perLevelViews[level] = uav;
3859 return uav;
3860}
3861
3862QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
3863 AddressMode u, AddressMode v, AddressMode w)
3865{
3866}
3867
3872
3874{
3875 if (!samplerState)
3876 return;
3877
3878 samplerState->Release();
3879 samplerState = nullptr;
3880
3881 QRHI_RES_RHI(QRhiD3D11);
3882 if (rhiD)
3883 rhiD->unregisterResource(this);
3884}
3885
3886static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
3887{
3888 if (minFilter == QRhiSampler::Nearest) {
3889 if (magFilter == QRhiSampler::Nearest) {
3890 if (mipFilter == QRhiSampler::Linear)
3891 return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
3892 else
3893 return D3D11_FILTER_MIN_MAG_MIP_POINT;
3894 } else {
3895 if (mipFilter == QRhiSampler::Linear)
3896 return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
3897 else
3898 return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
3899 }
3900 } else {
3901 if (magFilter == QRhiSampler::Nearest) {
3902 if (mipFilter == QRhiSampler::Linear)
3903 return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
3904 else
3905 return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
3906 } else {
3907 if (mipFilter == QRhiSampler::Linear)
3908 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3909 else
3910 return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
3911 }
3912 }
3913
3914 Q_UNREACHABLE();
3915 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3916}
3917
3918static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
3919{
3920 switch (m) {
3921 case QRhiSampler::Repeat:
3922 return D3D11_TEXTURE_ADDRESS_WRAP;
3923 case QRhiSampler::ClampToEdge:
3924 return D3D11_TEXTURE_ADDRESS_CLAMP;
3925 case QRhiSampler::Mirror:
3926 return D3D11_TEXTURE_ADDRESS_MIRROR;
3927 default:
3928 Q_UNREACHABLE();
3929 return D3D11_TEXTURE_ADDRESS_CLAMP;
3930 }
3931}
3932
3933static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
3934{
3935 switch (op) {
3936 case QRhiSampler::Never:
3937 return D3D11_COMPARISON_NEVER;
3938 case QRhiSampler::Less:
3939 return D3D11_COMPARISON_LESS;
3940 case QRhiSampler::Equal:
3941 return D3D11_COMPARISON_EQUAL;
3942 case QRhiSampler::LessOrEqual:
3943 return D3D11_COMPARISON_LESS_EQUAL;
3944 case QRhiSampler::Greater:
3945 return D3D11_COMPARISON_GREATER;
3946 case QRhiSampler::NotEqual:
3947 return D3D11_COMPARISON_NOT_EQUAL;
3948 case QRhiSampler::GreaterOrEqual:
3949 return D3D11_COMPARISON_GREATER_EQUAL;
3950 case QRhiSampler::Always:
3951 return D3D11_COMPARISON_ALWAYS;
3952 default:
3953 Q_UNREACHABLE();
3954 return D3D11_COMPARISON_NEVER;
3955 }
3956}
3957
3959{
3960 if (samplerState)
3961 destroy();
3962
3963 D3D11_SAMPLER_DESC desc = {};
3964 desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
3965 if (m_compareOp != Never)
3966 desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
3967 desc.AddressU = toD3DAddressMode(m_addressU);
3968 desc.AddressV = toD3DAddressMode(m_addressV);
3969 desc.AddressW = toD3DAddressMode(m_addressW);
3970 desc.MaxAnisotropy = 1.0f;
3971 desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
3972 desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f;
3973
3974 QRHI_RES_RHI(QRhiD3D11);
3975 HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
3976 if (FAILED(hr)) {
3977 qWarning("Failed to create sampler state: %s",
3978 qPrintable(QSystemError::windowsComString(hr)));
3979 return false;
3980 }
3981
3982 generation += 1;
3983 rhiD->registerResource(this);
3984 return true;
3985}
3986
3987// dummy, no Vulkan-style RenderPass+Framebuffer concept here
3992
3997
3999{
4000 QRHI_RES_RHI(QRhiD3D11);
4001 if (rhiD)
4002 rhiD->unregisterResource(this);
4003}
4004
4005bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
4006{
4007 Q_UNUSED(other);
4008 return true;
4009}
4010
4012{
4013 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
4014 QRHI_RES_RHI(QRhiD3D11);
4015 rhiD->registerResource(rpD, false);
4016 return rpD;
4017}
4018
4020{
4021 return {};
4022}
4023
4024QD3D11SwapChainRenderTarget::QD3D11SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
4026 d(rhi)
4027{
4028}
4029
4034
4036{
4037 // nothing to do here
4038}
4039
4041{
4042 return d.pixelSize;
4043}
4044
4046{
4047 return d.dpr;
4048}
4049
4051{
4052 return d.sampleCount;
4053}
4054
4056 const QRhiTextureRenderTargetDescription &desc,
4057 Flags flags)
4059 d(rhi)
4060{
4061 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
4062 ownsRtv[i] = false;
4063 rtv[i] = nullptr;
4064 }
4065}
4066
4071
4073{
4074 if (!rtv[0] && !dsv)
4075 return;
4076
4077 if (dsv) {
4078 if (ownsDsv)
4079 dsv->Release();
4080 dsv = nullptr;
4081 }
4082
4083 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
4084 if (rtv[i]) {
4085 if (ownsRtv[i])
4086 rtv[i]->Release();
4087 rtv[i] = nullptr;
4088 }
4089 }
4090
4091 QRHI_RES_RHI(QRhiD3D11);
4092 if (rhiD)
4093 rhiD->unregisterResource(this);
4094}
4095
4097{
4098 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
4099 QRHI_RES_RHI(QRhiD3D11);
4100 rhiD->registerResource(rpD, false);
4101 return rpD;
4102}
4103
4105{
4106 if (rtv[0] || dsv)
4107 destroy();
4108
4109 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4110 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4111 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4112
4113 QRHI_RES_RHI(QRhiD3D11);
4114
4115 d.colorAttCount = 0;
4116 int attIndex = 0;
4117 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4118 d.colorAttCount += 1;
4119 const QRhiColorAttachment &colorAtt(*it);
4120 QRhiTexture *texture = colorAtt.texture();
4121 QRhiRenderBuffer *rb = colorAtt.renderBuffer();
4122 Q_ASSERT(texture || rb);
4123 if (texture) {
4124 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
4125 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
4126 rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
4127 if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
4128 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
4129 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
4130 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
4131 rtvDesc.Texture2DArray.ArraySize = 1;
4132 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
4133 if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
4134 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
4135 rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
4136 rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
4137 rtvDesc.Texture1DArray.ArraySize = 1;
4138 } else {
4139 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
4140 rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
4141 }
4142 } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
4143 if (texD->sampleDesc.Count > 1) {
4144 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
4145 rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
4146 rtvDesc.Texture2DMSArray.ArraySize = 1;
4147 } else {
4148 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
4149 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
4150 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
4151 rtvDesc.Texture2DArray.ArraySize = 1;
4152 }
4153 } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
4154 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
4155 rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
4156 rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
4157 rtvDesc.Texture3D.WSize = 1;
4158 } else {
4159 if (texD->sampleDesc.Count > 1) {
4160 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
4161 } else {
4162 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
4163 rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
4164 }
4165 }
4166 HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->textureResource(), &rtvDesc, &rtv[attIndex]);
4167 if (FAILED(hr)) {
4168 qWarning("Failed to create rtv: %s",
4169 qPrintable(QSystemError::windowsComString(hr)));
4170 return false;
4171 }
4172 ownsRtv[attIndex] = true;
4173 if (attIndex == 0) {
4174 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
4175 d.sampleCount = int(texD->sampleDesc.Count);
4176 }
4177 } else if (rb) {
4178 QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
4179 ownsRtv[attIndex] = false;
4180 rtv[attIndex] = rbD->rtv;
4181 if (attIndex == 0) {
4182 d.pixelSize = rbD->pixelSize();
4183 d.sampleCount = int(rbD->sampleDesc.Count);
4184 }
4185 }
4186 }
4187 d.dpr = 1;
4188
4189 if (hasDepthStencil) {
4190 if (m_desc.depthTexture()) {
4191 ownsDsv = true;
4192 QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture());
4193 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
4194 dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
4195 dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
4196 : D3D11_DSV_DIMENSION_TEXTURE2D;
4197 if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
4198 if (depthTexD->sampleDesc.Count > 1) {
4199 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
4200 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
4201 dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
4202 dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
4203 } else {
4204 dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
4205 dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
4206 }
4207 } else {
4208 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
4209 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
4210 dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
4211 dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
4212 } else {
4213 dsvDesc.Texture2DArray.FirstArraySlice = 0;
4214 dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
4215 }
4216 }
4217 }
4218 HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
4219 if (FAILED(hr)) {
4220 qWarning("Failed to create dsv: %s",
4221 qPrintable(QSystemError::windowsComString(hr)));
4222 return false;
4223 }
4224 if (d.colorAttCount == 0) {
4225 d.pixelSize = depthTexD->pixelSize();
4226 d.sampleCount = int(depthTexD->sampleDesc.Count);
4227 }
4228 } else {
4229 ownsDsv = false;
4230 QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer());
4231 dsv = depthRbD->dsv;
4232 if (d.colorAttCount == 0) {
4233 d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
4234 d.sampleCount = int(depthRbD->sampleDesc.Count);
4235 }
4236 }
4237 d.dsAttCount = 1;
4238 } else {
4239 d.dsAttCount = 0;
4240 }
4241
4242 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
4243 d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr;
4244
4245 d.dsv = dsv;
4246 d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
4247
4248 QRhiRenderTargetAttachmentTracker::updateResIdList<QD3D11Texture, QD3D11RenderBuffer>(m_desc, &d.currentResIdList);
4249
4250 rhiD->registerResource(this);
4251 return true;
4252}
4253
4255{
4256 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D11Texture, QD3D11RenderBuffer>(m_desc, d.currentResIdList))
4257 const_cast<QD3D11TextureRenderTarget *>(this)->create();
4258
4259 return d.pixelSize;
4260}
4261
4263{
4264 return d.dpr;
4265}
4266
4268{
4269 return d.sampleCount;
4270}
4271
4276
4281
4283{
4284 sortedBindings.clear();
4285 boundResourceData.clear();
4286
4287 QRHI_RES_RHI(QRhiD3D11);
4288 if (rhiD)
4289 rhiD->unregisterResource(this);
4290}
4291
4293{
4294 if (!sortedBindings.isEmpty())
4295 destroy();
4296
4297 QRHI_RES_RHI(QRhiD3D11);
4298 if (!rhiD->sanityCheckShaderResourceBindings(this))
4299 return false;
4300
4301 rhiD->updateLayoutDesc(this);
4302
4303 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4304 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4305
4306 boundResourceData.resize(sortedBindings.count());
4307
4308 for (BoundResourceData &bd : boundResourceData)
4309 memset(&bd, 0, sizeof(BoundResourceData));
4310
4311 hasDynamicOffset = false;
4312 for (const QRhiShaderResourceBinding &b : sortedBindings) {
4313 const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
4314 if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
4315 hasDynamicOffset = true;
4316 break;
4317 }
4318 }
4319
4320 generation += 1;
4321 rhiD->registerResource(this, false);
4322 return true;
4323}
4324
4326{
4327 sortedBindings.clear();
4328 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4329 if (!flags.testFlag(BindingsAreSorted))
4330 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4331
4332 Q_ASSERT(boundResourceData.count() == sortedBindings.count());
4333 for (BoundResourceData &bd : boundResourceData)
4334 memset(&bd, 0, sizeof(BoundResourceData));
4335
4336 generation += 1;
4337}
4338
4341{
4342}
4343
4348
4349template<typename T>
4350inline void releasePipelineShader(T &s)
4351{
4352 if (s.shader) {
4353 s.shader->Release();
4354 s.shader = nullptr;
4355 }
4356 s.nativeResourceBindingMap.clear();
4357}
4358
4360{
4361 if (!dsState)
4362 return;
4363
4364 dsState->Release();
4365 dsState = nullptr;
4366
4367 if (blendState) {
4368 blendState->Release();
4369 blendState = nullptr;
4370 }
4371
4372 if (inputLayout) {
4373 inputLayout->Release();
4374 inputLayout = nullptr;
4375 }
4376
4377 if (rastState) {
4378 rastState->Release();
4379 rastState = nullptr;
4380 }
4381
4382 releasePipelineShader(vs);
4383 releasePipelineShader(hs);
4384 releasePipelineShader(ds);
4385 releasePipelineShader(gs);
4386 releasePipelineShader(fs);
4387
4388 QRHI_RES_RHI(QRhiD3D11);
4389 if (rhiD)
4390 rhiD->unregisterResource(this);
4391}
4392
4393static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
4394{
4395 switch (c) {
4396 case QRhiGraphicsPipeline::None:
4397 return D3D11_CULL_NONE;
4398 case QRhiGraphicsPipeline::Front:
4399 return D3D11_CULL_FRONT;
4400 case QRhiGraphicsPipeline::Back:
4401 return D3D11_CULL_BACK;
4402 default:
4403 Q_UNREACHABLE();
4404 return D3D11_CULL_NONE;
4405 }
4406}
4407
4408static inline D3D11_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
4409{
4410 switch (mode) {
4411 case QRhiGraphicsPipeline::Fill:
4412 return D3D11_FILL_SOLID;
4413 case QRhiGraphicsPipeline::Line:
4414 return D3D11_FILL_WIREFRAME;
4415 default:
4416 Q_UNREACHABLE();
4417 return D3D11_FILL_SOLID;
4418 }
4419}
4420
4421static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
4422{
4423 switch (op) {
4424 case QRhiGraphicsPipeline::Never:
4425 return D3D11_COMPARISON_NEVER;
4426 case QRhiGraphicsPipeline::Less:
4427 return D3D11_COMPARISON_LESS;
4428 case QRhiGraphicsPipeline::Equal:
4429 return D3D11_COMPARISON_EQUAL;
4430 case QRhiGraphicsPipeline::LessOrEqual:
4431 return D3D11_COMPARISON_LESS_EQUAL;
4432 case QRhiGraphicsPipeline::Greater:
4433 return D3D11_COMPARISON_GREATER;
4434 case QRhiGraphicsPipeline::NotEqual:
4435 return D3D11_COMPARISON_NOT_EQUAL;
4436 case QRhiGraphicsPipeline::GreaterOrEqual:
4437 return D3D11_COMPARISON_GREATER_EQUAL;
4438 case QRhiGraphicsPipeline::Always:
4439 return D3D11_COMPARISON_ALWAYS;
4440 default:
4441 Q_UNREACHABLE();
4442 return D3D11_COMPARISON_ALWAYS;
4443 }
4444}
4445
4446static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
4447{
4448 switch (op) {
4449 case QRhiGraphicsPipeline::StencilZero:
4450 return D3D11_STENCIL_OP_ZERO;
4451 case QRhiGraphicsPipeline::Keep:
4452 return D3D11_STENCIL_OP_KEEP;
4453 case QRhiGraphicsPipeline::Replace:
4454 return D3D11_STENCIL_OP_REPLACE;
4455 case QRhiGraphicsPipeline::IncrementAndClamp:
4456 return D3D11_STENCIL_OP_INCR_SAT;
4457 case QRhiGraphicsPipeline::DecrementAndClamp:
4458 return D3D11_STENCIL_OP_DECR_SAT;
4459 case QRhiGraphicsPipeline::Invert:
4460 return D3D11_STENCIL_OP_INVERT;
4461 case QRhiGraphicsPipeline::IncrementAndWrap:
4462 return D3D11_STENCIL_OP_INCR;
4463 case QRhiGraphicsPipeline::DecrementAndWrap:
4464 return D3D11_STENCIL_OP_DECR;
4465 default:
4466 Q_UNREACHABLE();
4467 return D3D11_STENCIL_OP_KEEP;
4468 }
4469}
4470
4471static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
4472{
4473 switch (format) {
4474 case QRhiVertexInputAttribute::Float4:
4475 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4476 case QRhiVertexInputAttribute::Float3:
4477 return DXGI_FORMAT_R32G32B32_FLOAT;
4478 case QRhiVertexInputAttribute::Float2:
4479 return DXGI_FORMAT_R32G32_FLOAT;
4480 case QRhiVertexInputAttribute::Float:
4481 return DXGI_FORMAT_R32_FLOAT;
4482 case QRhiVertexInputAttribute::UNormByte4:
4483 return DXGI_FORMAT_R8G8B8A8_UNORM;
4484 case QRhiVertexInputAttribute::UNormByte2:
4485 return DXGI_FORMAT_R8G8_UNORM;
4486 case QRhiVertexInputAttribute::UNormByte:
4487 return DXGI_FORMAT_R8_UNORM;
4488 case QRhiVertexInputAttribute::UInt4:
4489 return DXGI_FORMAT_R32G32B32A32_UINT;
4490 case QRhiVertexInputAttribute::UInt3:
4491 return DXGI_FORMAT_R32G32B32_UINT;
4492 case QRhiVertexInputAttribute::UInt2:
4493 return DXGI_FORMAT_R32G32_UINT;
4494 case QRhiVertexInputAttribute::UInt:
4495 return DXGI_FORMAT_R32_UINT;
4496 case QRhiVertexInputAttribute::SInt4:
4497 return DXGI_FORMAT_R32G32B32A32_SINT;
4498 case QRhiVertexInputAttribute::SInt3:
4499 return DXGI_FORMAT_R32G32B32_SINT;
4500 case QRhiVertexInputAttribute::SInt2:
4501 return DXGI_FORMAT_R32G32_SINT;
4502 case QRhiVertexInputAttribute::SInt:
4503 return DXGI_FORMAT_R32_SINT;
4504 case QRhiVertexInputAttribute::Half4:
4505 // Note: D3D does not support half3. Pass through half3 as half4.
4506 case QRhiVertexInputAttribute::Half3:
4507 return DXGI_FORMAT_R16G16B16A16_FLOAT;
4508 case QRhiVertexInputAttribute::Half2:
4509 return DXGI_FORMAT_R16G16_FLOAT;
4510 case QRhiVertexInputAttribute::Half:
4511 return DXGI_FORMAT_R16_FLOAT;
4512 case QRhiVertexInputAttribute::UShort4:
4513 // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
4514 case QRhiVertexInputAttribute::UShort3:
4515 return DXGI_FORMAT_R16G16B16A16_UINT;
4516 case QRhiVertexInputAttribute::UShort2:
4517 return DXGI_FORMAT_R16G16_UINT;
4518 case QRhiVertexInputAttribute::UShort:
4519 return DXGI_FORMAT_R16_UINT;
4520 case QRhiVertexInputAttribute::SShort4:
4521 // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
4522 case QRhiVertexInputAttribute::SShort3:
4523 return DXGI_FORMAT_R16G16B16A16_SINT;
4524 case QRhiVertexInputAttribute::SShort2:
4525 return DXGI_FORMAT_R16G16_SINT;
4526 case QRhiVertexInputAttribute::SShort:
4527 return DXGI_FORMAT_R16_SINT;
4528 default:
4529 Q_UNREACHABLE();
4530 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4531 }
4532}
4533
4534static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
4535{
4536 switch (t) {
4537 case QRhiGraphicsPipeline::Triangles:
4538 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
4539 case QRhiGraphicsPipeline::TriangleStrip:
4540 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
4541 case QRhiGraphicsPipeline::Lines:
4542 return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
4543 case QRhiGraphicsPipeline::LineStrip:
4544 return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
4545 case QRhiGraphicsPipeline::Points:
4546 return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
4547 case QRhiGraphicsPipeline::Patches:
4548 Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
4549 return D3D11_PRIMITIVE_TOPOLOGY(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
4550 default:
4551 Q_UNREACHABLE();
4552 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
4553 }
4554}
4555
4556static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
4557{
4558 UINT8 f = 0;
4559 if (c.testFlag(QRhiGraphicsPipeline::R))
4560 f |= D3D11_COLOR_WRITE_ENABLE_RED;
4561 if (c.testFlag(QRhiGraphicsPipeline::G))
4562 f |= D3D11_COLOR_WRITE_ENABLE_GREEN;
4563 if (c.testFlag(QRhiGraphicsPipeline::B))
4564 f |= D3D11_COLOR_WRITE_ENABLE_BLUE;
4565 if (c.testFlag(QRhiGraphicsPipeline::A))
4566 f |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
4567 return f;
4568}
4569
4570static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
4571{
4572 // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
4573 // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
4574 // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
4575 // set as an alpha src/dest factor), but for D3D we have to take care of it
4576 // ourselves. Hence the rgb argument.
4577
4578 switch (f) {
4579 case QRhiGraphicsPipeline::Zero:
4580 return D3D11_BLEND_ZERO;
4581 case QRhiGraphicsPipeline::One:
4582 return D3D11_BLEND_ONE;
4583 case QRhiGraphicsPipeline::SrcColor:
4584 return rgb ? D3D11_BLEND_SRC_COLOR : D3D11_BLEND_SRC_ALPHA;
4585 case QRhiGraphicsPipeline::OneMinusSrcColor:
4586 return rgb ? D3D11_BLEND_INV_SRC_COLOR : D3D11_BLEND_INV_SRC_ALPHA;
4587 case QRhiGraphicsPipeline::DstColor:
4588 return rgb ? D3D11_BLEND_DEST_COLOR : D3D11_BLEND_DEST_ALPHA;
4589 case QRhiGraphicsPipeline::OneMinusDstColor:
4590 return rgb ? D3D11_BLEND_INV_DEST_COLOR : D3D11_BLEND_INV_DEST_ALPHA;
4591 case QRhiGraphicsPipeline::SrcAlpha:
4592 return D3D11_BLEND_SRC_ALPHA;
4593 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4594 return D3D11_BLEND_INV_SRC_ALPHA;
4595 case QRhiGraphicsPipeline::DstAlpha:
4596 return D3D11_BLEND_DEST_ALPHA;
4597 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4598 return D3D11_BLEND_INV_DEST_ALPHA;
4599 case QRhiGraphicsPipeline::ConstantColor:
4600 case QRhiGraphicsPipeline::ConstantAlpha:
4601 return D3D11_BLEND_BLEND_FACTOR;
4602 case QRhiGraphicsPipeline::OneMinusConstantColor:
4603 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4604 return D3D11_BLEND_INV_BLEND_FACTOR;
4605 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4606 return D3D11_BLEND_SRC_ALPHA_SAT;
4607 case QRhiGraphicsPipeline::Src1Color:
4608 return rgb ? D3D11_BLEND_SRC1_COLOR : D3D11_BLEND_SRC1_ALPHA;
4609 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4610 return rgb ? D3D11_BLEND_INV_SRC1_COLOR : D3D11_BLEND_INV_SRC1_ALPHA;
4611 case QRhiGraphicsPipeline::Src1Alpha:
4612 return D3D11_BLEND_SRC1_ALPHA;
4613 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4614 return D3D11_BLEND_INV_SRC1_ALPHA;
4615 default:
4616 Q_UNREACHABLE();
4617 return D3D11_BLEND_ZERO;
4618 }
4619}
4620
4621static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
4622{
4623 switch (op) {
4624 case QRhiGraphicsPipeline::Add:
4625 return D3D11_BLEND_OP_ADD;
4626 case QRhiGraphicsPipeline::Subtract:
4627 return D3D11_BLEND_OP_SUBTRACT;
4628 case QRhiGraphicsPipeline::ReverseSubtract:
4629 return D3D11_BLEND_OP_REV_SUBTRACT;
4630 case QRhiGraphicsPipeline::Min:
4631 return D3D11_BLEND_OP_MIN;
4632 case QRhiGraphicsPipeline::Max:
4633 return D3D11_BLEND_OP_MAX;
4634 default:
4635 Q_UNREACHABLE();
4636 return D3D11_BLEND_OP_ADD;
4637 }
4638}
4639
4640static inline QByteArray sourceHash(const QByteArray &source)
4641{
4642 // taken from the GL backend, use the same mechanism to get a key
4643 QCryptographicHash keyBuilder(QCryptographicHash::Sha1);
4644 keyBuilder.addData(source);
4645 return keyBuilder.result().toHex();
4646}
4647
4648QByteArray QRhiD3D11::compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, uint flags,
4649 QString *error, QShaderKey *usedShaderKey)
4650{
4651 QShaderKey key = { QShader::DxbcShader, 50, shaderVariant };
4652 QShaderCode dxbc = shader.shader(key);
4653 if (!dxbc.shader().isEmpty()) {
4654 if (usedShaderKey)
4655 *usedShaderKey = key;
4656 return dxbc.shader();
4657 }
4658
4659 key = { QShader::HlslShader, 50, shaderVariant };
4660 QShaderCode hlslSource = shader.shader(key);
4661 if (hlslSource.shader().isEmpty()) {
4662 qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
4663 return QByteArray();
4664 }
4665
4666 if (usedShaderKey)
4667 *usedShaderKey = key;
4668
4669 const char *target;
4670 switch (shader.stage()) {
4671 case QShader::VertexStage:
4672 target = "vs_5_0";
4673 break;
4674 case QShader::TessellationControlStage:
4675 target = "hs_5_0";
4676 break;
4677 case QShader::TessellationEvaluationStage:
4678 target = "ds_5_0";
4679 break;
4680 case QShader::GeometryStage:
4681 target = "gs_5_0";
4682 break;
4683 case QShader::FragmentStage:
4684 target = "ps_5_0";
4685 break;
4686 case QShader::ComputeStage:
4687 target = "cs_5_0";
4688 break;
4689 default:
4690 Q_UNREACHABLE();
4691 return QByteArray();
4692 }
4693
4694 BytecodeCacheKey cacheKey;
4695 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
4696 cacheKey.sourceHash = sourceHash(hlslSource.shader());
4697 cacheKey.target = target;
4698 cacheKey.entryPoint = hlslSource.entryPoint();
4699 cacheKey.compileFlags = flags;
4700 auto cacheIt = m_bytecodeCache.constFind(cacheKey);
4701 if (cacheIt != m_bytecodeCache.constEnd())
4702 return cacheIt.value();
4703 }
4704
4705 static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
4706 if (d3dCompile == nullptr) {
4707 qWarning("Unable to resolve function D3DCompile()");
4708 return QByteArray();
4709 }
4710
4711 ID3DBlob *bytecode = nullptr;
4712 ID3DBlob *errors = nullptr;
4713 HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
4714 nullptr, nullptr, nullptr,
4715 hlslSource.entryPoint().constData(), target, flags, 0, &bytecode, &errors);
4716 if (FAILED(hr) || !bytecode) {
4717 qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
4718 if (errors) {
4719 *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
4720 int(errors->GetBufferSize()));
4721 errors->Release();
4722 }
4723 return QByteArray();
4724 }
4725
4726 QByteArray result;
4727 result.resize(int(bytecode->GetBufferSize()));
4728 memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
4729 bytecode->Release();
4730
4731 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
4732 m_bytecodeCache.insert(cacheKey, result);
4733
4734 return result;
4735}
4736
4738{
4739 if (dsState)
4740 destroy();
4741
4742 QRHI_RES_RHI(QRhiD3D11);
4743 rhiD->pipelineCreationStart();
4744 if (!rhiD->sanityCheckGraphicsPipeline(this))
4745 return false;
4746
4747 D3D11_RASTERIZER_DESC rastDesc = {};
4748 rastDesc.FillMode = toD3DFillMode(m_polygonMode);
4749 rastDesc.CullMode = toD3DCullMode(m_cullMode);
4750 rastDesc.FrontCounterClockwise = m_frontFace == CCW;
4751 rastDesc.DepthBias = m_depthBias;
4752 rastDesc.SlopeScaledDepthBias = m_slopeScaledDepthBias;
4753 rastDesc.DepthClipEnable = m_depthClamp ? FALSE : TRUE;
4754 rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
4755 rastDesc.MultisampleEnable = rhiD->effectiveSampleDesc(m_sampleCount).Count > 1;
4756 HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
4757 if (FAILED(hr)) {
4758 qWarning("Failed to create rasterizer state: %s",
4759 qPrintable(QSystemError::windowsComString(hr)));
4760 return false;
4761 }
4762
4763 D3D11_DEPTH_STENCIL_DESC dsDesc = {};
4764 dsDesc.DepthEnable = m_depthTest;
4765 dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
4766 dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
4767 dsDesc.StencilEnable = m_stencilTest;
4768 if (m_stencilTest) {
4769 dsDesc.StencilReadMask = UINT8(m_stencilReadMask);
4770 dsDesc.StencilWriteMask = UINT8(m_stencilWriteMask);
4771 dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
4772 dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
4773 dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
4774 dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
4775 dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
4776 dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
4777 dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
4778 dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
4779 }
4780 hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
4781 if (FAILED(hr)) {
4782 qWarning("Failed to create depth-stencil state: %s",
4783 qPrintable(QSystemError::windowsComString(hr)));
4784 return false;
4785 }
4786
4787 D3D11_BLEND_DESC blendDesc = {};
4788 blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1;
4789 for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
4790 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
4791 D3D11_RENDER_TARGET_BLEND_DESC blend = {};
4792 blend.BlendEnable = b.enable;
4793 blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
4794 blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
4795 blend.BlendOp = toD3DBlendOp(b.opColor);
4796 blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
4797 blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
4798 blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
4799 blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
4800 blendDesc.RenderTarget[i] = blend;
4801 }
4802 if (m_targetBlends.isEmpty()) {
4803 D3D11_RENDER_TARGET_BLEND_DESC blend = {};
4804 blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
4805 blendDesc.RenderTarget[0] = blend;
4806 }
4807 hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState);
4808 if (FAILED(hr)) {
4809 qWarning("Failed to create blend state: %s",
4810 qPrintable(QSystemError::windowsComString(hr)));
4811 return false;
4812 }
4813
4814 QByteArray vsByteCode;
4815 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
4816 auto cacheIt = rhiD->m_shaderCache.constFind(shaderStage);
4817 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
4818 switch (shaderStage.type()) {
4819 case QRhiShaderStage::Vertex:
4820 vs.shader = static_cast<ID3D11VertexShader *>(cacheIt->s);
4821 vs.shader->AddRef();
4822 vsByteCode = cacheIt->bytecode;
4823 vs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4824 break;
4825 case QRhiShaderStage::TessellationControl:
4826 hs.shader = static_cast<ID3D11HullShader *>(cacheIt->s);
4827 hs.shader->AddRef();
4828 hs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4829 break;
4830 case QRhiShaderStage::TessellationEvaluation:
4831 ds.shader = static_cast<ID3D11DomainShader *>(cacheIt->s);
4832 ds.shader->AddRef();
4833 ds.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4834 break;
4835 case QRhiShaderStage::Geometry:
4836 gs.shader = static_cast<ID3D11GeometryShader *>(cacheIt->s);
4837 gs.shader->AddRef();
4838 gs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4839 break;
4840 case QRhiShaderStage::Fragment:
4841 fs.shader = static_cast<ID3D11PixelShader *>(cacheIt->s);
4842 fs.shader->AddRef();
4843 fs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4844 break;
4845 default:
4846 break;
4847 }
4848 } else {
4849 QString error;
4850 QShaderKey shaderKey;
4851 UINT compileFlags = 0;
4852 if (m_flags.testFlag(CompileShadersWithDebugInfo))
4853 compileFlags |= D3DCOMPILE_DEBUG;
4854
4855 const QByteArray bytecode = rhiD->compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), compileFlags,
4856 &error, &shaderKey);
4857 if (bytecode.isEmpty()) {
4858 qWarning("HLSL shader compilation failed: %s", qPrintable(error));
4859 return false;
4860 }
4861
4862 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES) {
4863 // Use the simplest strategy: too many cached shaders -> drop them all.
4864 rhiD->clearShaderCache();
4865 }
4866
4867 switch (shaderStage.type()) {
4868 case QRhiShaderStage::Vertex:
4869 hr = rhiD->dev->CreateVertexShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &vs.shader);
4870 if (FAILED(hr)) {
4871 qWarning("Failed to create vertex shader: %s",
4872 qPrintable(QSystemError::windowsComString(hr)));
4873 return false;
4874 }
4875 vsByteCode = bytecode;
4876 vs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4877 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs.shader, bytecode, vs.nativeResourceBindingMap));
4878 vs.shader->AddRef();
4879 break;
4880 case QRhiShaderStage::TessellationControl:
4881 hr = rhiD->dev->CreateHullShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &hs.shader);
4882 if (FAILED(hr)) {
4883 qWarning("Failed to create hull shader: %s",
4884 qPrintable(QSystemError::windowsComString(hr)));
4885 return false;
4886 }
4887 hs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4888 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(hs.shader, bytecode, hs.nativeResourceBindingMap));
4889 hs.shader->AddRef();
4890 break;
4891 case QRhiShaderStage::TessellationEvaluation:
4892 hr = rhiD->dev->CreateDomainShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &ds.shader);
4893 if (FAILED(hr)) {
4894 qWarning("Failed to create domain shader: %s",
4895 qPrintable(QSystemError::windowsComString(hr)));
4896 return false;
4897 }
4898 ds.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4899 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(ds.shader, bytecode, ds.nativeResourceBindingMap));
4900 ds.shader->AddRef();
4901 break;
4902 case QRhiShaderStage::Geometry:
4903 hr = rhiD->dev->CreateGeometryShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &gs.shader);
4904 if (FAILED(hr)) {
4905 qWarning("Failed to create geometry shader: %s",
4906 qPrintable(QSystemError::windowsComString(hr)));
4907 return false;
4908 }
4909 gs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4910 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(gs.shader, bytecode, gs.nativeResourceBindingMap));
4911 gs.shader->AddRef();
4912 break;
4913 case QRhiShaderStage::Fragment:
4914 hr = rhiD->dev->CreatePixelShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &fs.shader);
4915 if (FAILED(hr)) {
4916 qWarning("Failed to create pixel shader: %s",
4917 qPrintable(QSystemError::windowsComString(hr)));
4918 return false;
4919 }
4920 fs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4921 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(fs.shader, bytecode, fs.nativeResourceBindingMap));
4922 fs.shader->AddRef();
4923 break;
4924 default:
4925 break;
4926 }
4927 }
4928 }
4929
4930 d3dTopology = toD3DTopology(m_topology, m_patchControlPointCount);
4931
4932 if (!vsByteCode.isEmpty()) {
4933 QByteArrayList matrixSliceSemantics;
4934 QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
4935 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
4936 it != itEnd; ++it)
4937 {
4938 D3D11_INPUT_ELEMENT_DESC desc = {};
4939 // The output from SPIRV-Cross uses TEXCOORD<location> as the
4940 // semantic, except for matrices that are unrolled into consecutive
4941 // vec2/3/4s attributes and need TEXCOORD<location>_ as
4942 // SemanticName and row/column index as SemanticIndex.
4943 const int matrixSlice = it->matrixSlice();
4944 if (matrixSlice < 0) {
4945 desc.SemanticName = "TEXCOORD";
4946 desc.SemanticIndex = UINT(it->location());
4947 } else {
4948 QByteArray sem;
4949 sem.resize(16);
4950 std::snprintf(sem.data(), sem.size(), "TEXCOORD%d_", it->location() - matrixSlice);
4951 matrixSliceSemantics.append(sem);
4952 desc.SemanticName = matrixSliceSemantics.last().constData();
4953 desc.SemanticIndex = UINT(matrixSlice);
4954 }
4955 desc.Format = toD3DAttributeFormat(it->format());
4956 desc.InputSlot = UINT(it->binding());
4957 desc.AlignedByteOffset = it->offset();
4958 const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
4959 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
4960 desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
4961 desc.InstanceDataStepRate = inputBinding->instanceStepRate();
4962 } else {
4963 desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
4964 }
4965 inputDescs.append(desc);
4966 }
4967 if (!inputDescs.isEmpty()) {
4968 hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), UINT(inputDescs.count()),
4969 vsByteCode, SIZE_T(vsByteCode.size()), &inputLayout);
4970 if (FAILED(hr)) {
4971 qWarning("Failed to create input layout: %s",
4972 qPrintable(QSystemError::windowsComString(hr)));
4973 return false;
4974 }
4975 } // else leave inputLayout set to nullptr; that's valid and it avoids a debug layer warning about an input layout with 0 elements
4976 }
4977
4978 rhiD->pipelineCreationEnd();
4979 generation += 1;
4980 rhiD->registerResource(this);
4981 return true;
4982}
4983
4986{
4987}
4988
4993
4995{
4996 if (!cs.shader)
4997 return;
4998
4999 cs.shader->Release();
5000 cs.shader = nullptr;
5001 cs.nativeResourceBindingMap.clear();
5002
5003 QRHI_RES_RHI(QRhiD3D11);
5004 if (rhiD)
5005 rhiD->unregisterResource(this);
5006}
5007
5009{
5010 if (cs.shader)
5011 destroy();
5012
5013 QRHI_RES_RHI(QRhiD3D11);
5014 rhiD->pipelineCreationStart();
5015
5016 auto cacheIt = rhiD->m_shaderCache.constFind(m_shaderStage);
5017 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
5018 cs.shader = static_cast<ID3D11ComputeShader *>(cacheIt->s);
5019 cs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
5020 } else {
5021 QString error;
5022 QShaderKey shaderKey;
5023 UINT compileFlags = 0;
5024 if (m_flags.testFlag(CompileShadersWithDebugInfo))
5025 compileFlags |= D3DCOMPILE_DEBUG;
5026
5027 const QByteArray bytecode = rhiD->compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), compileFlags,
5028 &error, &shaderKey);
5029 if (bytecode.isEmpty()) {
5030 qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
5031 return false;
5032 }
5033
5034 HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &cs.shader);
5035 if (FAILED(hr)) {
5036 qWarning("Failed to create compute shader: %s",
5037 qPrintable(QSystemError::windowsComString(hr)));
5038 return false;
5039 }
5040
5041 cs.nativeResourceBindingMap = m_shaderStage.shader().nativeResourceBindingMap(shaderKey);
5042
5043 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES)
5045
5046 rhiD->m_shaderCache.insert(m_shaderStage, QRhiD3D11::Shader(cs.shader, bytecode, cs.nativeResourceBindingMap));
5047 }
5048
5049 cs.shader->AddRef();
5050
5051 rhiD->pipelineCreationEnd();
5052 generation += 1;
5053 rhiD->registerResource(this);
5054 return true;
5055}
5056
5059{
5061}
5062
5067
5069{
5070 // nothing to do here
5071}
5072
5074{
5075 // Creates the query objects if not yet done, but otherwise calling this
5076 // function is expected to be a no-op.
5077
5078 D3D11_QUERY_DESC queryDesc = {};
5079 for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
5080 if (!disjointQuery[i]) {
5081 queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
5082 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &disjointQuery[i]);
5083 if (FAILED(hr)) {
5084 qWarning("Failed to create timestamp disjoint query: %s",
5085 qPrintable(QSystemError::windowsComString(hr)));
5086 return false;
5087 }
5088 }
5089 queryDesc.Query = D3D11_QUERY_TIMESTAMP;
5090 for (int j = 0; j < 2; ++j) {
5091 const int idx = 2 * i + j;
5092 if (!query[idx]) {
5093 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &query[idx]);
5094 if (FAILED(hr)) {
5095 qWarning("Failed to create timestamp query: %s",
5096 qPrintable(QSystemError::windowsComString(hr)));
5097 return false;
5098 }
5099 }
5100 }
5101 }
5102 return true;
5103}
5104
5106{
5107 for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
5108 active[i] = false;
5109 if (disjointQuery[i]) {
5110 disjointQuery[i]->Release();
5111 disjointQuery[i] = nullptr;
5112 }
5113 for (int j = 0; j < 2; ++j) {
5114 const int idx = TIMESTAMP_PAIRS * i + j;
5115 if (query[idx]) {
5116 query[idx]->Release();
5117 query[idx] = nullptr;
5118 }
5119 }
5120 }
5121}
5122
5123bool QD3D11SwapChainTimestamps::tryQueryTimestamps(int pairIndex, ID3D11DeviceContext *context, double *elapsedSec)
5124{
5125 bool result = false;
5126 if (!active[pairIndex])
5127 return result;
5128
5129 ID3D11Query *tsDisjoint = disjointQuery[pairIndex];
5130 ID3D11Query *tsStart = query[pairIndex * 2];
5131 ID3D11Query *tsEnd = query[pairIndex * 2 + 1];
5132 quint64 timestamps[2];
5133 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
5134
5135 bool ok = true;
5136 ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5137 ok &= context->GetData(tsEnd, &timestamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5138 ok &= context->GetData(tsStart, &timestamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5139
5140 if (ok) {
5141 if (!dj.Disjoint && dj.Frequency) {
5142 const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
5143 *elapsedSec = elapsedMs / 1000.0;
5144 result = true;
5145 }
5146 active[pairIndex] = false;
5147 } // else leave active set, will retry in a subsequent beginFrame
5148
5149 return result;
5150}
5151
5152QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
5153 : QRhiSwapChain(rhi), rt(rhi, this), rtRight(rhi, this), cb(rhi)
5154{
5155 backBufferTex = nullptr;
5156 backBufferRtv = nullptr;
5157 for (int i = 0; i < BUFFER_COUNT; ++i) {
5158 msaaTex[i] = nullptr;
5159 msaaRtv[i] = nullptr;
5160 }
5161}
5162
5167
5169{
5170 if (backBufferRtv) {
5171 backBufferRtv->Release();
5172 backBufferRtv = nullptr;
5173 }
5174 if (backBufferRtvRight) {
5175 backBufferRtvRight->Release();
5176 backBufferRtvRight = nullptr;
5177 }
5178 if (backBufferTex) {
5179 backBufferTex->Release();
5180 backBufferTex = nullptr;
5181 }
5182 for (int i = 0; i < BUFFER_COUNT; ++i) {
5183 if (msaaRtv[i]) {
5184 msaaRtv[i]->Release();
5185 msaaRtv[i] = nullptr;
5186 }
5187 if (msaaTex[i]) {
5188 msaaTex[i]->Release();
5189 msaaTex[i] = nullptr;
5190 }
5191 }
5192}
5193
5195{
5196 if (!swapChain)
5197 return;
5198
5200
5201 timestamps.destroy();
5202
5203 swapChain->Release();
5204 swapChain = nullptr;
5205
5206 if (dcompVisual) {
5207 dcompVisual->Release();
5208 dcompVisual = nullptr;
5209 }
5210
5211 if (dcompTarget) {
5212 dcompTarget->Release();
5213 dcompTarget = nullptr;
5214 }
5215
5216 if (frameLatencyWaitableObject) {
5217 CloseHandle(frameLatencyWaitableObject);
5218 frameLatencyWaitableObject = nullptr;
5219 }
5220
5221 QDxgiVSyncService::instance()->unregisterWindow(window);
5222
5223 QRHI_RES_RHI(QRhiD3D11);
5224 if (rhiD) {
5225 rhiD->unregisterResource(this);
5226 // See Deferred Destruction Issues with Flip Presentation Swap Chains in
5227 // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush
5228 rhiD->context->Flush();
5229 }
5230}
5231
5233{
5234 return &cb;
5235}
5236
5241
5243{
5244 return targetBuffer == StereoTargetBuffer::LeftBuffer? &rt: &rtRight;
5245}
5246
5248{
5249 Q_ASSERT(m_window);
5250 return m_window->size() * m_window->devicePixelRatio();
5251}
5252
5254{
5255 if (f == SDR)
5256 return true;
5257
5258 if (!m_window) {
5259 qWarning("Attempted to call isFormatSupported() without a window set");
5260 return false;
5261 }
5262
5263 QRHI_RES_RHI(QRhiD3D11);
5264 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window))
5265 return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
5266
5267 return false;
5268}
5269
5271{
5272 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
5273 // Must use m_window, not window, given this may be called before createOrResize().
5274 if (m_window) {
5275 QRHI_RES_RHI(QRhiD3D11);
5276 info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window);
5277 }
5278 return info;
5279}
5280
5282{
5283 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
5284 QRHI_RES_RHI(QRhiD3D11);
5285 rhiD->registerResource(rpD, false);
5286 return rpD;
5287}
5288
5289bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
5290 ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
5291{
5292 D3D11_TEXTURE2D_DESC desc = {};
5293 desc.Width = UINT(size.width());
5294 desc.Height = UINT(size.height());
5295 desc.MipLevels = 1;
5296 desc.ArraySize = 1;
5297 desc.Format = format;
5298 desc.SampleDesc = sampleDesc;
5299 desc.Usage = D3D11_USAGE_DEFAULT;
5300 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
5301
5302 QRHI_RES_RHI(QRhiD3D11);
5303 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex);
5304 if (FAILED(hr)) {
5305 qWarning("Failed to create color buffer texture: %s",
5306 qPrintable(QSystemError::windowsComString(hr)));
5307 return false;
5308 }
5309
5310 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5311 rtvDesc.Format = format;
5312 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
5313 hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv);
5314 if (FAILED(hr)) {
5315 qWarning("Failed to create color buffer rtv: %s",
5316 qPrintable(QSystemError::windowsComString(hr)));
5317 (*tex)->Release();
5318 *tex = nullptr;
5319 return false;
5320 }
5321
5322 return true;
5323}
5324
5326{
5327 if (dcompDevice)
5328 return true;
5329
5330 qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
5331 dcompDevice = QRhiD3D::createDirectCompositionDevice();
5332 return dcompDevice ? true : false;
5333}
5334
5335static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
5336static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
5337
5339{
5340 // Can be called multiple times due to window resizes - that is not the
5341 // same as a simple destroy+create (as with other resources). Just need to
5342 // resize the buffers then.
5343
5344 const bool needsRegistration = !window || window != m_window;
5345 const bool stereo = m_window->format().stereo();
5346
5347 // except if the window actually changes
5348 if (window && window != m_window)
5349 destroy();
5350
5351 window = m_window;
5352 m_currentPixelSize = surfacePixelSize();
5353 pixelSize = m_currentPixelSize;
5354
5355 if (pixelSize.isEmpty())
5356 return false;
5357
5358 HWND hwnd = reinterpret_cast<HWND>(window->winId());
5359 HRESULT hr;
5360
5361 QRHI_RES_RHI(QRhiD3D11);
5362
5363 if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
5365 if (!dcompTarget) {
5366 hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
5367 if (FAILED(hr)) {
5368 qWarning("Failed to create Direct Compsition target for the window: %s",
5369 qPrintable(QSystemError::windowsComString(hr)));
5370 }
5371 }
5372 if (dcompTarget && !dcompVisual) {
5373 hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
5374 if (FAILED(hr)) {
5375 qWarning("Failed to create DirectComposition visual: %s",
5376 qPrintable(QSystemError::windowsComString(hr)));
5377 }
5378 }
5379 }
5380 // simple consistency check
5381 if (window->requestedFormat().alphaBufferSize() <= 0)
5382 qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
5383 "This may lead to problems.");
5384 }
5385
5386 swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
5387 swapChainFlags = 0;
5388
5389 // A non-flip swapchain can do Present(0) as expected without
5390 // ALLOW_TEARING, and ALLOW_TEARING is not compatible with it at all so the
5391 // flag must not be set then. Whereas for flip we should use it, if
5392 // supported, to get better results for 'unthrottled' presentation.
5393 if (swapInterval == 0 && rhiD->supportsAllowTearing)
5394 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
5395
5396 // maxFrameLatency 0 means no waitable object usage.
5397 // Ignore it also when NoVSync is on, and when using WARP.
5398 const bool useFrameLatencyWaitableObject = rhiD->maxFrameLatency != 0
5399 && swapInterval != 0
5400 && rhiD->driverInfoStruct.deviceType != QRhiDriverInfo::CpuDevice;
5401
5402 if (useFrameLatencyWaitableObject) {
5403 // the flag is not supported in real fullscreen on D3D11, but perhaps that's fine since we only do borderless
5404 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
5405 }
5406
5407 if (!swapChain) {
5408 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
5409 colorFormat = DEFAULT_FORMAT;
5410 srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
5411
5412 DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
5413 if (m_format != SDR) {
5414 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) {
5415 // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
5416 switch (m_format) {
5417 case HDRExtendedSrgbLinear:
5418 colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
5419 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
5420 srgbAdjustedColorFormat = colorFormat;
5421 break;
5422 case HDR10:
5423 colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
5424 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
5425 srgbAdjustedColorFormat = colorFormat;
5426 break;
5427 default:
5428 break;
5429 }
5430 } else {
5431 // This happens also when Use HDR is set to Off in the Windows
5432 // Display settings. Show a helpful warning, but continue with the
5433 // default non-HDR format.
5434 qWarning("The output associated with the window is not HDR capable "
5435 "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
5436 }
5437 }
5438
5439 // We use a FLIP model swapchain which implies a buffer count of 2
5440 // (as opposed to the old DISCARD with back buffer count == 1).
5441 // This makes no difference for the rest of the stuff except that
5442 // automatic MSAA is unsupported and needs to be implemented via a
5443 // custom multisample render target and an explicit resolve.
5444
5445 DXGI_SWAP_CHAIN_DESC1 desc = {};
5446 desc.Width = UINT(pixelSize.width());
5447 desc.Height = UINT(pixelSize.height());
5448 desc.Format = colorFormat;
5449 desc.SampleDesc.Count = 1;
5450 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
5451 desc.BufferCount = BUFFER_COUNT;
5452 desc.Flags = swapChainFlags;
5453 desc.Scaling = rhiD->useLegacySwapchainModel ? DXGI_SCALING_STRETCH : DXGI_SCALING_NONE;
5454 desc.SwapEffect = rhiD->useLegacySwapchainModel ? DXGI_SWAP_EFFECT_DISCARD : DXGI_SWAP_EFFECT_FLIP_DISCARD;
5455 desc.Stereo = stereo;
5456
5457 if (dcompVisual) {
5458 // With DirectComposition setting AlphaMode to STRAIGHT fails the
5459 // swapchain creation, whereas the result seems to be identical
5460 // with any of the other values, including IGNORE. (?)
5461 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
5462
5463 // DirectComposition has its own limitations, cannot use
5464 // SCALING_NONE. So with semi-transparency requested we are forced
5465 // to SCALING_STRETCH.
5466 desc.Scaling = DXGI_SCALING_STRETCH;
5467 }
5468
5469 IDXGIFactory2 *fac = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory);
5470 IDXGISwapChain1 *sc1;
5471
5472 if (dcompVisual)
5473 hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
5474 else
5475 hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
5476
5477 // If failed and we tried a HDR format, then try with SDR. This
5478 // matches other backends, such as Vulkan where if the format is
5479 // not supported, the default one is used instead.
5480 if (FAILED(hr) && m_format != SDR) {
5481 colorFormat = DEFAULT_FORMAT;
5482 desc.Format = DEFAULT_FORMAT;
5483 if (dcompVisual)
5484 hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
5485 else
5486 hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
5487 }
5488
5489 if (SUCCEEDED(hr)) {
5490 swapChain = sc1;
5491 IDXGISwapChain3 *sc3 = nullptr;
5492 if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
5493 if (m_format != SDR) {
5494 hr = sc3->SetColorSpace1(hdrColorSpace);
5495 if (FAILED(hr))
5496 qWarning("Failed to set color space on swapchain: %s",
5497 qPrintable(QSystemError::windowsComString(hr)));
5498 }
5499 if (useFrameLatencyWaitableObject) {
5500 sc3->SetMaximumFrameLatency(rhiD->maxFrameLatency);
5501 frameLatencyWaitableObject = sc3->GetFrameLatencyWaitableObject();
5502 }
5503 sc3->Release();
5504 } else {
5505 if (m_format != SDR)
5506 qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
5507 if (useFrameLatencyWaitableObject) {
5508 IDXGISwapChain2 *sc2 = nullptr;
5509 if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain2), reinterpret_cast<void **>(&sc2)))) {
5510 sc2->SetMaximumFrameLatency(rhiD->maxFrameLatency);
5511 frameLatencyWaitableObject = sc2->GetFrameLatencyWaitableObject();
5512 sc2->Release();
5513 } else { // this cannot really happen since we require DXGIFactory2
5514 qWarning("IDXGISwapChain2 not available, FrameLatencyWaitableObject cannot be used");
5515 }
5516 }
5517 }
5518 if (dcompVisual) {
5519 hr = dcompVisual->SetContent(sc1);
5520 if (SUCCEEDED(hr)) {
5521 hr = dcompTarget->SetRoot(dcompVisual);
5522 if (FAILED(hr)) {
5523 qWarning("Failed to associate Direct Composition visual with the target: %s",
5524 qPrintable(QSystemError::windowsComString(hr)));
5525 }
5526 } else {
5527 qWarning("Failed to set content for Direct Composition visual: %s",
5528 qPrintable(QSystemError::windowsComString(hr)));
5529 }
5530 } else {
5531 // disable Alt+Enter; not relevant when using DirectComposition
5532 rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
5533 }
5534 }
5535 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
5536 qWarning("Device loss detected during swapchain creation");
5537 rhiD->deviceLost = true;
5538 return false;
5539 } else if (FAILED(hr)) {
5540 qWarning("Failed to create D3D11 swapchain: %s"
5541 " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
5542 qPrintable(QSystemError::windowsComString(hr)),
5543 desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
5544 desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
5545 return false;
5546 }
5547 } else {
5549 // flip model -> buffer count is the real buffer count, not 1 like with the legacy modes
5550 hr = swapChain->ResizeBuffers(UINT(BUFFER_COUNT), UINT(pixelSize.width()), UINT(pixelSize.height()),
5551 colorFormat, swapChainFlags);
5552 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
5553 qWarning("Device loss detected in ResizeBuffers()");
5554 rhiD->deviceLost = true;
5555 return false;
5556 } else if (FAILED(hr)) {
5557 qWarning("Failed to resize D3D11 swapchain: %s",
5558 qPrintable(QSystemError::windowsComString(hr)));
5559 return false;
5560 }
5561 }
5562
5563 // This looks odd (for FLIP_*, esp. compared with backends for Vulkan
5564 // & co.) but the backbuffer is always at index 0, with magic underneath.
5565 // Some explanation from
5566 // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
5567 //
5568 // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
5569 // Every call to Present implicitly changed the resource identity of the
5570 // returned interface. Direct3D 12 no longer supports that implicit
5571 // resource identity change, due to the CPU overhead required and the
5572 // flexible resource descriptor design. As a result, the application must
5573 // manually call GetBuffer for every each buffer created with the
5574 // swapchain."
5575
5576 // So just query index 0 once (per resize) and be done with it.
5577 hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&backBufferTex));
5578 if (FAILED(hr)) {
5579 qWarning("Failed to query swapchain backbuffer: %s",
5580 qPrintable(QSystemError::windowsComString(hr)));
5581 return false;
5582 }
5583 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5584 rtvDesc.Format = srgbAdjustedColorFormat;
5585 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
5586 hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
5587 if (FAILED(hr)) {
5588 qWarning("Failed to create rtv for swapchain backbuffer: %s",
5589 qPrintable(QSystemError::windowsComString(hr)));
5590 return false;
5591 }
5592
5593 if (stereo) {
5594 // Create a second render target view for the right eye
5595 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
5596 rtvDesc.Texture2DArray.FirstArraySlice = 1;
5597 rtvDesc.Texture2DArray.ArraySize = 1;
5598 hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtvRight);
5599 if (FAILED(hr)) {
5600 qWarning("Failed to create rtv for swapchain backbuffer (right eye): %s",
5601 qPrintable(QSystemError::windowsComString(hr)));
5602 return false;
5603 }
5604 }
5605
5606 // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
5607 for (int i = 0; i < BUFFER_COUNT; ++i) {
5608 if (sampleDesc.Count > 1) {
5609 if (!newColorBuffer(pixelSize, srgbAdjustedColorFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
5610 return false;
5611 }
5612 }
5613
5614 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
5615 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
5616 m_depthStencil->sampleCount(), m_sampleCount);
5617 }
5618 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
5619 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
5620 m_depthStencil->setPixelSize(pixelSize);
5621 if (!m_depthStencil->create())
5622 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
5623 pixelSize.width(), pixelSize.height());
5624 } else {
5625 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
5626 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
5627 pixelSize.width(), pixelSize.height());
5628 }
5629 }
5630
5631 currentFrameSlot = 0;
5632 lastFrameLatencyWaitSlot = -1; // wait already in the first frame, as instructed in the dxgi docs
5633 frameCount = 0;
5634 ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
5635
5636 rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
5637 QD3D11SwapChainRenderTarget *rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rt);
5638 rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
5639 rtD->d.pixelSize = pixelSize;
5640 rtD->d.dpr = float(window->devicePixelRatio());
5641 rtD->d.sampleCount = int(sampleDesc.Count);
5642 rtD->d.colorAttCount = 1;
5643 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
5644
5645 if (stereo) {
5646 rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rtRight);
5647 rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
5648 rtD->d.pixelSize = pixelSize;
5649 rtD->d.dpr = float(window->devicePixelRatio());
5650 rtD->d.sampleCount = int(sampleDesc.Count);
5651 rtD->d.colorAttCount = 1;
5652 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
5653 rtD->d.rtv[0] = backBufferRtvRight;
5654 rtD->d.dsv = ds ? ds->dsv : nullptr;
5655 }
5656
5657 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps)) {
5658 timestamps.prepare(rhiD);
5659 // timestamp queries are optional so we can go on even if they failed
5660 }
5661
5662 QDxgiVSyncService::instance()->registerWindow(window);
5663
5664 if (needsRegistration)
5665 rhiD->registerResource(this);
5666
5667 return true;
5668}
5669
5670bool RenderTargetUavUpdateState::update(QD3D11RenderTargetData *data, ID3D11UnorderedAccessView *const *uavs, int count)
5671{
5672 bool ret = false;
5673 if (dsv != data->dsv) {
5674 dsv = data->dsv;
5675 ret = true;
5676 }
5677 for (int i = 0; i < data->colorAttCount; i++) {
5678 ret |= rtv[i] != data->rtv[i];
5679 rtv[i] = data->rtv[i];
5680 }
5682 ret |= rtv[i] != nullptr;
5683 rtv[i] = nullptr;
5684 }
5685 for (int i = 0; i < count; i++) {
5686 ret |= uav[i] != uavs[i];
5687 uav[i] = uavs[i];
5688 }
5689 for (int i = count; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; i++) {
5690 ret |= uav[i] != nullptr;
5691 uav[i] = nullptr;
5692 }
5693 return ret;
5694}
5695
5696
5697QT_END_NAMESPACE
QRhiDriverInfo info() const override
const char * constData() const
Definition qrhi_p.h:372
int gsHighestActiveSrvBinding
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override
bool deviceLost
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
int dsHighestActiveSrvBinding
bool isYUpInNDC() const override
void drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
QRhiSwapChain * createSwapChain() override
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
bool isFeatureSupported(QRhi::Feature feature) const override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
bool isDeviceLost() const override
bool vsHasIndexBufferBound
void executeBufferHostWrites(QD3D11Buffer *bufD)
void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD, const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
QRhiStats statistics() override
QList< QSize > supportedShadingRates(int sampleCount) const override
QRhiComputePipeline * createComputePipeline() override
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
bool debugLayer
QRhi::FrameOpResult finish() override
void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) override
QRhiGraphicsPipeline * createGraphicsPipeline() override
QRhiShaderResourceBindings * createShaderResourceBindings() override
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
QList< int > supportedSampleCounts() const override
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
int csHighestActiveSrvBinding
bool isClipDepthZeroToOne() const override
bool ensureDirectCompositionDevice()
const QRhiNativeHandles * nativeHandles(QRhiCommandBuffer *cb) override
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
QD3D11SwapChain * currentSwapChain
void reportLiveObjects(ID3D11Device *device)
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
void destroy() override
QMatrix4x4 clipSpaceCorrMatrix() const override
bool isYUpInFramebuffer() const override
int resourceLimit(QRhi::ResourceLimit limit) const override
void beginExternal(QRhiCommandBuffer *cb) override
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
void setPipelineCacheData(const QByteArray &data) override
void executeCommandBuffer(QD3D11CommandBuffer *cbD)
void debugMarkEnd(QRhiCommandBuffer *cb) override
void releaseCachedResources() override
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
bool importedDeviceAndContext
void resetShaderResources(QD3D11RenderTargetData *rtD, RenderTargetUavUpdateState &rtUavState)
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
bool supportsAllowTearing
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
void endExternal(QRhiCommandBuffer *cb) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
QRhiShadingRateMap * createShadingRateMap() override
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
void clearShaderCache()
bool useLegacySwapchainModel
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
bool makeThreadLocalNativeContextCurrent() override
bool create(QRhi::Flags flags) override
int csHighestActiveUavBinding
void finishActiveReadbacks()
int fsHighestActiveSrvBinding
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override
QByteArray pipelineCacheData() override
const QRhiNativeHandles * nativeHandles() override
QRhiDriverInfo driverInfo() const override
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
int ubufAlignment() const override
void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
int vsHighestActiveSrvBinding
int hsHighestActiveSrvBinding
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice=nullptr)
DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount) const
int fsHighestActiveUavBinding
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
int vsHighestActiveVertexBufferBinding
void drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:597
void fillDriverInfo(QRhiDriverInfo *info, const DXGI_ADAPTER_DESC1 &desc)
@ UnBounded
Definition qrhi_p.h:285
@ Bounded
Definition qrhi_p.h:286
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:31
#define QRHI_RES(t, x)
Definition qrhi_p.h:30
static const DXGI_FORMAT DEFAULT_SRGB_FORMAT
static void applyDynamicOffsets(UINT *offsets, int batchIndex, const QRhiBatchedBindings< UINT > *originalBindings, const QRhiBatchedBindings< UINT > *staticOffsets, const uint *dynOfsPairs, int dynOfsPairCount)
static D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
#define SETUAVBATCH(stagePrefixL, stagePrefixU)
static QByteArray sourceHash(const QByteArray &source)
#define SETSAMPLERBATCH(stagePrefixL, stagePrefixU)
static const int RBM_HULL
static uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
static std::pair< int, int > mapBinding(int binding, int stageIndex, const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
static const int RBM_FRAGMENT
#define SETUBUFBATCH(stagePrefixL, stagePrefixU)
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
static DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
static D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
static const int RBM_VERTEX
static D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
#define D3D11_1_UAV_SLOT_COUNT
static const int RBM_DOMAIN
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)
void releasePipelineShader(T &s)
static D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
static DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
static const int RBM_GEOMETRY
static D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
static IDXGIFactory1 * createDXGIFactory2()
static D3D11_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
static bool isDepthTextureFormat(QRhiTexture::Format format)
static const int RBM_COMPUTE
static D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
#define SETSHADER(StageL, StageU)
static DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
static const DXGI_FORMAT DEFAULT_FORMAT
static uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType)
#define D3D11_VS_INPUT_REGISTER_COUNT
#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)
static const int RBM_SUPPORTED_STAGES
bool hasPendingDynamicUpdates
Definition qrhid3d11_p.h:45
void endFullDynamicBufferUpdateForCurrentFrame() override
To be called when the entire contents of the buffer data has been updated in the memory block returne...
char * dynBuf
Definition qrhid3d11_p.h:44
QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
char * beginFullDynamicBufferUpdateForCurrentFrame() override
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiBuffer::NativeBuffer nativeBuffer() override
ID3D11UnorderedAccessView * unorderedAccessView(quint32 offset)
QD3D11RenderTargetData * prevRtD
static const int MAX_DYNAMIC_OFFSET_COUNT
static const int MAX_VERTEX_BUFFER_BINDING_COUNT
int retainResourceBatches(const QD3D11ShaderResourceBindings::ResourceBatches &resourceBatches)
QD3D11CommandBuffer(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QD3D11ComputePipeline(QRhiImplementation *rhi)
bool create() override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QD3D11GraphicsPipeline(QRhiImplementation *rhi)
bool create() override
Creates the corresponding native graphics resources.
QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool create() override
Creates the corresponding native graphics resources.
QRhiTexture::Format backingFormat() const override
QD3D11RenderPassDescriptor(QRhiImplementation *rhi)
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
QVector< quint32 > serializedFormat() const override
static const int MAX_COLOR_ATTACHMENTS
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v, AddressMode w)
bool create() override
QD3D11GraphicsPipeline * lastUsedGraphicsPipeline
bool create() override
Creates the corresponding resource binding set.
void updateResources(UpdateFlags flags) override
QD3D11ComputePipeline * lastUsedComputePipeline
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QD3D11ShaderResourceBindings(QRhiImplementation *rhi)
int sampleCount() const override
QD3D11SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
float devicePixelRatio() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QSize pixelSize() const override
bool prepare(QRhiD3D11 *rhiD)
bool tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec)
bool active[TIMESTAMP_PAIRS]
static const int TIMESTAMP_PAIRS
QRhiSwapChainHdrInfo hdrInfo() override
\variable QRhiSwapChainHdrInfo::limitsType
int lastFrameLatencyWaitSlot
QWindow * window
QD3D11RenderBuffer * ds
QRhiRenderTarget * currentFrameRenderTarget() override
QD3D11SwapChain(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiRenderTarget * currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
QSize surfacePixelSize() override
bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
static const int BUFFER_COUNT
bool isFormatSupported(Format f) override
QRhiCommandBuffer * currentFrameCommandBuffer() override
int currentTimestampPairIndex
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
QSize pixelSize() const override
QD3D11TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
float devicePixelRatio() const override
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
int sampleCount() const override
bool ownsRtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS]
bool create() override
Creates the corresponding native graphics resources.
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
NativeTexture nativeTexture() override
bool prepareCreate(QSize *adjustedSize=nullptr)
QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
ID3D11UnorderedAccessView * unorderedAccessViewForLevel(int level)
bool finishCreate()
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1867
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1557
bool update(QD3D11RenderTargetData *data, ID3D11UnorderedAccessView *const *uavs=nullptr, int count=0)