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