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