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 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
1835 if (img.depth() == 32) {
1836 const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4;
1837 cmd.args.updateSubRes.src = cbD->retainImage(img) + offset;
1838 } else {
1839 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
1840 bpl = img.bytesPerLine();
1841 cmd.args.updateSubRes.src = cbD->retainImage(img);
1842 }
1843 } else {
1844 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
1845 cmd.args.updateSubRes.src = cbD->retainImage(img);
1846 }
1847 box.left = UINT(dp.x());
1848 box.top = UINT(dp.y());
1849 box.right = UINT(dp.x() + size.width());
1850 box.bottom = UINT(dp.y() + size.height());
1851 cmd.args.updateSubRes.hasDstBox = true;
1852 cmd.args.updateSubRes.dstBox = box;
1853 cmd.args.updateSubRes.srcRowPitch = UINT(bpl);
1854 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
1855 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1856 : subresDesc.sourceSize();
1857 quint32 bpl = 0;
1858 QSize blockDim;
1859 compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim);
1860 // Everything must be a multiple of the block width and
1861 // height, so e.g. a mip level of size 2x2 will be 4x4 when it
1862 // comes to the actual data.
1863 box.left = UINT(aligned(dp.x(), blockDim.width()));
1864 box.top = UINT(aligned(dp.y(), blockDim.height()));
1865 box.right = UINT(aligned(dp.x() + size.width(), blockDim.width()));
1866 box.bottom = UINT(aligned(dp.y() + size.height(), blockDim.height()));
1867 cmd.args.updateSubRes.hasDstBox = true;
1868 cmd.args.updateSubRes.dstBox = box;
1869 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1870 cmd.args.updateSubRes.srcRowPitch = bpl;
1871 } else if (!subresDesc.data().isEmpty()) {
1872 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1873 : subresDesc.sourceSize();
1874 quint32 bpl = 0;
1875 if (subresDesc.dataStride())
1876 bpl = subresDesc.dataStride();
1877 else
1878 textureFormatInfo(texD->m_format, size, &bpl, nullptr, nullptr);
1879 box.left = UINT(dp.x());
1880 box.top = UINT(dp.y());
1881 box.right = UINT(dp.x() + size.width());
1882 box.bottom = UINT(dp.y() + size.height());
1883 cmd.args.updateSubRes.hasDstBox = true;
1884 cmd.args.updateSubRes.dstBox = box;
1885 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1886 cmd.args.updateSubRes.srcRowPitch = bpl;
1887 } else {
1888 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1889 cbD->commands.unget();
1890 }
1891}
1892
1893void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1894{
1895 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1897
1898 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
1899 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
1901 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1902 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1903 memcpy(bufD->dynBuf + u.offset, u.data.constData(), size_t(u.data.size()));
1904 bufD->hasPendingDynamicUpdates = true;
1906 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1907 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1908 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
1909 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1911 cmd.args.updateSubRes.dst = bufD->buffer;
1912 cmd.args.updateSubRes.dstSubRes = 0;
1913 cmd.args.updateSubRes.src = cbD->retainBufferData(u.data);
1914 cmd.args.updateSubRes.srcRowPitch = 0;
1915 // Specify the region (even when offset is 0 and all data is provided)
1916 // since the ID3D11Buffer's size is rounded up to be a multiple of 256
1917 // while the data we have has the original size.
1918 D3D11_BOX box;
1919 box.left = u.offset;
1920 box.top = box.front = 0;
1921 box.back = box.bottom = 1;
1922 box.right = u.offset + u.data.size(); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
1923 cmd.args.updateSubRes.hasDstBox = true;
1924 cmd.args.updateSubRes.dstBox = box;
1926 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1927 if (bufD->m_type == QRhiBuffer::Dynamic) {
1928 u.result->data.resize(u.readSize);
1929 memcpy(u.result->data.data(), bufD->dynBuf + u.offset, size_t(u.readSize));
1930 if (u.result->completed)
1931 u.result->completed();
1932 } else {
1933 BufferReadback readback;
1934 readback.result = u.result;
1935 readback.byteSize = u.readSize;
1936
1937 D3D11_BUFFER_DESC desc = {};
1938 desc.ByteWidth = readback.byteSize;
1939 desc.Usage = D3D11_USAGE_STAGING;
1940 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1941 HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf);
1942 if (FAILED(hr)) {
1943 qWarning("Failed to create buffer: %s",
1944 qPrintable(QSystemError::windowsComString(hr)));
1945 continue;
1946 }
1947
1948 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
1950 cmd.args.copySubRes.dst = readback.stagingBuf;
1951 cmd.args.copySubRes.dstSubRes = 0;
1952 cmd.args.copySubRes.dstX = 0;
1953 cmd.args.copySubRes.dstY = 0;
1954 cmd.args.copySubRes.dstZ = 0;
1955 cmd.args.copySubRes.src = bufD->buffer;
1956 cmd.args.copySubRes.srcSubRes = 0;
1957 cmd.args.copySubRes.hasSrcBox = true;
1958 D3D11_BOX box;
1959 box.left = u.offset;
1960 box.top = box.front = 0;
1961 box.back = box.bottom = 1;
1962 box.right = u.offset + u.readSize;
1963 cmd.args.copySubRes.srcBox = box;
1964
1965 activeBufferReadbacks.append(readback);
1966 }
1967 }
1968 }
1969 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
1970 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
1972 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
1973 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
1974 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
1975 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
1976 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
1977 }
1978 }
1980 Q_ASSERT(u.src && u.dst);
1981 QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
1982 QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
1983 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
1984 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
1985 UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), srcIs3D ? 0u : UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
1986 UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), dstIs3D ? 0u : UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
1987 const QPoint dp = u.desc.destinationTopLeft();
1988 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
1989 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
1990 const QPoint sp = u.desc.sourceTopLeft();
1991 D3D11_BOX srcBox;
1992 srcBox.left = UINT(sp.x());
1993 srcBox.top = UINT(sp.y());
1994 srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
1995 // back, right, bottom are exclusive
1996 srcBox.right = srcBox.left + UINT(copySize.width());
1997 srcBox.bottom = srcBox.top + UINT(copySize.height());
1998 srcBox.back = srcBox.front + 1;
1999 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2001 cmd.args.copySubRes.dst = dstD->textureResource();
2002 cmd.args.copySubRes.dstSubRes = dstSubRes;
2003 cmd.args.copySubRes.dstX = UINT(dp.x());
2004 cmd.args.copySubRes.dstY = UINT(dp.y());
2005 cmd.args.copySubRes.dstZ = dstIs3D ? UINT(u.desc.destinationLayer()) : 0u;
2006 cmd.args.copySubRes.src = srcD->textureResource();
2007 cmd.args.copySubRes.srcSubRes = srcSubRes;
2008 cmd.args.copySubRes.hasSrcBox = true;
2009 cmd.args.copySubRes.srcBox = srcBox;
2011 TextureReadback readback;
2012 readback.desc = u.rb;
2013 readback.result = u.result;
2014
2015 ID3D11Resource *src;
2016 DXGI_FORMAT dxgiFormat;
2017 QRect rect;
2018 QRhiTexture::Format format;
2019 UINT subres = 0;
2020 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.rb.texture());
2021 QD3D11SwapChain *swapChainD = nullptr;
2022 bool is3D = false;
2023
2024 if (texD) {
2025 if (texD->sampleDesc.Count > 1) {
2026 qWarning("Multisample texture cannot be read back");
2027 continue;
2028 }
2029 src = texD->textureResource();
2030 dxgiFormat = texD->dxgiFormat;
2031 if (u.rb.rect().isValid())
2032 rect = u.rb.rect();
2033 else
2034 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2035 format = texD->m_format;
2036 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2037 subres = D3D11CalcSubresource(UINT(u.rb.level()), UINT(is3D ? 0 : u.rb.layer()), texD->mipLevelCount);
2038 } else {
2039 Q_ASSERT(contextState.currentSwapChain);
2040 swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
2041 if (swapChainD->sampleDesc.Count > 1) {
2042 // Unlike with textures, reading back a multisample swapchain image
2043 // has to be supported. Insert a resolve.
2044 QD3D11CommandBuffer::Command &rcmd(cbD->commands.get());
2046 rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
2047 rcmd.args.resolveSubRes.dstSubRes = 0;
2048 rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
2049 rcmd.args.resolveSubRes.srcSubRes = 0;
2050 rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
2051 }
2052 src = swapChainD->backBufferTex;
2053 dxgiFormat = swapChainD->colorFormat;
2054 if (u.rb.rect().isValid())
2055 rect = u.rb.rect();
2056 else
2057 rect = QRect({0, 0}, swapChainD->pixelSize);
2058 format = swapchainReadbackTextureFormat(dxgiFormat, nullptr);
2059 if (format == QRhiTexture::UnknownFormat)
2060 continue;
2061 }
2062 quint32 byteSize = 0;
2063 quint32 bpl = 0;
2064 textureFormatInfo(format, rect.size(), &bpl, &byteSize, nullptr);
2065
2066 D3D11_TEXTURE2D_DESC desc = {};
2067 desc.Width = UINT(rect.width());
2068 desc.Height = UINT(rect.height());
2069 desc.MipLevels = 1;
2070 desc.ArraySize = 1;
2071 desc.Format = dxgiFormat;
2072 desc.SampleDesc.Count = 1;
2073 desc.Usage = D3D11_USAGE_STAGING;
2074 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
2075 ID3D11Texture2D *stagingTex;
2076 HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
2077 if (FAILED(hr)) {
2078 qWarning("Failed to create readback staging texture: %s",
2079 qPrintable(QSystemError::windowsComString(hr)));
2080 return;
2081 }
2082
2083 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2085 cmd.args.copySubRes.dst = stagingTex;
2086 cmd.args.copySubRes.dstSubRes = 0;
2087 cmd.args.copySubRes.dstX = 0;
2088 cmd.args.copySubRes.dstY = 0;
2089 cmd.args.copySubRes.dstZ = 0;
2090 cmd.args.copySubRes.src = src;
2091 cmd.args.copySubRes.srcSubRes = subres;
2092
2093 D3D11_BOX srcBox = {};
2094 srcBox.left = UINT(rect.left());
2095 srcBox.top = UINT(rect.top());
2096 srcBox.front = is3D ? UINT(u.rb.layer()) : 0u;
2097 // back, right, bottom are exclusive
2098 srcBox.right = srcBox.left + desc.Width;
2099 srcBox.bottom = srcBox.top + desc.Height;
2100 srcBox.back = srcBox.front + 1;
2101 cmd.args.copySubRes.hasSrcBox = true;
2102 cmd.args.copySubRes.srcBox = srcBox;
2103
2104 readback.stagingTex = stagingTex;
2105 readback.byteSize = byteSize;
2106 readback.bpl = bpl;
2107 readback.pixelSize = rect.size();
2108 readback.format = format;
2109
2110 activeTextureReadbacks.append(readback);
2112 Q_ASSERT(u.dst->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
2113 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2115 cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.dst)->srv;
2116 }
2117 }
2118
2119 ud->free();
2120}
2121
2123{
2124 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
2125
2126 for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
2127 const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]);
2128 readback.result->format = readback.format;
2129 readback.result->pixelSize = readback.pixelSize;
2130
2131 D3D11_MAPPED_SUBRESOURCE mp;
2132 HRESULT hr = context->Map(readback.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
2133 if (SUCCEEDED(hr)) {
2134 readback.result->data.resize(int(readback.byteSize));
2135 // nothing says the rows are tightly packed in the texture, must take
2136 // the stride into account
2137 char *dst = readback.result->data.data();
2138 char *src = static_cast<char *>(mp.pData);
2139 for (int y = 0, h = readback.pixelSize.height(); y != h; ++y) {
2140 memcpy(dst, src, readback.bpl);
2141 dst += readback.bpl;
2142 src += mp.RowPitch;
2143 }
2144 context->Unmap(readback.stagingTex, 0);
2145 } else {
2146 qWarning("Failed to map readback staging texture: %s",
2147 qPrintable(QSystemError::windowsComString(hr)));
2148 }
2149
2150 readback.stagingTex->Release();
2151
2152 if (readback.result->completed)
2153 completedCallbacks.append(readback.result->completed);
2154
2155 activeTextureReadbacks.removeLast();
2156 }
2157
2158 for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) {
2159 const QRhiD3D11::BufferReadback &readback(activeBufferReadbacks[i]);
2160
2161 D3D11_MAPPED_SUBRESOURCE mp;
2162 HRESULT hr = context->Map(readback.stagingBuf, 0, D3D11_MAP_READ, 0, &mp);
2163 if (SUCCEEDED(hr)) {
2164 readback.result->data.resize(int(readback.byteSize));
2165 memcpy(readback.result->data.data(), mp.pData, readback.byteSize);
2166 context->Unmap(readback.stagingBuf, 0);
2167 } else {
2168 qWarning("Failed to map readback staging texture: %s",
2169 qPrintable(QSystemError::windowsComString(hr)));
2170 }
2171
2172 readback.stagingBuf->Release();
2173
2174 if (readback.result->completed)
2175 completedCallbacks.append(readback.result->completed);
2176
2177 activeBufferReadbacks.removeLast();
2178 }
2179
2180 for (auto f : completedCallbacks)
2181 f();
2182}
2183
2184void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2185{
2186 Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass);
2187
2188 enqueueResourceUpdates(cb, resourceUpdates);
2189}
2190
2191void QRhiD3D11::beginPass(QRhiCommandBuffer *cb,
2192 QRhiRenderTarget *rt,
2193 const QColor &colorClearValue,
2194 const QRhiDepthStencilClearValue &depthStencilClearValue,
2195 QRhiResourceUpdateBatch *resourceUpdates,
2197{
2198 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2200
2201 if (resourceUpdates)
2202 enqueueResourceUpdates(cb, resourceUpdates);
2203
2204 bool wantsColorClear = true;
2205 bool wantsDsClear = true;
2207 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
2209 wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
2210 wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
2211 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D11Texture, QD3D11RenderBuffer>(rtTex->description(), rtD->currentResIdList))
2212 rtTex->create();
2213 }
2214
2216
2217 QD3D11CommandBuffer::Command &fbCmd(cbD->commands.get());
2219 fbCmd.args.setRenderTarget.rt = rt;
2220
2221 QD3D11CommandBuffer::Command &clearCmd(cbD->commands.get());
2223 clearCmd.args.clear.rt = rt;
2224 clearCmd.args.clear.mask = 0;
2225 if (rtD->colorAttCount && wantsColorClear)
2226 clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color;
2227 if (rtD->dsAttCount && wantsDsClear)
2229
2230 clearCmd.args.clear.c[0] = colorClearValue.redF();
2231 clearCmd.args.clear.c[1] = colorClearValue.greenF();
2232 clearCmd.args.clear.c[2] = colorClearValue.blueF();
2233 clearCmd.args.clear.c[3] = colorClearValue.alphaF();
2234 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
2235 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
2236
2238 cbD->currentTarget = rt;
2239
2241}
2242
2243void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2244{
2245 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2247
2248 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
2249 QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
2250 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2251 it != itEnd; ++it)
2252 {
2253 const QRhiColorAttachment &colorAtt(*it);
2254 if (!colorAtt.resolveTexture())
2255 continue;
2256
2257 QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture());
2258 QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture());
2259 QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer());
2260 Q_ASSERT(srcTexD || srcRbD);
2261 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2263 cmd.args.resolveSubRes.dst = dstTexD->textureResource();
2264 cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()),
2265 UINT(colorAtt.resolveLayer()),
2266 dstTexD->mipLevelCount);
2267 if (srcTexD) {
2268 cmd.args.resolveSubRes.src = srcTexD->textureResource();
2269 if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
2270 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2271 int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
2272 cbD->commands.unget();
2273 continue;
2274 }
2275 if (srcTexD->sampleDesc.Count <= 1) {
2276 qWarning("Cannot resolve a non-multisample texture");
2277 cbD->commands.unget();
2278 continue;
2279 }
2280 if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
2281 qWarning("Resolve source and destination sizes do not match");
2282 cbD->commands.unget();
2283 continue;
2284 }
2285 } else {
2286 cmd.args.resolveSubRes.src = srcRbD->tex;
2287 if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
2288 qWarning("Resolve source (%d) and destination (%d) formats do not match",
2289 int(srcRbD->dxgiFormat), int(dstTexD->dxgiFormat));
2290 cbD->commands.unget();
2291 continue;
2292 }
2293 if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
2294 qWarning("Resolve source and destination sizes do not match");
2295 cbD->commands.unget();
2296 continue;
2297 }
2298 }
2299 cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1);
2300 cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
2301 }
2302 if (rtTex->m_desc.depthResolveTexture())
2303 qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
2304 }
2305
2307 cbD->currentTarget = nullptr;
2308
2309 if (resourceUpdates)
2310 enqueueResourceUpdates(cb, resourceUpdates);
2311}
2312
2313void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb,
2314 QRhiResourceUpdateBatch *resourceUpdates,
2316{
2317 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2319
2320 if (resourceUpdates)
2321 enqueueResourceUpdates(cb, resourceUpdates);
2322
2323 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2325
2327
2329}
2330
2331void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2332{
2333 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2335
2337
2338 if (resourceUpdates)
2339 enqueueResourceUpdates(cb, resourceUpdates);
2340}
2341
2342void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
2343{
2344 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2347 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
2348
2349 if (pipelineChanged) {
2350 cbD->currentGraphicsPipeline = nullptr;
2351 cbD->currentComputePipeline = psD;
2352 cbD->currentPipelineGeneration = psD->generation;
2353
2354 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2356 cmd.args.bindComputePipeline.ps = psD;
2357 }
2358}
2359
2360void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
2361{
2362 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
2364
2365 QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
2367 cmd.args.dispatch.x = UINT(x);
2368 cmd.args.dispatch.y = UINT(y);
2369 cmd.args.dispatch.z = UINT(z);
2370}
2371
2372static inline std::pair<int, int> mapBinding(int binding,
2373 int stageIndex,
2374 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
2375{
2376 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
2377 if (!map || map->isEmpty())
2378 return { binding, binding }; // assume 1:1 mapping
2379
2380 auto it = map->constFind(binding);
2381 if (it != map->cend())
2382 return *it;
2383
2384 // Hitting this path is normal too. It is not given that the resource is
2385 // present in the shaders for all the stages specified by the visibility
2386 // mask in the QRhiShaderResourceBinding.
2387 return { -1, -1 };
2388}
2389
2391 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
2392{
2393 srbD->resourceBatches.clear();
2394
2395 struct Stage {
2396 struct Buffer {
2397 int binding; // stored and sent along in XXorigbindings just for applyDynamicOffsets()
2398 int breg; // b0, b1, ...
2399 ID3D11Buffer *buffer;
2400 uint offsetInConstants;
2401 uint sizeInConstants;
2402 };
2403 struct Texture {
2404 int treg; // t0, t1, ...
2405 ID3D11ShaderResourceView *srv;
2406 };
2407 struct Sampler {
2408 int sreg; // s0, s1, ...
2409 ID3D11SamplerState *sampler;
2410 };
2411 struct Uav {
2412 int ureg;
2413 ID3D11UnorderedAccessView *uav;
2414 };
2415 QVarLengthArray<Buffer, 8> buffers;
2416 QVarLengthArray<Texture, 8> textures;
2417 QVarLengthArray<Sampler, 8> samplers;
2418 QVarLengthArray<Uav, 8> uavs;
2419 void buildBufferBatches(QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches) const
2420 {
2421 for (const Buffer &buf : buffers) {
2422 batches.ubufs.feed(buf.breg, buf.buffer);
2423 batches.ubuforigbindings.feed(buf.breg, UINT(buf.binding));
2424 batches.ubufoffsets.feed(buf.breg, buf.offsetInConstants);
2425 batches.ubufsizes.feed(buf.breg, buf.sizeInConstants);
2426 }
2427 batches.finish();
2428 }
2429 void buildSamplerBatches(QD3D11ShaderResourceBindings::StageSamplerBatches &batches) const
2430 {
2431 for (const Texture &t : textures)
2432 batches.shaderresources.feed(t.treg, t.srv);
2433 for (const Sampler &s : samplers)
2434 batches.samplers.feed(s.sreg, s.sampler);
2435 batches.finish();
2436 }
2437 void buildUavBatches(QD3D11ShaderResourceBindings::StageUavBatches &batches) const
2438 {
2439 for (const Stage::Uav &u : uavs)
2440 batches.uavs.feed(u.ureg, u.uav);
2441 batches.finish();
2442 }
2443 } res[RBM_SUPPORTED_STAGES];
2444
2445 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
2446 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
2447 QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
2448 switch (b->type) {
2449 case QRhiShaderResourceBinding::UniformBuffer:
2450 {
2451 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
2452 Q_ASSERT(aligned(b->u.ubuf.offset, 256u) == b->u.ubuf.offset);
2453 bd.ubuf.id = bufD->m_id;
2454 bd.ubuf.generation = bufD->generation;
2455 // Dynamic ubuf offsets are not considered here, those are baked in
2456 // at a later stage, which is good as vsubufoffsets and friends are
2457 // per-srb, not per-setShaderResources call. Other backends (GL,
2458 // Metal) are different in this respect since those do not store
2459 // per-srb vsubufoffsets etc. data so life's a bit easier for them.
2460 // But here we have to defer baking in the dynamic offset.
2461 const quint32 offsetInConstants = b->u.ubuf.offset / 16;
2462 // size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
2463 // We can round up if needed since the buffers's actual size
2464 // (ByteWidth) is always a multiple of 256.
2465 const quint32 sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256u) / 16;
2466 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
2467 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
2468 if (nativeBinding.first >= 0)
2469 res[RBM_VERTEX].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2470 }
2471 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
2472 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
2473 if (nativeBinding.first >= 0)
2474 res[RBM_HULL].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2475 }
2476 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
2477 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
2478 if (nativeBinding.first >= 0)
2479 res[RBM_DOMAIN].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2480 }
2481 if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
2482 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
2483 if (nativeBinding.first >= 0)
2484 res[RBM_GEOMETRY].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2485 }
2486 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2487 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2488 if (nativeBinding.first >= 0)
2489 res[RBM_FRAGMENT].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2490 }
2491 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2492 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2493 if (nativeBinding.first >= 0)
2494 res[RBM_COMPUTE].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
2495 }
2496 }
2497 break;
2498 case QRhiShaderResourceBinding::SampledTexture:
2499 case QRhiShaderResourceBinding::Texture:
2500 case QRhiShaderResourceBinding::Sampler:
2501 {
2502 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
2503 bd.stex.count = data->count;
2504 const std::pair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
2505 const std::pair<int, int> nativeBindingHull = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
2506 const std::pair<int, int> nativeBindingDomain = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
2507 const std::pair<int, int> nativeBindingGeom = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
2508 const std::pair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2509 const std::pair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2510 // if SPIR-V binding b is mapped to tN and sN in HLSL, and it
2511 // is an array, then it will use tN, tN+1, tN+2, ..., and sN,
2512 // sN+1, sN+2, ...
2513 for (int elem = 0; elem < data->count; ++elem) {
2514 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
2515 QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
2516 bd.stex.d[elem].texId = texD ? texD->m_id : 0;
2517 bd.stex.d[elem].texGeneration = texD ? texD->generation : 0;
2518 bd.stex.d[elem].samplerId = samplerD ? samplerD->m_id : 0;
2519 bd.stex.d[elem].samplerGeneration = samplerD ? samplerD->generation : 0;
2520 // Must handle all three cases (combined, separate, separate):
2521 // first = texture binding, second = sampler binding
2522 // first = texture binding
2523 // first = sampler binding
2524 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
2525 const int samplerBinding = texD && samplerD ? nativeBindingVert.second
2526 : (samplerD ? nativeBindingVert.first : -1);
2527 if (nativeBindingVert.first >= 0 && texD)
2528 res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv });
2529 if (samplerBinding >= 0)
2530 res[RBM_VERTEX].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2531 }
2532 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
2533 const int samplerBinding = texD && samplerD ? nativeBindingHull.second
2534 : (samplerD ? nativeBindingHull.first : -1);
2535 if (nativeBindingHull.first >= 0 && texD)
2536 res[RBM_HULL].textures.append({ nativeBindingHull.first + elem, texD->srv });
2537 if (samplerBinding >= 0)
2538 res[RBM_HULL].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2539 }
2540 if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
2541 const int samplerBinding = texD && samplerD ? nativeBindingDomain.second
2542 : (samplerD ? nativeBindingDomain.first : -1);
2543 if (nativeBindingDomain.first >= 0 && texD)
2544 res[RBM_DOMAIN].textures.append({ nativeBindingDomain.first + elem, texD->srv });
2545 if (samplerBinding >= 0)
2546 res[RBM_DOMAIN].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2547 }
2548 if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
2549 const int samplerBinding = texD && samplerD ? nativeBindingGeom.second
2550 : (samplerD ? nativeBindingGeom.first : -1);
2551 if (nativeBindingGeom.first >= 0 && texD)
2552 res[RBM_GEOMETRY].textures.append({ nativeBindingGeom.first + elem, texD->srv });
2553 if (samplerBinding >= 0)
2554 res[RBM_GEOMETRY].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2555 }
2556 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2557 const int samplerBinding = texD && samplerD ? nativeBindingFrag.second
2558 : (samplerD ? nativeBindingFrag.first : -1);
2559 if (nativeBindingFrag.first >= 0 && texD)
2560 res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
2561 if (samplerBinding >= 0)
2562 res[RBM_FRAGMENT].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2563 }
2564 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2565 const int samplerBinding = texD && samplerD ? nativeBindingComp.second
2566 : (samplerD ? nativeBindingComp.first : -1);
2567 if (nativeBindingComp.first >= 0 && texD)
2568 res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
2569 if (samplerBinding >= 0)
2570 res[RBM_COMPUTE].samplers.append({ samplerBinding + elem, samplerD->samplerState });
2571 }
2572 }
2573 }
2574 break;
2575 case QRhiShaderResourceBinding::ImageLoad:
2576 case QRhiShaderResourceBinding::ImageStore:
2577 case QRhiShaderResourceBinding::ImageLoadStore:
2578 {
2579 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
2580 bd.simage.id = texD->m_id;
2581 bd.simage.generation = texD->generation;
2582 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2583 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2584 if (nativeBinding.first >= 0) {
2585 ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
2586 if (uav)
2587 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
2588 }
2589 } else if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
2590 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
2591 if (nativeBinding.first >= 0) {
2592 ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
2593 if (uav)
2594 res[RBM_FRAGMENT].uavs.append({ nativeBinding.first, uav });
2595 }
2596 } else {
2597 qWarning("Unordered access only supported at fragment/compute stage");
2598 }
2599 }
2600 break;
2601 case QRhiShaderResourceBinding::BufferLoad:
2602 case QRhiShaderResourceBinding::BufferStore:
2603 case QRhiShaderResourceBinding::BufferLoadStore:
2604 {
2605 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
2606 bd.sbuf.id = bufD->m_id;
2607 bd.sbuf.generation = bufD->generation;
2608 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
2609 std::pair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
2610 if (nativeBinding.first >= 0) {
2611 ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView(b->u.sbuf.offset);
2612 if (uav)
2613 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
2614 }
2615 } else {
2616 qWarning("Unordered access only supported at compute stage");
2617 }
2618 }
2619 break;
2620 default:
2621 Q_UNREACHABLE();
2622 break;
2623 }
2624 }
2625
2626 // QRhiBatchedBindings works with the native bindings and expects
2627 // sorted input. The pre-sorted QRhiShaderResourceBinding list (based
2628 // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
2629 // have to sort here every time.
2630 for (int stage = 0; stage < RBM_SUPPORTED_STAGES; ++stage) {
2631 std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) {
2632 return a.breg < b.breg;
2633 });
2634 std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) {
2635 return a.treg < b.treg;
2636 });
2637 std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) {
2638 return a.sreg < b.sreg;
2639 });
2640 std::sort(res[stage].uavs.begin(), res[stage].uavs.end(), [](const Stage::Uav &a, const Stage::Uav &b) {
2641 return a.ureg < b.ureg;
2642 });
2643 }
2644
2645 res[RBM_VERTEX].buildBufferBatches(srbD->resourceBatches.vsUniformBufferBatches);
2646 res[RBM_HULL].buildBufferBatches(srbD->resourceBatches.hsUniformBufferBatches);
2647 res[RBM_DOMAIN].buildBufferBatches(srbD->resourceBatches.dsUniformBufferBatches);
2648 res[RBM_GEOMETRY].buildBufferBatches(srbD->resourceBatches.gsUniformBufferBatches);
2649 res[RBM_FRAGMENT].buildBufferBatches(srbD->resourceBatches.fsUniformBufferBatches);
2650 res[RBM_COMPUTE].buildBufferBatches(srbD->resourceBatches.csUniformBufferBatches);
2651
2652 res[RBM_VERTEX].buildSamplerBatches(srbD->resourceBatches.vsSamplerBatches);
2653 res[RBM_HULL].buildSamplerBatches(srbD->resourceBatches.hsSamplerBatches);
2654 res[RBM_DOMAIN].buildSamplerBatches(srbD->resourceBatches.dsSamplerBatches);
2655 res[RBM_GEOMETRY].buildSamplerBatches(srbD->resourceBatches.gsSamplerBatches);
2656 res[RBM_FRAGMENT].buildSamplerBatches(srbD->resourceBatches.fsSamplerBatches);
2657 res[RBM_COMPUTE].buildSamplerBatches(srbD->resourceBatches.csSamplerBatches);
2658
2659 res[RBM_FRAGMENT].buildUavBatches(srbD->resourceBatches.fsUavBatches);
2660 res[RBM_COMPUTE].buildUavBatches(srbD->resourceBatches.csUavBatches);
2661}
2662
2664{
2665 if (!bufD->hasPendingDynamicUpdates || bufD->m_size < 1)
2666 return;
2667
2668 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2669 bufD->hasPendingDynamicUpdates = false;
2670 D3D11_MAPPED_SUBRESOURCE mp;
2671 HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
2672 if (SUCCEEDED(hr)) {
2673 memcpy(mp.pData, bufD->dynBuf, bufD->m_size);
2674 context->Unmap(bufD->buffer, 0);
2675 } else {
2676 qWarning("Failed to map buffer: %s",
2677 qPrintable(QSystemError::windowsComString(hr)));
2678 }
2679}
2680
2681static void applyDynamicOffsets(UINT *offsets,
2682 int batchIndex,
2683 const QRhiBatchedBindings<UINT> *originalBindings,
2684 const QRhiBatchedBindings<UINT> *staticOffsets,
2685 const uint *dynOfsPairs, int dynOfsPairCount)
2686{
2687 const int count = staticOffsets->batches[batchIndex].resources.count();
2688 // Make a copy of the offset list, the entries that have no corresponding
2689 // dynamic offset will continue to use the existing offset value.
2690 for (int b = 0; b < count; ++b) {
2691 offsets[b] = staticOffsets->batches[batchIndex].resources[b];
2692 for (int di = 0; di < dynOfsPairCount; ++di) {
2693 const uint binding = dynOfsPairs[2 * di];
2694 // binding is the SPIR-V style binding point here, nothing to do
2695 // with the native one.
2696 if (binding == originalBindings->batches[batchIndex].resources[b]) {
2697 const uint offsetInConstants = dynOfsPairs[2 * di + 1];
2698 offsets[b] = offsetInConstants;
2699 break;
2700 }
2701 }
2702 }
2703}
2704
2705static inline uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType)
2706{
2707 if (startSlot + countSlots > maxSlots) {
2708 qWarning("Not enough D3D11 %s slots to bind %d resources starting at slot %d, max slots is %d",
2709 resType, countSlots, startSlot, maxSlots);
2710 countSlots = maxSlots > startSlot ? maxSlots - startSlot : 0;
2711 }
2712 return countSlots;
2713}
2714
2715#define SETUBUFBATCH(stagePrefixL, stagePrefixU)
2716 if (allResourceBatches.stagePrefixL##UniformBufferBatches.present) {
2717 const QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches(allResourceBatches.stagePrefixL##UniformBufferBatches);
2718 for (int i = 0, ie = batches.ubufs.batches.count(); i != ie; ++i) {
2719 const uint count = clampedResourceCount(batches.ubufs.batches[i].startBinding,
2720 batches.ubufs.batches[i].resources.count(),
2721 D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
2722 #stagePrefixU " cbuf");
2723 if (count) {
2724 if (!dynOfsPairCount) {
2725 context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding,
2726 count,
2727 batches.ubufs.batches[i].resources.constData(),
2728 batches.ubufoffsets.batches[i].resources.constData(),
2729 batches.ubufsizes.batches[i].resources.constData());
2730 } else {
2731 applyDynamicOffsets(offsets, i,
2732 &batches.ubuforigbindings, &batches.ubufoffsets,
2733 dynOfsPairs, dynOfsPairCount);
2734 context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding,
2735 count,
2736 batches.ubufs.batches[i].resources.constData(),
2737 offsets,
2738 batches.ubufsizes.batches[i].resources.constData());
2739 }
2740 }
2741 }
2742 }
2743
2744#define SETSAMPLERBATCH(stagePrefixL, stagePrefixU)
2745 if (allResourceBatches.stagePrefixL##SamplerBatches.present) {
2746 for (const auto &batch : allResourceBatches.stagePrefixL##SamplerBatches.samplers.batches) {
2747 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2748 D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, #stagePrefixU " sampler");
2749 if (count)
2750 context->stagePrefixU##SetSamplers(batch.startBinding, count, batch.resources.constData());
2751 }
2752 for (const auto &batch : allResourceBatches.stagePrefixL##SamplerBatches.shaderresources.batches) {
2753 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2754 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, #stagePrefixU " SRV");
2755 if (count) {
2756 context->stagePrefixU##SetShaderResources(batch.startBinding, count, batch.resources.constData());
2757 contextState.stagePrefixL##HighestActiveSrvBinding = qMax(contextState.stagePrefixL##HighestActiveSrvBinding,
2758 int(batch.startBinding + count) - 1);
2759 }
2760 }
2761 }
2762
2763#define SETUAVBATCH(stagePrefixL, stagePrefixU)
2764 if (allResourceBatches.stagePrefixL##UavBatches.present) {
2765 for (const auto &batch : allResourceBatches.stagePrefixL##UavBatches.uavs.batches) {
2766 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2767 D3D11_1_UAV_SLOT_COUNT, #stagePrefixU " UAV");
2768 if (count) {
2769 context->stagePrefixU##SetUnorderedAccessViews(batch.startBinding,
2770 count,
2771 batch.resources.constData(),
2772 nullptr);
2773 contextState.stagePrefixL##HighestActiveUavBinding = qMax(contextState.stagePrefixL##HighestActiveUavBinding,
2774 int(batch.startBinding + count) - 1);
2775 }
2776 }
2777 }
2778
2779void QRhiD3D11::bindShaderResources(const QD3D11ShaderResourceBindings::ResourceBatches &allResourceBatches,
2780 const uint *dynOfsPairs, int dynOfsPairCount,
2781 bool offsetOnlyChange,
2783 RenderTargetUavUpdateState &rtUavState)
2784{
2786
2787 SETUBUFBATCH(vs, VS)
2788 SETUBUFBATCH(hs, HS)
2789 SETUBUFBATCH(ds, DS)
2790 SETUBUFBATCH(gs, GS)
2791 SETUBUFBATCH(fs, PS)
2792 SETUBUFBATCH(cs, CS)
2793
2794 if (!offsetOnlyChange) {
2795 SETSAMPLERBATCH(vs, VS)
2796 SETSAMPLERBATCH(hs, HS)
2797 SETSAMPLERBATCH(ds, DS)
2798 SETSAMPLERBATCH(gs, GS)
2799 SETSAMPLERBATCH(fs, PS)
2800 SETSAMPLERBATCH(cs, CS)
2801
2802 SETUAVBATCH(cs, CS)
2803
2804 if (allResourceBatches.fsUavBatches.present) {
2805 for (const auto &batch : allResourceBatches.fsUavBatches.uavs.batches) {
2806 const uint count = qMin(clampedResourceCount(batch.startBinding, batch.resources.count(),
2807 D3D11_1_UAV_SLOT_COUNT, "fs UAV"),
2808 uint(QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS));
2809 if (count) {
2810 if (rtUavState.update(rtD, batch.resources.constData(), count)) {
2811 context->OMSetRenderTargetsAndUnorderedAccessViews(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv,
2812 UINT(rtD->colorAttCount), count, batch.resources.constData(), nullptr);
2813 }
2814 contextState.fsHighestActiveUavBinding = qMax(contextState.fsHighestActiveUavBinding,
2815 int(batch.startBinding + count) - 1);
2816 }
2817 }
2818 }
2819 }
2820}
2821
2823 RenderTargetUavUpdateState &rtUavState)
2824{
2825 // Output cannot be bound on input etc.
2826
2827 if (contextState.vsHasIndexBufferBound) {
2828 context->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
2829 contextState.vsHasIndexBufferBound = false;
2830 }
2831
2832 if (contextState.vsHighestActiveVertexBufferBinding >= 0) {
2833 const int count = contextState.vsHighestActiveVertexBufferBinding + 1;
2834 QVarLengthArray<ID3D11Buffer *, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullbufs(count);
2835 for (int i = 0; i < count; ++i)
2836 nullbufs[i] = nullptr;
2837 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullstrides(count);
2838 for (int i = 0; i < count; ++i)
2839 nullstrides[i] = 0;
2840 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nulloffsets(count);
2841 for (int i = 0; i < count; ++i)
2842 nulloffsets[i] = 0;
2843 context->IASetVertexBuffers(0, UINT(count), nullbufs.constData(), nullstrides.constData(), nulloffsets.constData());
2844 contextState.vsHighestActiveVertexBufferBinding = -1;
2845 }
2846
2847 int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
2848 nullsrvCount = qMax(nullsrvCount, contextState.hsHighestActiveSrvBinding);
2849 nullsrvCount = qMax(nullsrvCount, contextState.dsHighestActiveSrvBinding);
2850 nullsrvCount = qMax(nullsrvCount, contextState.gsHighestActiveSrvBinding);
2851 nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
2852 nullsrvCount += 1;
2853 if (nullsrvCount > 0) {
2854 QVarLengthArray<ID3D11ShaderResourceView *,
2855 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nullsrvs(nullsrvCount);
2856 for (int i = 0; i < nullsrvs.count(); ++i)
2857 nullsrvs[i] = nullptr;
2858 if (contextState.vsHighestActiveSrvBinding >= 0) {
2859 context->VSSetShaderResources(0, UINT(contextState.vsHighestActiveSrvBinding + 1), nullsrvs.constData());
2860 contextState.vsHighestActiveSrvBinding = -1;
2861 }
2862 if (contextState.hsHighestActiveSrvBinding >= 0) {
2863 context->HSSetShaderResources(0, UINT(contextState.hsHighestActiveSrvBinding + 1), nullsrvs.constData());
2864 contextState.hsHighestActiveSrvBinding = -1;
2865 }
2866 if (contextState.dsHighestActiveSrvBinding >= 0) {
2867 context->DSSetShaderResources(0, UINT(contextState.dsHighestActiveSrvBinding + 1), nullsrvs.constData());
2868 contextState.dsHighestActiveSrvBinding = -1;
2869 }
2870 if (contextState.gsHighestActiveSrvBinding >= 0) {
2871 context->GSSetShaderResources(0, UINT(contextState.gsHighestActiveSrvBinding + 1), nullsrvs.constData());
2872 contextState.gsHighestActiveSrvBinding = -1;
2873 }
2874 if (contextState.fsHighestActiveSrvBinding >= 0) {
2875 context->PSSetShaderResources(0, UINT(contextState.fsHighestActiveSrvBinding + 1), nullsrvs.constData());
2876 contextState.fsHighestActiveSrvBinding = -1;
2877 }
2878 if (contextState.csHighestActiveSrvBinding >= 0) {
2879 context->CSSetShaderResources(0, UINT(contextState.csHighestActiveSrvBinding + 1), nullsrvs.constData());
2880 contextState.csHighestActiveSrvBinding = -1;
2881 }
2882 }
2883
2884 if (contextState.fsHighestActiveUavBinding >= 0) {
2885 rtUavState.update(rtD);
2886 context->OMSetRenderTargetsAndUnorderedAccessViews(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv, 0, 0, nullptr, nullptr);
2887 contextState.fsHighestActiveUavBinding = -1;
2888 }
2889 if (contextState.csHighestActiveUavBinding >= 0) {
2890 const int nulluavCount = contextState.csHighestActiveUavBinding + 1;
2891 QVarLengthArray<ID3D11UnorderedAccessView *,
2892 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nulluavs(nulluavCount);
2893 for (int i = 0; i < nulluavCount; ++i)
2894 nulluavs[i] = nullptr;
2895 context->CSSetUnorderedAccessViews(0, UINT(nulluavCount), nulluavs.constData(), nullptr);
2896 contextState.csHighestActiveUavBinding = -1;
2897 }
2898}
2899
2900#define SETSHADER(StageL, StageU)
2901 if (psD->StageL.shader) {
2902 context->StageU##SetShader(psD->StageL.shader, nullptr, 0);
2903 currentShaderMask |= StageU##MaskBit;
2904 } else if (currentShaderMask & StageU##MaskBit) {
2905 context->StageU##SetShader(nullptr, nullptr, 0);
2906 currentShaderMask &= ~StageU##MaskBit;
2907 }
2908
2910{
2911 quint32 stencilRef = 0;
2912 float blendConstants[] = { 1, 1, 1, 1 };
2913 enum ActiveShaderMask {
2914 VSMaskBit = 0x01,
2915 HSMaskBit = 0x02,
2916 DSMaskBit = 0x04,
2917 GSMaskBit = 0x08,
2918 PSMaskBit = 0x10
2919 };
2920 int currentShaderMask = 0xFF;
2921
2922 // Track render target and uav updates during executeCommandBuffer.
2923 // Prevents multiple identical OMSetRenderTargetsAndUnorderedAccessViews calls.
2924 RenderTargetUavUpdateState rtUavState;
2925
2926 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
2927 const QD3D11CommandBuffer::Command &cmd(*it);
2928 switch (cmd.cmd) {
2929 case QD3D11CommandBuffer::Command::BeginFrame:
2930 if (cmd.args.beginFrame.tsDisjointQuery)
2931 context->Begin(cmd.args.beginFrame.tsDisjointQuery);
2932 if (cmd.args.beginFrame.tsQuery) {
2933 if (cmd.args.beginFrame.swapchainData) {
2934 // The timestamps seem to include vsync time with Present(1), except
2935 // when running on a non-primary gpu. This is not ideal. So try working
2936 // it around by issuing a semi-fake OMSetRenderTargets early and
2937 // writing the first timestamp only afterwards.
2938 QD3D11RenderTargetData *rtD = cmd.args.beginFrame.swapchainData;
2939 rtUavState.update(rtD);
2940 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
2941 cbD->prevRtD = rtD;
2942 }
2943 context->End(cmd.args.beginFrame.tsQuery); // no Begin() for D3D11_QUERY_TIMESTAMP
2944 }
2945 break;
2946 case QD3D11CommandBuffer::Command::EndFrame:
2947 if (cmd.args.endFrame.tsQuery)
2948 context->End(cmd.args.endFrame.tsQuery);
2949 if (cmd.args.endFrame.tsDisjointQuery)
2950 context->End(cmd.args.endFrame.tsDisjointQuery);
2951 break;
2954 break;
2956 {
2957 QD3D11RenderTargetData *rtD = rtData(cmd.args.setRenderTarget.rt);
2958 if (rtUavState.update(rtD))
2959 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
2960 cbD->prevRtD = rtD;
2961 }
2962 break;
2964 {
2965 QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt);
2966 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) {
2967 for (int i = 0; i < rtD->colorAttCount; ++i)
2968 context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c);
2969 }
2970 uint ds = 0;
2971 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth)
2972 ds |= D3D11_CLEAR_DEPTH;
2973 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil)
2974 ds |= D3D11_CLEAR_STENCIL;
2975 if (ds)
2976 context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, UINT8(cmd.args.clear.s));
2977 }
2978 break;
2980 {
2981 D3D11_VIEWPORT v;
2982 v.TopLeftX = cmd.args.viewport.x;
2983 v.TopLeftY = cmd.args.viewport.y;
2984 v.Width = cmd.args.viewport.w;
2985 v.Height = cmd.args.viewport.h;
2986 v.MinDepth = cmd.args.viewport.d0;
2987 v.MaxDepth = cmd.args.viewport.d1;
2988 context->RSSetViewports(1, &v);
2989 }
2990 break;
2992 {
2993 D3D11_RECT r;
2994 r.left = cmd.args.scissor.x;
2995 r.top = cmd.args.scissor.y;
2996 // right and bottom are exclusive
2997 r.right = cmd.args.scissor.x + cmd.args.scissor.w;
2998 r.bottom = cmd.args.scissor.y + cmd.args.scissor.h;
2999 context->RSSetScissorRects(1, &r);
3000 }
3001 break;
3003 contextState.vsHighestActiveVertexBufferBinding = qMax<int>(
3005 cmd.args.bindVertexBuffers.startSlot + cmd.args.bindVertexBuffers.slotCount - 1);
3006 context->IASetVertexBuffers(UINT(cmd.args.bindVertexBuffers.startSlot),
3007 UINT(cmd.args.bindVertexBuffers.slotCount),
3008 cmd.args.bindVertexBuffers.buffers,
3009 cmd.args.bindVertexBuffers.strides,
3010 cmd.args.bindVertexBuffers.offsets);
3011 break;
3013 contextState.vsHasIndexBufferBound = true;
3014 context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer,
3015 cmd.args.bindIndexBuffer.format,
3016 cmd.args.bindIndexBuffer.offset);
3017 break;
3019 {
3020 QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
3021 SETSHADER(vs, VS)
3022 SETSHADER(hs, HS)
3023 SETSHADER(ds, DS)
3024 SETSHADER(gs, GS)
3025 SETSHADER(fs, PS)
3026 context->IASetPrimitiveTopology(psD->d3dTopology);
3027 context->IASetInputLayout(psD->inputLayout); // may be null, that's ok
3028 context->OMSetDepthStencilState(psD->dsState, stencilRef);
3029 context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff);
3030 context->RSSetState(psD->rastState);
3031 }
3032 break;
3033 case QD3D11CommandBuffer::Command::BindShaderResources:
3034 bindShaderResources(cbD->resourceBatchRetainPool[cmd.args.bindShaderResources.resourceBatchesIndex],
3035 cmd.args.bindShaderResources.dynamicOffsetPairs,
3036 cmd.args.bindShaderResources.dynamicOffsetCount,
3037 cmd.args.bindShaderResources.offsetOnlyChange,
3038 cbD->prevRtD,
3039 rtUavState);
3040 break;
3042 stencilRef = cmd.args.stencilRef.ref;
3043 context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef);
3044 break;
3046 memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float));
3047 context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff);
3048 break;
3050 if (cmd.args.draw.ps) {
3051 if (cmd.args.draw.instanceCount == 1 && cmd.args.draw.firstInstance == 0)
3052 context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex);
3053 else
3054 context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
3055 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
3056 } else {
3057 qWarning("No graphics pipeline active for draw; ignored");
3058 }
3059 break;
3061 if (cmd.args.drawIndexed.ps) {
3062 if (cmd.args.drawIndexed.instanceCount == 1 && cmd.args.drawIndexed.firstInstance == 0)
3063 context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex,
3064 cmd.args.drawIndexed.vertexOffset);
3065 else
3066 context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
3067 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
3068 cmd.args.drawIndexed.firstInstance);
3069 } else {
3070 qWarning("No graphics pipeline active for drawIndexed; ignored");
3071 }
3072 break;
3073 case QD3D11CommandBuffer::Command::UpdateSubRes:
3074 context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes,
3075 cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr,
3076 cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0);
3077 break;
3078 case QD3D11CommandBuffer::Command::CopySubRes:
3079 context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
3080 cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, cmd.args.copySubRes.dstZ,
3081 cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
3082 cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
3083 break;
3084 case QD3D11CommandBuffer::Command::ResolveSubRes:
3085 context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes,
3086 cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
3087 cmd.args.resolveSubRes.format);
3088 break;
3089 case QD3D11CommandBuffer::Command::GenMip:
3090 context->GenerateMips(cmd.args.genMip.srv);
3091 break;
3092 case QD3D11CommandBuffer::Command::DebugMarkBegin:
3093 annotations->BeginEvent(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
3094 break;
3095 case QD3D11CommandBuffer::Command::DebugMarkEnd:
3096 annotations->EndEvent();
3097 break;
3098 case QD3D11CommandBuffer::Command::DebugMarkMsg:
3099 annotations->SetMarker(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
3100 break;
3101 case QD3D11CommandBuffer::Command::BindComputePipeline:
3102 context->CSSetShader(cmd.args.bindComputePipeline.ps->cs.shader, nullptr, 0);
3103 break;
3104 case QD3D11CommandBuffer::Command::Dispatch:
3105 context->Dispatch(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
3106 break;
3107 default:
3108 break;
3109 }
3110 }
3111}
3112
3113QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
3115{
3116}
3117
3122
3124{
3125 if (!buffer)
3126 return;
3127
3128 buffer->Release();
3129 buffer = nullptr;
3130
3131 delete[] dynBuf;
3132 dynBuf = nullptr;
3133
3134 for (auto it = uavs.begin(), end = uavs.end(); it != end; ++it)
3135 it.value()->Release();
3136 uavs.clear();
3137
3138 QRHI_RES_RHI(QRhiD3D11);
3139 if (rhiD)
3140 rhiD->unregisterResource(this);
3141}
3142
3143static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
3144{
3145 int u = 0;
3146 if (usage.testFlag(QRhiBuffer::VertexBuffer))
3147 u |= D3D11_BIND_VERTEX_BUFFER;
3148 if (usage.testFlag(QRhiBuffer::IndexBuffer))
3149 u |= D3D11_BIND_INDEX_BUFFER;
3150 if (usage.testFlag(QRhiBuffer::UniformBuffer))
3151 u |= D3D11_BIND_CONSTANT_BUFFER;
3152 if (usage.testFlag(QRhiBuffer::StorageBuffer))
3153 u |= D3D11_BIND_UNORDERED_ACCESS;
3154 return uint(u);
3155}
3156
3158{
3159 if (buffer)
3160 destroy();
3161
3162 if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
3163 qWarning("UniformBuffer must always be combined with Dynamic on D3D11");
3164 return false;
3165 }
3166
3167 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3168 qWarning("StorageBuffer cannot be combined with Dynamic");
3169 return false;
3170 }
3171
3172 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3173 const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
3174
3175 D3D11_BUFFER_DESC desc = {};
3176 desc.ByteWidth = roundedSize;
3177 desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
3178 desc.BindFlags = toD3DBufferUsage(m_usage);
3179 desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
3180 desc.MiscFlags = m_usage.testFlag(QRhiBuffer::StorageBuffer) ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0;
3181
3182 QRHI_RES_RHI(QRhiD3D11);
3183 HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
3184 if (FAILED(hr)) {
3185 qWarning("Failed to create buffer: %s",
3186 qPrintable(QSystemError::windowsComString(hr)));
3187 return false;
3188 }
3189
3190 if (m_type == Dynamic) {
3191 dynBuf = new char[nonZeroSize];
3193 }
3194
3195 if (!m_objectName.isEmpty())
3196 buffer->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3197
3198 generation += 1;
3199 rhiD->registerResource(this);
3200 return true;
3201}
3202
3204{
3205 if (m_type == Dynamic) {
3206 QRHI_RES_RHI(QRhiD3D11);
3208 }
3209 return { { &buffer }, 1 };
3210}
3211
3213{
3214 // Shortcut the entire buffer update mechanism and allow the client to do
3215 // the host writes directly to the buffer. This will lead to unexpected
3216 // results when combined with QRhiResourceUpdateBatch-based updates for the
3217 // buffer, since dynBuf is left untouched and out of sync, but provides a
3218 // fast path for dynamic buffers that have all their content changed in
3219 // every frame.
3220 Q_ASSERT(m_type == Dynamic);
3221 D3D11_MAPPED_SUBRESOURCE mp;
3222 QRHI_RES_RHI(QRhiD3D11);
3223 HRESULT hr = rhiD->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
3224 if (FAILED(hr)) {
3225 qWarning("Failed to map buffer: %s",
3226 qPrintable(QSystemError::windowsComString(hr)));
3227 return nullptr;
3228 }
3229 return static_cast<char *>(mp.pData);
3230}
3231
3233{
3234 QRHI_RES_RHI(QRhiD3D11);
3235 rhiD->context->Unmap(buffer, 0);
3236}
3237
3239{
3240 auto it = uavs.find(offset);
3241 if (it != uavs.end())
3242 return it.value();
3243
3244 // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
3245 D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
3246 desc.Format = DXGI_FORMAT_R32_TYPELESS;
3247 desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
3248 desc.Buffer.FirstElement = offset / 4u;
3249 desc.Buffer.NumElements = aligned(m_size - offset, 4u) / 4u;
3250 desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
3251
3252 QRHI_RES_RHI(QRhiD3D11);
3253 ID3D11UnorderedAccessView *uav = nullptr;
3254 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav);
3255 if (FAILED(hr)) {
3256 qWarning("Failed to create UAV: %s",
3257 qPrintable(QSystemError::windowsComString(hr)));
3258 return nullptr;
3259 }
3260
3261 uavs[offset] = uav;
3262 return uav;
3263}
3264
3265QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
3266 int sampleCount, QRhiRenderBuffer::Flags flags,
3267 QRhiTexture::Format backingFormatHint)
3269{
3270}
3271
3276
3278{
3279 if (!tex)
3280 return;
3281
3282 if (dsv) {
3283 dsv->Release();
3284 dsv = nullptr;
3285 }
3286
3287 if (rtv) {
3288 rtv->Release();
3289 rtv = nullptr;
3290 }
3291
3292 tex->Release();
3293 tex = nullptr;
3294
3295 QRHI_RES_RHI(QRhiD3D11);
3296 if (rhiD)
3297 rhiD->unregisterResource(this);
3298}
3299
3301{
3302 if (tex)
3303 destroy();
3304
3305 if (m_pixelSize.isEmpty())
3306 return false;
3307
3308 QRHI_RES_RHI(QRhiD3D11);
3309 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
3310
3311 D3D11_TEXTURE2D_DESC desc = {};
3312 desc.Width = UINT(m_pixelSize.width());
3313 desc.Height = UINT(m_pixelSize.height());
3314 desc.MipLevels = 1;
3315 desc.ArraySize = 1;
3316 desc.SampleDesc = sampleDesc;
3317 desc.Usage = D3D11_USAGE_DEFAULT;
3318
3319 if (m_type == Color) {
3320 dxgiFormat = m_backingFormatHint == QRhiTexture::UnknownFormat ? DXGI_FORMAT_R8G8B8A8_UNORM
3321 : toD3DTextureFormat(m_backingFormatHint, {});
3322 desc.Format = dxgiFormat;
3323 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
3324 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3325 if (FAILED(hr)) {
3326 qWarning("Failed to create color renderbuffer: %s",
3327 qPrintable(QSystemError::windowsComString(hr)));
3328 return false;
3329 }
3330 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
3331 rtvDesc.Format = dxgiFormat;
3332 rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS
3333 : D3D11_RTV_DIMENSION_TEXTURE2D;
3334 hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv);
3335 if (FAILED(hr)) {
3336 qWarning("Failed to create rtv: %s",
3337 qPrintable(QSystemError::windowsComString(hr)));
3338 return false;
3339 }
3340 } else if (m_type == DepthStencil) {
3341 dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
3342 desc.Format = dxgiFormat;
3343 desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
3344 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3345 if (FAILED(hr)) {
3346 qWarning("Failed to create depth-stencil buffer: %s",
3347 qPrintable(QSystemError::windowsComString(hr)));
3348 return false;
3349 }
3350 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
3351 dsvDesc.Format = dxgiFormat;
3352 dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
3353 : D3D11_DSV_DIMENSION_TEXTURE2D;
3354 hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv);
3355 if (FAILED(hr)) {
3356 qWarning("Failed to create dsv: %s",
3357 qPrintable(QSystemError::windowsComString(hr)));
3358 return false;
3359 }
3360 } else {
3361 return false;
3362 }
3363
3364 if (!m_objectName.isEmpty())
3365 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3366
3367 generation += 1;
3368 rhiD->registerResource(this);
3369 return true;
3370}
3371
3373{
3374 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3375 return m_backingFormatHint;
3376 else
3377 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3378}
3379
3380QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
3381 int arraySize, int sampleCount, Flags flags)
3383{
3384 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3385 perLevelViews[i] = nullptr;
3386}
3387
3392
3394{
3395 if (!tex && !tex3D && !tex1D)
3396 return;
3397
3398 if (srv) {
3399 srv->Release();
3400 srv = nullptr;
3401 }
3402
3403 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3404 if (perLevelViews[i]) {
3405 perLevelViews[i]->Release();
3406 perLevelViews[i] = nullptr;
3407 }
3408 }
3409
3410 if (owns) {
3411 if (tex)
3412 tex->Release();
3413 if (tex3D)
3414 tex3D->Release();
3415 if (tex1D)
3416 tex1D->Release();
3417 }
3418
3419 tex = nullptr;
3420 tex3D = nullptr;
3421 tex1D = nullptr;
3422
3423 QRHI_RES_RHI(QRhiD3D11);
3424 if (rhiD)
3425 rhiD->unregisterResource(this);
3426}
3427
3428static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
3429{
3430 switch (format) {
3431 case QRhiTexture::Format::D16:
3432 return DXGI_FORMAT_R16_FLOAT;
3433 case QRhiTexture::Format::D24:
3434 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
3435 case QRhiTexture::Format::D24S8:
3436 return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
3437 case QRhiTexture::Format::D32F:
3438 return DXGI_FORMAT_R32_FLOAT;
3439 case QRhiTexture::Format::D32FS8:
3440 return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
3441 default:
3442 Q_UNREACHABLE();
3443 return DXGI_FORMAT_R32_FLOAT;
3444 }
3445}
3446
3447static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
3448{
3449 switch (format) {
3450 case QRhiTexture::Format::D16:
3451 return DXGI_FORMAT_D16_UNORM;
3452 case QRhiTexture::Format::D24:
3453 return DXGI_FORMAT_D24_UNORM_S8_UINT;
3454 case QRhiTexture::Format::D24S8:
3455 return DXGI_FORMAT_D24_UNORM_S8_UINT;
3456 case QRhiTexture::Format::D32F:
3457 return DXGI_FORMAT_D32_FLOAT;
3458 case QRhiTexture::Format::D32FS8:
3459 return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
3460 default:
3461 Q_UNREACHABLE();
3462 return DXGI_FORMAT_D32_FLOAT;
3463 }
3464}
3465
3466bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
3467{
3468 if (tex || tex3D || tex1D)
3469 destroy();
3470
3471 QRHI_RES_RHI(QRhiD3D11);
3472 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
3473 return false;
3474
3475 const bool isDepth = isDepthTextureFormat(m_format);
3476 const bool isCube = m_flags.testFlag(CubeMap);
3477 const bool is3D = m_flags.testFlag(ThreeDimensional);
3478 const bool isArray = m_flags.testFlag(TextureArray);
3479 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3480 const bool is1D = m_flags.testFlag(OneDimensional);
3481
3482 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3483 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3484
3485 dxgiFormat = toD3DTextureFormat(m_format, m_flags);
3486 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
3487 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
3488 if (sampleDesc.Count > 1) {
3489 if (isCube) {
3490 qWarning("Cubemap texture cannot be multisample");
3491 return false;
3492 }
3493 if (is3D) {
3494 qWarning("3D texture cannot be multisample");
3495 return false;
3496 }
3497 if (hasMipMaps) {
3498 qWarning("Multisample texture cannot have mipmaps");
3499 return false;
3500 }
3501 }
3502 if (isDepth && hasMipMaps) {
3503 qWarning("Depth texture cannot have mipmaps");
3504 return false;
3505 }
3506 if (isCube && is3D) {
3507 qWarning("Texture cannot be both cube and 3D");
3508 return false;
3509 }
3510 if (isArray && is3D) {
3511 qWarning("Texture cannot be both array and 3D");
3512 return false;
3513 }
3514 if (isCube && is1D) {
3515 qWarning("Texture cannot be both cube and 1D");
3516 return false;
3517 }
3518 if (is1D && is3D) {
3519 qWarning("Texture cannot be both 1D and 3D");
3520 return false;
3521 }
3522 if (m_depth > 1 && !is3D) {
3523 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
3524 return false;
3525 }
3526 if (m_arraySize > 0 && !isArray) {
3527 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
3528 return false;
3529 }
3530 if (m_arraySize < 1 && isArray) {
3531 qWarning("Texture is an array but array size is %d", m_arraySize);
3532 return false;
3533 }
3534
3535 if (adjustedSize)
3536 *adjustedSize = size;
3537
3538 return true;
3539}
3540
3542{
3543 QRHI_RES_RHI(QRhiD3D11);
3544 const bool isDepth = isDepthTextureFormat(m_format);
3545 const bool isCube = m_flags.testFlag(CubeMap);
3546 const bool is3D = m_flags.testFlag(ThreeDimensional);
3547 const bool isArray = m_flags.testFlag(TextureArray);
3548 const bool is1D = m_flags.testFlag(OneDimensional);
3549
3550 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
3551 srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
3552 if (isCube) {
3553 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
3554 srvDesc.TextureCube.MipLevels = mipLevelCount;
3555 } else {
3556 if (is1D) {
3557 if (isArray) {
3558 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
3559 srvDesc.Texture1DArray.MipLevels = mipLevelCount;
3560 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3561 srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
3562 srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
3563 } else {
3564 srvDesc.Texture1DArray.FirstArraySlice = 0;
3565 srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
3566 }
3567 } else {
3568 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
3569 srvDesc.Texture1D.MipLevels = mipLevelCount;
3570 }
3571 } else if (isArray) {
3572 if (sampleDesc.Count > 1) {
3573 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
3574 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3575 srvDesc.Texture2DMSArray.FirstArraySlice = UINT(m_arrayRangeStart);
3576 srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
3577 } else {
3578 srvDesc.Texture2DMSArray.FirstArraySlice = 0;
3579 srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
3580 }
3581 } else {
3582 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
3583 srvDesc.Texture2DArray.MipLevels = mipLevelCount;
3584 if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
3585 srvDesc.Texture2DArray.FirstArraySlice = UINT(m_arrayRangeStart);
3586 srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
3587 } else {
3588 srvDesc.Texture2DArray.FirstArraySlice = 0;
3589 srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
3590 }
3591 }
3592 } else {
3593 if (sampleDesc.Count > 1) {
3594 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
3595 } else if (is3D) {
3596 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
3597 srvDesc.Texture3D.MipLevels = mipLevelCount;
3598 } else {
3599 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
3600 srvDesc.Texture2D.MipLevels = mipLevelCount;
3601 }
3602 }
3603 }
3604
3605 HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv);
3606 if (FAILED(hr)) {
3607 qWarning("Failed to create srv: %s",
3608 qPrintable(QSystemError::windowsComString(hr)));
3609 return false;
3610 }
3611
3612 generation += 1;
3613 return true;
3614}
3615
3617{
3618 QSize size;
3619 if (!prepareCreate(&size))
3620 return false;
3621
3622 const bool isDepth = isDepthTextureFormat(m_format);
3623 const bool isCube = m_flags.testFlag(CubeMap);
3624 const bool is3D = m_flags.testFlag(ThreeDimensional);
3625 const bool isArray = m_flags.testFlag(TextureArray);
3626 const bool is1D = m_flags.testFlag(OneDimensional);
3627
3628 uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
3629 uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
3630 if (m_flags.testFlag(RenderTarget)) {
3631 if (isDepth)
3632 bindFlags |= D3D11_BIND_DEPTH_STENCIL;
3633 else
3634 bindFlags |= D3D11_BIND_RENDER_TARGET;
3635 }
3636 if (m_flags.testFlag(UsedWithGenerateMips)) {
3637 if (isDepth) {
3638 qWarning("Depth texture cannot have mipmaps generated");
3639 return false;
3640 }
3641 bindFlags |= D3D11_BIND_RENDER_TARGET;
3642 miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
3643 }
3644 if (m_flags.testFlag(UsedWithLoadStore))
3645 bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
3646
3647 QRHI_RES_RHI(QRhiD3D11);
3648 if (is1D) {
3649 D3D11_TEXTURE1D_DESC desc = {};
3650 desc.Width = UINT(size.width());
3651 desc.MipLevels = mipLevelCount;
3652 desc.ArraySize = isArray ? UINT(qMax(0, m_arraySize)) : 1;
3653 desc.Format = dxgiFormat;
3654 desc.Usage = D3D11_USAGE_DEFAULT;
3655 desc.BindFlags = bindFlags;
3656 desc.MiscFlags = miscFlags;
3657
3658 HRESULT hr = rhiD->dev->CreateTexture1D(&desc, nullptr, &tex1D);
3659 if (FAILED(hr)) {
3660 qWarning("Failed to create 1D texture: %s",
3661 qPrintable(QSystemError::windowsComString(hr)));
3662 return false;
3663 }
3664 if (!m_objectName.isEmpty())
3665 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()),
3666 m_objectName.constData());
3667 } else if (!is3D) {
3668 D3D11_TEXTURE2D_DESC desc = {};
3669 desc.Width = UINT(size.width());
3670 desc.Height = UINT(size.height());
3671 desc.MipLevels = mipLevelCount;
3672 desc.ArraySize = isCube ? 6 : (isArray ? UINT(qMax(0, m_arraySize)) : 1);
3673 desc.Format = dxgiFormat;
3674 desc.SampleDesc = sampleDesc;
3675 desc.Usage = D3D11_USAGE_DEFAULT;
3676 desc.BindFlags = bindFlags;
3677 desc.MiscFlags = miscFlags;
3678
3679 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
3680 if (FAILED(hr)) {
3681 qWarning("Failed to create 2D texture: %s",
3682 qPrintable(QSystemError::windowsComString(hr)));
3683 return false;
3684 }
3685 if (!m_objectName.isEmpty())
3686 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3687 } else {
3688 D3D11_TEXTURE3D_DESC desc = {};
3689 desc.Width = UINT(size.width());
3690 desc.Height = UINT(size.height());
3691 desc.Depth = UINT(qMax(1, m_depth));
3692 desc.MipLevels = mipLevelCount;
3693 desc.Format = dxgiFormat;
3694 desc.Usage = D3D11_USAGE_DEFAULT;
3695 desc.BindFlags = bindFlags;
3696 desc.MiscFlags = miscFlags;
3697
3698 HRESULT hr = rhiD->dev->CreateTexture3D(&desc, nullptr, &tex3D);
3699 if (FAILED(hr)) {
3700 qWarning("Failed to create 3D texture: %s",
3701 qPrintable(QSystemError::windowsComString(hr)));
3702 return false;
3703 }
3704 if (!m_objectName.isEmpty())
3705 tex3D->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
3706 }
3707
3708 if (!finishCreate())
3709 return false;
3710
3711 owns = true;
3712 rhiD->registerResource(this);
3713 return true;
3714}
3715
3716bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
3717{
3718 if (!src.object)
3719 return false;
3720
3721 if (!prepareCreate())
3722 return false;
3723
3724 if (m_flags.testFlag(ThreeDimensional))
3725 tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
3726 else if (m_flags.testFlags(OneDimensional))
3727 tex1D = reinterpret_cast<ID3D11Texture1D *>(src.object);
3728 else
3729 tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
3730
3731 if (!finishCreate())
3732 return false;
3733
3734 owns = false;
3735 QRHI_RES_RHI(QRhiD3D11);
3736 rhiD->registerResource(this);
3737 return true;
3738}
3739
3741{
3742 return { quint64(textureResource()), 0 };
3743}
3744
3746{
3747 if (perLevelViews[level])
3748 return perLevelViews[level];
3749
3750 const bool isCube = m_flags.testFlag(CubeMap);
3751 const bool isArray = m_flags.testFlag(TextureArray);
3752 const bool is3D = m_flags.testFlag(ThreeDimensional);
3753 D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
3754 desc.Format = dxgiFormat;
3755 if (isCube) {
3756 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
3757 desc.Texture2DArray.MipSlice = UINT(level);
3758 desc.Texture2DArray.FirstArraySlice = 0;
3759 desc.Texture2DArray.ArraySize = 6;
3760 } else if (isArray) {
3761 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
3762 desc.Texture2DArray.MipSlice = UINT(level);
3763 desc.Texture2DArray.FirstArraySlice = 0;
3764 desc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
3765 } else if (is3D) {
3766 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
3767 desc.Texture3D.MipSlice = UINT(level);
3768 desc.Texture3D.WSize = UINT(m_depth);
3769 } else {
3770 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
3771 desc.Texture2D.MipSlice = UINT(level);
3772 }
3773
3774 QRHI_RES_RHI(QRhiD3D11);
3775 ID3D11UnorderedAccessView *uav = nullptr;
3776 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav);
3777 if (FAILED(hr)) {
3778 qWarning("Failed to create UAV: %s",
3779 qPrintable(QSystemError::windowsComString(hr)));
3780 return nullptr;
3781 }
3782
3783 perLevelViews[level] = uav;
3784 return uav;
3785}
3786
3787QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
3788 AddressMode u, AddressMode v, AddressMode w)
3790{
3791}
3792
3797
3799{
3800 if (!samplerState)
3801 return;
3802
3803 samplerState->Release();
3804 samplerState = nullptr;
3805
3806 QRHI_RES_RHI(QRhiD3D11);
3807 if (rhiD)
3808 rhiD->unregisterResource(this);
3809}
3810
3811static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
3812{
3813 if (minFilter == QRhiSampler::Nearest) {
3814 if (magFilter == QRhiSampler::Nearest) {
3815 if (mipFilter == QRhiSampler::Linear)
3816 return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
3817 else
3818 return D3D11_FILTER_MIN_MAG_MIP_POINT;
3819 } else {
3820 if (mipFilter == QRhiSampler::Linear)
3821 return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
3822 else
3823 return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
3824 }
3825 } else {
3826 if (magFilter == QRhiSampler::Nearest) {
3827 if (mipFilter == QRhiSampler::Linear)
3828 return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
3829 else
3830 return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
3831 } else {
3832 if (mipFilter == QRhiSampler::Linear)
3833 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3834 else
3835 return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
3836 }
3837 }
3838
3839 Q_UNREACHABLE();
3840 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3841}
3842
3843static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
3844{
3845 switch (m) {
3846 case QRhiSampler::Repeat:
3847 return D3D11_TEXTURE_ADDRESS_WRAP;
3848 case QRhiSampler::ClampToEdge:
3849 return D3D11_TEXTURE_ADDRESS_CLAMP;
3850 case QRhiSampler::Mirror:
3851 return D3D11_TEXTURE_ADDRESS_MIRROR;
3852 default:
3853 Q_UNREACHABLE();
3854 return D3D11_TEXTURE_ADDRESS_CLAMP;
3855 }
3856}
3857
3858static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
3859{
3860 switch (op) {
3861 case QRhiSampler::Never:
3862 return D3D11_COMPARISON_NEVER;
3863 case QRhiSampler::Less:
3864 return D3D11_COMPARISON_LESS;
3865 case QRhiSampler::Equal:
3866 return D3D11_COMPARISON_EQUAL;
3867 case QRhiSampler::LessOrEqual:
3868 return D3D11_COMPARISON_LESS_EQUAL;
3869 case QRhiSampler::Greater:
3870 return D3D11_COMPARISON_GREATER;
3871 case QRhiSampler::NotEqual:
3872 return D3D11_COMPARISON_NOT_EQUAL;
3873 case QRhiSampler::GreaterOrEqual:
3874 return D3D11_COMPARISON_GREATER_EQUAL;
3875 case QRhiSampler::Always:
3876 return D3D11_COMPARISON_ALWAYS;
3877 default:
3878 Q_UNREACHABLE();
3879 return D3D11_COMPARISON_NEVER;
3880 }
3881}
3882
3884{
3885 if (samplerState)
3886 destroy();
3887
3888 D3D11_SAMPLER_DESC desc = {};
3889 desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
3890 if (m_compareOp != Never)
3891 desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
3892 desc.AddressU = toD3DAddressMode(m_addressU);
3893 desc.AddressV = toD3DAddressMode(m_addressV);
3894 desc.AddressW = toD3DAddressMode(m_addressW);
3895 desc.MaxAnisotropy = 1.0f;
3896 desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
3897 desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f;
3898
3899 QRHI_RES_RHI(QRhiD3D11);
3900 HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
3901 if (FAILED(hr)) {
3902 qWarning("Failed to create sampler state: %s",
3903 qPrintable(QSystemError::windowsComString(hr)));
3904 return false;
3905 }
3906
3907 generation += 1;
3908 rhiD->registerResource(this);
3909 return true;
3910}
3911
3912// dummy, no Vulkan-style RenderPass+Framebuffer concept here
3917
3922
3924{
3925 QRHI_RES_RHI(QRhiD3D11);
3926 if (rhiD)
3927 rhiD->unregisterResource(this);
3928}
3929
3930bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
3931{
3932 Q_UNUSED(other);
3933 return true;
3934}
3935
3937{
3938 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
3939 QRHI_RES_RHI(QRhiD3D11);
3940 rhiD->registerResource(rpD, false);
3941 return rpD;
3942}
3943
3945{
3946 return {};
3947}
3948
3949QD3D11SwapChainRenderTarget::QD3D11SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
3951 d(rhi)
3952{
3953}
3954
3959
3961{
3962 // nothing to do here
3963}
3964
3966{
3967 return d.pixelSize;
3968}
3969
3971{
3972 return d.dpr;
3973}
3974
3976{
3977 return d.sampleCount;
3978}
3979
3981 const QRhiTextureRenderTargetDescription &desc,
3982 Flags flags)
3984 d(rhi)
3985{
3986 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
3987 ownsRtv[i] = false;
3988 rtv[i] = nullptr;
3989 }
3990}
3991
3996
3998{
3999 if (!rtv[0] && !dsv)
4000 return;
4001
4002 if (dsv) {
4003 if (ownsDsv)
4004 dsv->Release();
4005 dsv = nullptr;
4006 }
4007
4008 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
4009 if (rtv[i]) {
4010 if (ownsRtv[i])
4011 rtv[i]->Release();
4012 rtv[i] = nullptr;
4013 }
4014 }
4015
4016 QRHI_RES_RHI(QRhiD3D11);
4017 if (rhiD)
4018 rhiD->unregisterResource(this);
4019}
4020
4022{
4023 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
4024 QRHI_RES_RHI(QRhiD3D11);
4025 rhiD->registerResource(rpD, false);
4026 return rpD;
4027}
4028
4030{
4031 if (rtv[0] || dsv)
4032 destroy();
4033
4034 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4035 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4036 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4037
4038 QRHI_RES_RHI(QRhiD3D11);
4039
4040 d.colorAttCount = 0;
4041 int attIndex = 0;
4042 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4043 d.colorAttCount += 1;
4044 const QRhiColorAttachment &colorAtt(*it);
4045 QRhiTexture *texture = colorAtt.texture();
4046 QRhiRenderBuffer *rb = colorAtt.renderBuffer();
4047 Q_ASSERT(texture || rb);
4048 if (texture) {
4049 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
4050 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
4051 rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
4052 if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
4053 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
4054 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
4055 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
4056 rtvDesc.Texture2DArray.ArraySize = 1;
4057 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
4058 if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
4059 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
4060 rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
4061 rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
4062 rtvDesc.Texture1DArray.ArraySize = 1;
4063 } else {
4064 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
4065 rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
4066 }
4067 } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
4068 if (texD->sampleDesc.Count > 1) {
4069 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
4070 rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
4071 rtvDesc.Texture2DMSArray.ArraySize = 1;
4072 } else {
4073 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
4074 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
4075 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
4076 rtvDesc.Texture2DArray.ArraySize = 1;
4077 }
4078 } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
4079 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
4080 rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
4081 rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
4082 rtvDesc.Texture3D.WSize = 1;
4083 } else {
4084 if (texD->sampleDesc.Count > 1) {
4085 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
4086 } else {
4087 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
4088 rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
4089 }
4090 }
4091 HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->textureResource(), &rtvDesc, &rtv[attIndex]);
4092 if (FAILED(hr)) {
4093 qWarning("Failed to create rtv: %s",
4094 qPrintable(QSystemError::windowsComString(hr)));
4095 return false;
4096 }
4097 ownsRtv[attIndex] = true;
4098 if (attIndex == 0) {
4099 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
4100 d.sampleCount = int(texD->sampleDesc.Count);
4101 }
4102 } else if (rb) {
4103 QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
4104 ownsRtv[attIndex] = false;
4105 rtv[attIndex] = rbD->rtv;
4106 if (attIndex == 0) {
4107 d.pixelSize = rbD->pixelSize();
4108 d.sampleCount = int(rbD->sampleDesc.Count);
4109 }
4110 }
4111 }
4112 d.dpr = 1;
4113
4114 if (hasDepthStencil) {
4115 if (m_desc.depthTexture()) {
4116 ownsDsv = true;
4117 QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture());
4118 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
4119 dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
4120 dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
4121 : D3D11_DSV_DIMENSION_TEXTURE2D;
4122 if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
4123 if (depthTexD->sampleDesc.Count > 1) {
4124 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
4125 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
4126 dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
4127 dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
4128 } else {
4129 dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
4130 dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
4131 }
4132 } else {
4133 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
4134 if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
4135 dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
4136 dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
4137 } else {
4138 dsvDesc.Texture2DArray.FirstArraySlice = 0;
4139 dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
4140 }
4141 }
4142 }
4143 HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
4144 if (FAILED(hr)) {
4145 qWarning("Failed to create dsv: %s",
4146 qPrintable(QSystemError::windowsComString(hr)));
4147 return false;
4148 }
4149 if (d.colorAttCount == 0) {
4150 d.pixelSize = depthTexD->pixelSize();
4151 d.sampleCount = int(depthTexD->sampleDesc.Count);
4152 }
4153 } else {
4154 ownsDsv = false;
4155 QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer());
4156 dsv = depthRbD->dsv;
4157 if (d.colorAttCount == 0) {
4158 d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
4159 d.sampleCount = int(depthRbD->sampleDesc.Count);
4160 }
4161 }
4162 d.dsAttCount = 1;
4163 } else {
4164 d.dsAttCount = 0;
4165 }
4166
4167 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
4168 d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr;
4169
4170 d.dsv = dsv;
4171 d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
4172
4173 QRhiRenderTargetAttachmentTracker::updateResIdList<QD3D11Texture, QD3D11RenderBuffer>(m_desc, &d.currentResIdList);
4174
4175 rhiD->registerResource(this);
4176 return true;
4177}
4178
4180{
4181 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D11Texture, QD3D11RenderBuffer>(m_desc, d.currentResIdList))
4182 const_cast<QD3D11TextureRenderTarget *>(this)->create();
4183
4184 return d.pixelSize;
4185}
4186
4188{
4189 return d.dpr;
4190}
4191
4193{
4194 return d.sampleCount;
4195}
4196
4201
4206
4208{
4209 sortedBindings.clear();
4210 boundResourceData.clear();
4211
4212 QRHI_RES_RHI(QRhiD3D11);
4213 if (rhiD)
4214 rhiD->unregisterResource(this);
4215}
4216
4218{
4219 if (!sortedBindings.isEmpty())
4220 destroy();
4221
4222 QRHI_RES_RHI(QRhiD3D11);
4223 if (!rhiD->sanityCheckShaderResourceBindings(this))
4224 return false;
4225
4226 rhiD->updateLayoutDesc(this);
4227
4228 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4229 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4230
4231 boundResourceData.resize(sortedBindings.count());
4232
4233 for (BoundResourceData &bd : boundResourceData)
4234 memset(&bd, 0, sizeof(BoundResourceData));
4235
4236 hasDynamicOffset = false;
4237 for (const QRhiShaderResourceBinding &b : sortedBindings) {
4238 const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
4239 if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
4240 hasDynamicOffset = true;
4241 break;
4242 }
4243 }
4244
4245 generation += 1;
4246 rhiD->registerResource(this, false);
4247 return true;
4248}
4249
4251{
4252 sortedBindings.clear();
4253 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4254 if (!flags.testFlag(BindingsAreSorted))
4255 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4256
4257 Q_ASSERT(boundResourceData.count() == sortedBindings.count());
4258 for (BoundResourceData &bd : boundResourceData)
4259 memset(&bd, 0, sizeof(BoundResourceData));
4260
4261 generation += 1;
4262}
4263
4266{
4267}
4268
4273
4274template<typename T>
4275inline void releasePipelineShader(T &s)
4276{
4277 if (s.shader) {
4278 s.shader->Release();
4279 s.shader = nullptr;
4280 }
4281 s.nativeResourceBindingMap.clear();
4282}
4283
4285{
4286 if (!dsState)
4287 return;
4288
4289 dsState->Release();
4290 dsState = nullptr;
4291
4292 if (blendState) {
4293 blendState->Release();
4294 blendState = nullptr;
4295 }
4296
4297 if (inputLayout) {
4298 inputLayout->Release();
4299 inputLayout = nullptr;
4300 }
4301
4302 if (rastState) {
4303 rastState->Release();
4304 rastState = nullptr;
4305 }
4306
4307 releasePipelineShader(vs);
4308 releasePipelineShader(hs);
4309 releasePipelineShader(ds);
4310 releasePipelineShader(gs);
4311 releasePipelineShader(fs);
4312
4313 QRHI_RES_RHI(QRhiD3D11);
4314 if (rhiD)
4315 rhiD->unregisterResource(this);
4316}
4317
4318static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
4319{
4320 switch (c) {
4321 case QRhiGraphicsPipeline::None:
4322 return D3D11_CULL_NONE;
4323 case QRhiGraphicsPipeline::Front:
4324 return D3D11_CULL_FRONT;
4325 case QRhiGraphicsPipeline::Back:
4326 return D3D11_CULL_BACK;
4327 default:
4328 Q_UNREACHABLE();
4329 return D3D11_CULL_NONE;
4330 }
4331}
4332
4333static inline D3D11_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
4334{
4335 switch (mode) {
4336 case QRhiGraphicsPipeline::Fill:
4337 return D3D11_FILL_SOLID;
4338 case QRhiGraphicsPipeline::Line:
4339 return D3D11_FILL_WIREFRAME;
4340 default:
4341 Q_UNREACHABLE();
4342 return D3D11_FILL_SOLID;
4343 }
4344}
4345
4346static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
4347{
4348 switch (op) {
4349 case QRhiGraphicsPipeline::Never:
4350 return D3D11_COMPARISON_NEVER;
4351 case QRhiGraphicsPipeline::Less:
4352 return D3D11_COMPARISON_LESS;
4353 case QRhiGraphicsPipeline::Equal:
4354 return D3D11_COMPARISON_EQUAL;
4355 case QRhiGraphicsPipeline::LessOrEqual:
4356 return D3D11_COMPARISON_LESS_EQUAL;
4357 case QRhiGraphicsPipeline::Greater:
4358 return D3D11_COMPARISON_GREATER;
4359 case QRhiGraphicsPipeline::NotEqual:
4360 return D3D11_COMPARISON_NOT_EQUAL;
4361 case QRhiGraphicsPipeline::GreaterOrEqual:
4362 return D3D11_COMPARISON_GREATER_EQUAL;
4363 case QRhiGraphicsPipeline::Always:
4364 return D3D11_COMPARISON_ALWAYS;
4365 default:
4366 Q_UNREACHABLE();
4367 return D3D11_COMPARISON_ALWAYS;
4368 }
4369}
4370
4371static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
4372{
4373 switch (op) {
4374 case QRhiGraphicsPipeline::StencilZero:
4375 return D3D11_STENCIL_OP_ZERO;
4376 case QRhiGraphicsPipeline::Keep:
4377 return D3D11_STENCIL_OP_KEEP;
4378 case QRhiGraphicsPipeline::Replace:
4379 return D3D11_STENCIL_OP_REPLACE;
4380 case QRhiGraphicsPipeline::IncrementAndClamp:
4381 return D3D11_STENCIL_OP_INCR_SAT;
4382 case QRhiGraphicsPipeline::DecrementAndClamp:
4383 return D3D11_STENCIL_OP_DECR_SAT;
4384 case QRhiGraphicsPipeline::Invert:
4385 return D3D11_STENCIL_OP_INVERT;
4386 case QRhiGraphicsPipeline::IncrementAndWrap:
4387 return D3D11_STENCIL_OP_INCR;
4388 case QRhiGraphicsPipeline::DecrementAndWrap:
4389 return D3D11_STENCIL_OP_DECR;
4390 default:
4391 Q_UNREACHABLE();
4392 return D3D11_STENCIL_OP_KEEP;
4393 }
4394}
4395
4396static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
4397{
4398 switch (format) {
4399 case QRhiVertexInputAttribute::Float4:
4400 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4401 case QRhiVertexInputAttribute::Float3:
4402 return DXGI_FORMAT_R32G32B32_FLOAT;
4403 case QRhiVertexInputAttribute::Float2:
4404 return DXGI_FORMAT_R32G32_FLOAT;
4405 case QRhiVertexInputAttribute::Float:
4406 return DXGI_FORMAT_R32_FLOAT;
4407 case QRhiVertexInputAttribute::UNormByte4:
4408 return DXGI_FORMAT_R8G8B8A8_UNORM;
4409 case QRhiVertexInputAttribute::UNormByte2:
4410 return DXGI_FORMAT_R8G8_UNORM;
4411 case QRhiVertexInputAttribute::UNormByte:
4412 return DXGI_FORMAT_R8_UNORM;
4413 case QRhiVertexInputAttribute::UInt4:
4414 return DXGI_FORMAT_R32G32B32A32_UINT;
4415 case QRhiVertexInputAttribute::UInt3:
4416 return DXGI_FORMAT_R32G32B32_UINT;
4417 case QRhiVertexInputAttribute::UInt2:
4418 return DXGI_FORMAT_R32G32_UINT;
4419 case QRhiVertexInputAttribute::UInt:
4420 return DXGI_FORMAT_R32_UINT;
4421 case QRhiVertexInputAttribute::SInt4:
4422 return DXGI_FORMAT_R32G32B32A32_SINT;
4423 case QRhiVertexInputAttribute::SInt3:
4424 return DXGI_FORMAT_R32G32B32_SINT;
4425 case QRhiVertexInputAttribute::SInt2:
4426 return DXGI_FORMAT_R32G32_SINT;
4427 case QRhiVertexInputAttribute::SInt:
4428 return DXGI_FORMAT_R32_SINT;
4429 case QRhiVertexInputAttribute::Half4:
4430 // Note: D3D does not support half3. Pass through half3 as half4.
4431 case QRhiVertexInputAttribute::Half3:
4432 return DXGI_FORMAT_R16G16B16A16_FLOAT;
4433 case QRhiVertexInputAttribute::Half2:
4434 return DXGI_FORMAT_R16G16_FLOAT;
4435 case QRhiVertexInputAttribute::Half:
4436 return DXGI_FORMAT_R16_FLOAT;
4437 case QRhiVertexInputAttribute::UShort4:
4438 // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
4439 case QRhiVertexInputAttribute::UShort3:
4440 return DXGI_FORMAT_R16G16B16A16_UINT;
4441 case QRhiVertexInputAttribute::UShort2:
4442 return DXGI_FORMAT_R16G16_UINT;
4443 case QRhiVertexInputAttribute::UShort:
4444 return DXGI_FORMAT_R16_UINT;
4445 case QRhiVertexInputAttribute::SShort4:
4446 // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
4447 case QRhiVertexInputAttribute::SShort3:
4448 return DXGI_FORMAT_R16G16B16A16_SINT;
4449 case QRhiVertexInputAttribute::SShort2:
4450 return DXGI_FORMAT_R16G16_SINT;
4451 case QRhiVertexInputAttribute::SShort:
4452 return DXGI_FORMAT_R16_SINT;
4453 default:
4454 Q_UNREACHABLE();
4455 return DXGI_FORMAT_R32G32B32A32_FLOAT;
4456 }
4457}
4458
4459static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
4460{
4461 switch (t) {
4462 case QRhiGraphicsPipeline::Triangles:
4463 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
4464 case QRhiGraphicsPipeline::TriangleStrip:
4465 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
4466 case QRhiGraphicsPipeline::Lines:
4467 return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
4468 case QRhiGraphicsPipeline::LineStrip:
4469 return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
4470 case QRhiGraphicsPipeline::Points:
4471 return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
4472 case QRhiGraphicsPipeline::Patches:
4473 Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
4474 return D3D11_PRIMITIVE_TOPOLOGY(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
4475 default:
4476 Q_UNREACHABLE();
4477 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
4478 }
4479}
4480
4481static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
4482{
4483 UINT8 f = 0;
4484 if (c.testFlag(QRhiGraphicsPipeline::R))
4485 f |= D3D11_COLOR_WRITE_ENABLE_RED;
4486 if (c.testFlag(QRhiGraphicsPipeline::G))
4487 f |= D3D11_COLOR_WRITE_ENABLE_GREEN;
4488 if (c.testFlag(QRhiGraphicsPipeline::B))
4489 f |= D3D11_COLOR_WRITE_ENABLE_BLUE;
4490 if (c.testFlag(QRhiGraphicsPipeline::A))
4491 f |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
4492 return f;
4493}
4494
4495static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
4496{
4497 // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
4498 // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
4499 // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
4500 // set as an alpha src/dest factor), but for D3D we have to take care of it
4501 // ourselves. Hence the rgb argument.
4502
4503 switch (f) {
4504 case QRhiGraphicsPipeline::Zero:
4505 return D3D11_BLEND_ZERO;
4506 case QRhiGraphicsPipeline::One:
4507 return D3D11_BLEND_ONE;
4508 case QRhiGraphicsPipeline::SrcColor:
4509 return rgb ? D3D11_BLEND_SRC_COLOR : D3D11_BLEND_SRC_ALPHA;
4510 case QRhiGraphicsPipeline::OneMinusSrcColor:
4511 return rgb ? D3D11_BLEND_INV_SRC_COLOR : D3D11_BLEND_INV_SRC_ALPHA;
4512 case QRhiGraphicsPipeline::DstColor:
4513 return rgb ? D3D11_BLEND_DEST_COLOR : D3D11_BLEND_DEST_ALPHA;
4514 case QRhiGraphicsPipeline::OneMinusDstColor:
4515 return rgb ? D3D11_BLEND_INV_DEST_COLOR : D3D11_BLEND_INV_DEST_ALPHA;
4516 case QRhiGraphicsPipeline::SrcAlpha:
4517 return D3D11_BLEND_SRC_ALPHA;
4518 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4519 return D3D11_BLEND_INV_SRC_ALPHA;
4520 case QRhiGraphicsPipeline::DstAlpha:
4521 return D3D11_BLEND_DEST_ALPHA;
4522 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4523 return D3D11_BLEND_INV_DEST_ALPHA;
4524 case QRhiGraphicsPipeline::ConstantColor:
4525 case QRhiGraphicsPipeline::ConstantAlpha:
4526 return D3D11_BLEND_BLEND_FACTOR;
4527 case QRhiGraphicsPipeline::OneMinusConstantColor:
4528 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4529 return D3D11_BLEND_INV_BLEND_FACTOR;
4530 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4531 return D3D11_BLEND_SRC_ALPHA_SAT;
4532 case QRhiGraphicsPipeline::Src1Color:
4533 return rgb ? D3D11_BLEND_SRC1_COLOR : D3D11_BLEND_SRC1_ALPHA;
4534 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4535 return rgb ? D3D11_BLEND_INV_SRC1_COLOR : D3D11_BLEND_INV_SRC1_ALPHA;
4536 case QRhiGraphicsPipeline::Src1Alpha:
4537 return D3D11_BLEND_SRC1_ALPHA;
4538 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4539 return D3D11_BLEND_INV_SRC1_ALPHA;
4540 default:
4541 Q_UNREACHABLE();
4542 return D3D11_BLEND_ZERO;
4543 }
4544}
4545
4546static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
4547{
4548 switch (op) {
4549 case QRhiGraphicsPipeline::Add:
4550 return D3D11_BLEND_OP_ADD;
4551 case QRhiGraphicsPipeline::Subtract:
4552 return D3D11_BLEND_OP_SUBTRACT;
4553 case QRhiGraphicsPipeline::ReverseSubtract:
4554 return D3D11_BLEND_OP_REV_SUBTRACT;
4555 case QRhiGraphicsPipeline::Min:
4556 return D3D11_BLEND_OP_MIN;
4557 case QRhiGraphicsPipeline::Max:
4558 return D3D11_BLEND_OP_MAX;
4559 default:
4560 Q_UNREACHABLE();
4561 return D3D11_BLEND_OP_ADD;
4562 }
4563}
4564
4565static inline QByteArray sourceHash(const QByteArray &source)
4566{
4567 // taken from the GL backend, use the same mechanism to get a key
4568 QCryptographicHash keyBuilder(QCryptographicHash::Sha1);
4569 keyBuilder.addData(source);
4570 return keyBuilder.result().toHex();
4571}
4572
4573QByteArray QRhiD3D11::compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, uint flags,
4574 QString *error, QShaderKey *usedShaderKey)
4575{
4576 QShaderKey key = { QShader::DxbcShader, 50, shaderVariant };
4577 QShaderCode dxbc = shader.shader(key);
4578 if (!dxbc.shader().isEmpty()) {
4579 if (usedShaderKey)
4580 *usedShaderKey = key;
4581 return dxbc.shader();
4582 }
4583
4584 key = { QShader::HlslShader, 50, shaderVariant };
4585 QShaderCode hlslSource = shader.shader(key);
4586 if (hlslSource.shader().isEmpty()) {
4587 qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
4588 return QByteArray();
4589 }
4590
4591 if (usedShaderKey)
4592 *usedShaderKey = key;
4593
4594 const char *target;
4595 switch (shader.stage()) {
4596 case QShader::VertexStage:
4597 target = "vs_5_0";
4598 break;
4599 case QShader::TessellationControlStage:
4600 target = "hs_5_0";
4601 break;
4602 case QShader::TessellationEvaluationStage:
4603 target = "ds_5_0";
4604 break;
4605 case QShader::GeometryStage:
4606 target = "gs_5_0";
4607 break;
4608 case QShader::FragmentStage:
4609 target = "ps_5_0";
4610 break;
4611 case QShader::ComputeStage:
4612 target = "cs_5_0";
4613 break;
4614 default:
4615 Q_UNREACHABLE();
4616 return QByteArray();
4617 }
4618
4619 BytecodeCacheKey cacheKey;
4620 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
4621 cacheKey.sourceHash = sourceHash(hlslSource.shader());
4622 cacheKey.target = target;
4623 cacheKey.entryPoint = hlslSource.entryPoint();
4624 cacheKey.compileFlags = flags;
4625 auto cacheIt = m_bytecodeCache.constFind(cacheKey);
4626 if (cacheIt != m_bytecodeCache.constEnd())
4627 return cacheIt.value();
4628 }
4629
4630 static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
4631 if (d3dCompile == nullptr) {
4632 qWarning("Unable to resolve function D3DCompile()");
4633 return QByteArray();
4634 }
4635
4636 ID3DBlob *bytecode = nullptr;
4637 ID3DBlob *errors = nullptr;
4638 HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
4639 nullptr, nullptr, nullptr,
4640 hlslSource.entryPoint().constData(), target, flags, 0, &bytecode, &errors);
4641 if (FAILED(hr) || !bytecode) {
4642 qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
4643 if (errors) {
4644 *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
4645 int(errors->GetBufferSize()));
4646 errors->Release();
4647 }
4648 return QByteArray();
4649 }
4650
4651 QByteArray result;
4652 result.resize(int(bytecode->GetBufferSize()));
4653 memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
4654 bytecode->Release();
4655
4656 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
4657 m_bytecodeCache.insert(cacheKey, result);
4658
4659 return result;
4660}
4661
4663{
4664 if (dsState)
4665 destroy();
4666
4667 QRHI_RES_RHI(QRhiD3D11);
4668 rhiD->pipelineCreationStart();
4669 if (!rhiD->sanityCheckGraphicsPipeline(this))
4670 return false;
4671
4672 D3D11_RASTERIZER_DESC rastDesc = {};
4673 rastDesc.FillMode = toD3DFillMode(m_polygonMode);
4674 rastDesc.CullMode = toD3DCullMode(m_cullMode);
4675 rastDesc.FrontCounterClockwise = m_frontFace == CCW;
4676 rastDesc.DepthBias = m_depthBias;
4677 rastDesc.SlopeScaledDepthBias = m_slopeScaledDepthBias;
4678 rastDesc.DepthClipEnable = m_depthClamp ? FALSE : TRUE;
4679 rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
4680 rastDesc.MultisampleEnable = rhiD->effectiveSampleDesc(m_sampleCount).Count > 1;
4681 HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
4682 if (FAILED(hr)) {
4683 qWarning("Failed to create rasterizer state: %s",
4684 qPrintable(QSystemError::windowsComString(hr)));
4685 return false;
4686 }
4687
4688 D3D11_DEPTH_STENCIL_DESC dsDesc = {};
4689 dsDesc.DepthEnable = m_depthTest;
4690 dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
4691 dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
4692 dsDesc.StencilEnable = m_stencilTest;
4693 if (m_stencilTest) {
4694 dsDesc.StencilReadMask = UINT8(m_stencilReadMask);
4695 dsDesc.StencilWriteMask = UINT8(m_stencilWriteMask);
4696 dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
4697 dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
4698 dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
4699 dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
4700 dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
4701 dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
4702 dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
4703 dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
4704 }
4705 hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
4706 if (FAILED(hr)) {
4707 qWarning("Failed to create depth-stencil state: %s",
4708 qPrintable(QSystemError::windowsComString(hr)));
4709 return false;
4710 }
4711
4712 D3D11_BLEND_DESC blendDesc = {};
4713 blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1;
4714 for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
4715 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
4716 D3D11_RENDER_TARGET_BLEND_DESC blend = {};
4717 blend.BlendEnable = b.enable;
4718 blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
4719 blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
4720 blend.BlendOp = toD3DBlendOp(b.opColor);
4721 blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
4722 blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
4723 blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
4724 blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
4725 blendDesc.RenderTarget[i] = blend;
4726 }
4727 if (m_targetBlends.isEmpty()) {
4728 D3D11_RENDER_TARGET_BLEND_DESC blend = {};
4729 blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
4730 blendDesc.RenderTarget[0] = blend;
4731 }
4732 hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState);
4733 if (FAILED(hr)) {
4734 qWarning("Failed to create blend state: %s",
4735 qPrintable(QSystemError::windowsComString(hr)));
4736 return false;
4737 }
4738
4739 QByteArray vsByteCode;
4740 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
4741 auto cacheIt = rhiD->m_shaderCache.constFind(shaderStage);
4742 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
4743 switch (shaderStage.type()) {
4744 case QRhiShaderStage::Vertex:
4745 vs.shader = static_cast<ID3D11VertexShader *>(cacheIt->s);
4746 vs.shader->AddRef();
4747 vsByteCode = cacheIt->bytecode;
4748 vs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4749 break;
4750 case QRhiShaderStage::TessellationControl:
4751 hs.shader = static_cast<ID3D11HullShader *>(cacheIt->s);
4752 hs.shader->AddRef();
4753 hs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4754 break;
4755 case QRhiShaderStage::TessellationEvaluation:
4756 ds.shader = static_cast<ID3D11DomainShader *>(cacheIt->s);
4757 ds.shader->AddRef();
4758 ds.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4759 break;
4760 case QRhiShaderStage::Geometry:
4761 gs.shader = static_cast<ID3D11GeometryShader *>(cacheIt->s);
4762 gs.shader->AddRef();
4763 gs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4764 break;
4765 case QRhiShaderStage::Fragment:
4766 fs.shader = static_cast<ID3D11PixelShader *>(cacheIt->s);
4767 fs.shader->AddRef();
4768 fs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4769 break;
4770 default:
4771 break;
4772 }
4773 } else {
4774 QString error;
4775 QShaderKey shaderKey;
4776 UINT compileFlags = 0;
4777 if (m_flags.testFlag(CompileShadersWithDebugInfo))
4778 compileFlags |= D3DCOMPILE_DEBUG;
4779
4780 const QByteArray bytecode = rhiD->compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), compileFlags,
4781 &error, &shaderKey);
4782 if (bytecode.isEmpty()) {
4783 qWarning("HLSL shader compilation failed: %s", qPrintable(error));
4784 return false;
4785 }
4786
4787 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES) {
4788 // Use the simplest strategy: too many cached shaders -> drop them all.
4789 rhiD->clearShaderCache();
4790 }
4791
4792 switch (shaderStage.type()) {
4793 case QRhiShaderStage::Vertex:
4794 hr = rhiD->dev->CreateVertexShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &vs.shader);
4795 if (FAILED(hr)) {
4796 qWarning("Failed to create vertex shader: %s",
4797 qPrintable(QSystemError::windowsComString(hr)));
4798 return false;
4799 }
4800 vsByteCode = bytecode;
4801 vs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4802 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs.shader, bytecode, vs.nativeResourceBindingMap));
4803 vs.shader->AddRef();
4804 break;
4805 case QRhiShaderStage::TessellationControl:
4806 hr = rhiD->dev->CreateHullShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &hs.shader);
4807 if (FAILED(hr)) {
4808 qWarning("Failed to create hull shader: %s",
4809 qPrintable(QSystemError::windowsComString(hr)));
4810 return false;
4811 }
4812 hs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4813 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(hs.shader, bytecode, hs.nativeResourceBindingMap));
4814 hs.shader->AddRef();
4815 break;
4816 case QRhiShaderStage::TessellationEvaluation:
4817 hr = rhiD->dev->CreateDomainShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &ds.shader);
4818 if (FAILED(hr)) {
4819 qWarning("Failed to create domain shader: %s",
4820 qPrintable(QSystemError::windowsComString(hr)));
4821 return false;
4822 }
4823 ds.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4824 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(ds.shader, bytecode, ds.nativeResourceBindingMap));
4825 ds.shader->AddRef();
4826 break;
4827 case QRhiShaderStage::Geometry:
4828 hr = rhiD->dev->CreateGeometryShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &gs.shader);
4829 if (FAILED(hr)) {
4830 qWarning("Failed to create geometry shader: %s",
4831 qPrintable(QSystemError::windowsComString(hr)));
4832 return false;
4833 }
4834 gs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4835 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(gs.shader, bytecode, gs.nativeResourceBindingMap));
4836 gs.shader->AddRef();
4837 break;
4838 case QRhiShaderStage::Fragment:
4839 hr = rhiD->dev->CreatePixelShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &fs.shader);
4840 if (FAILED(hr)) {
4841 qWarning("Failed to create pixel shader: %s",
4842 qPrintable(QSystemError::windowsComString(hr)));
4843 return false;
4844 }
4845 fs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
4846 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(fs.shader, bytecode, fs.nativeResourceBindingMap));
4847 fs.shader->AddRef();
4848 break;
4849 default:
4850 break;
4851 }
4852 }
4853 }
4854
4855 d3dTopology = toD3DTopology(m_topology, m_patchControlPointCount);
4856
4857 if (!vsByteCode.isEmpty()) {
4858 QByteArrayList matrixSliceSemantics;
4859 QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
4860 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
4861 it != itEnd; ++it)
4862 {
4863 D3D11_INPUT_ELEMENT_DESC desc = {};
4864 // The output from SPIRV-Cross uses TEXCOORD<location> as the
4865 // semantic, except for matrices that are unrolled into consecutive
4866 // vec2/3/4s attributes and need TEXCOORD<location>_ as
4867 // SemanticName and row/column index as SemanticIndex.
4868 const int matrixSlice = it->matrixSlice();
4869 if (matrixSlice < 0) {
4870 desc.SemanticName = "TEXCOORD";
4871 desc.SemanticIndex = UINT(it->location());
4872 } else {
4873 QByteArray sem;
4874 sem.resize(16);
4875 std::snprintf(sem.data(), sem.size(), "TEXCOORD%d_", it->location() - matrixSlice);
4876 matrixSliceSemantics.append(sem);
4877 desc.SemanticName = matrixSliceSemantics.last().constData();
4878 desc.SemanticIndex = UINT(matrixSlice);
4879 }
4880 desc.Format = toD3DAttributeFormat(it->format());
4881 desc.InputSlot = UINT(it->binding());
4882 desc.AlignedByteOffset = it->offset();
4883 const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
4884 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
4885 desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
4886 desc.InstanceDataStepRate = inputBinding->instanceStepRate();
4887 } else {
4888 desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
4889 }
4890 inputDescs.append(desc);
4891 }
4892 if (!inputDescs.isEmpty()) {
4893 hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), UINT(inputDescs.count()),
4894 vsByteCode, SIZE_T(vsByteCode.size()), &inputLayout);
4895 if (FAILED(hr)) {
4896 qWarning("Failed to create input layout: %s",
4897 qPrintable(QSystemError::windowsComString(hr)));
4898 return false;
4899 }
4900 } // else leave inputLayout set to nullptr; that's valid and it avoids a debug layer warning about an input layout with 0 elements
4901 }
4902
4903 rhiD->pipelineCreationEnd();
4904 generation += 1;
4905 rhiD->registerResource(this);
4906 return true;
4907}
4908
4911{
4912}
4913
4918
4920{
4921 if (!cs.shader)
4922 return;
4923
4924 cs.shader->Release();
4925 cs.shader = nullptr;
4926 cs.nativeResourceBindingMap.clear();
4927
4928 QRHI_RES_RHI(QRhiD3D11);
4929 if (rhiD)
4930 rhiD->unregisterResource(this);
4931}
4932
4934{
4935 if (cs.shader)
4936 destroy();
4937
4938 QRHI_RES_RHI(QRhiD3D11);
4939 rhiD->pipelineCreationStart();
4940
4941 auto cacheIt = rhiD->m_shaderCache.constFind(m_shaderStage);
4942 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
4943 cs.shader = static_cast<ID3D11ComputeShader *>(cacheIt->s);
4944 cs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
4945 } else {
4946 QString error;
4947 QShaderKey shaderKey;
4948 UINT compileFlags = 0;
4949 if (m_flags.testFlag(CompileShadersWithDebugInfo))
4950 compileFlags |= D3DCOMPILE_DEBUG;
4951
4952 const QByteArray bytecode = rhiD->compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), compileFlags,
4953 &error, &shaderKey);
4954 if (bytecode.isEmpty()) {
4955 qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
4956 return false;
4957 }
4958
4959 HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &cs.shader);
4960 if (FAILED(hr)) {
4961 qWarning("Failed to create compute shader: %s",
4962 qPrintable(QSystemError::windowsComString(hr)));
4963 return false;
4964 }
4965
4966 cs.nativeResourceBindingMap = m_shaderStage.shader().nativeResourceBindingMap(shaderKey);
4967
4968 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES)
4970
4971 rhiD->m_shaderCache.insert(m_shaderStage, QRhiD3D11::Shader(cs.shader, bytecode, cs.nativeResourceBindingMap));
4972 }
4973
4974 cs.shader->AddRef();
4975
4976 rhiD->pipelineCreationEnd();
4977 generation += 1;
4978 rhiD->registerResource(this);
4979 return true;
4980}
4981
4984{
4986}
4987
4992
4994{
4995 // nothing to do here
4996}
4997
4999{
5000 // Creates the query objects if not yet done, but otherwise calling this
5001 // function is expected to be a no-op.
5002
5003 D3D11_QUERY_DESC queryDesc = {};
5004 for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
5005 if (!disjointQuery[i]) {
5006 queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
5007 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &disjointQuery[i]);
5008 if (FAILED(hr)) {
5009 qWarning("Failed to create timestamp disjoint query: %s",
5010 qPrintable(QSystemError::windowsComString(hr)));
5011 return false;
5012 }
5013 }
5014 queryDesc.Query = D3D11_QUERY_TIMESTAMP;
5015 for (int j = 0; j < 2; ++j) {
5016 const int idx = 2 * i + j;
5017 if (!query[idx]) {
5018 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &query[idx]);
5019 if (FAILED(hr)) {
5020 qWarning("Failed to create timestamp query: %s",
5021 qPrintable(QSystemError::windowsComString(hr)));
5022 return false;
5023 }
5024 }
5025 }
5026 }
5027 return true;
5028}
5029
5031{
5032 for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
5033 active[i] = false;
5034 if (disjointQuery[i]) {
5035 disjointQuery[i]->Release();
5036 disjointQuery[i] = nullptr;
5037 }
5038 for (int j = 0; j < 2; ++j) {
5039 const int idx = TIMESTAMP_PAIRS * i + j;
5040 if (query[idx]) {
5041 query[idx]->Release();
5042 query[idx] = nullptr;
5043 }
5044 }
5045 }
5046}
5047
5048bool QD3D11SwapChainTimestamps::tryQueryTimestamps(int pairIndex, ID3D11DeviceContext *context, double *elapsedSec)
5049{
5050 bool result = false;
5051 if (!active[pairIndex])
5052 return result;
5053
5054 ID3D11Query *tsDisjoint = disjointQuery[pairIndex];
5055 ID3D11Query *tsStart = query[pairIndex * 2];
5056 ID3D11Query *tsEnd = query[pairIndex * 2 + 1];
5057 quint64 timestamps[2];
5058 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
5059
5060 bool ok = true;
5061 ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5062 ok &= context->GetData(tsEnd, &timestamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5063 ok &= context->GetData(tsStart, &timestamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
5064
5065 if (ok) {
5066 if (!dj.Disjoint && dj.Frequency) {
5067 const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
5068 *elapsedSec = elapsedMs / 1000.0;
5069 result = true;
5070 }
5071 active[pairIndex] = false;
5072 } // else leave active set, will retry in a subsequent beginFrame
5073
5074 return result;
5075}
5076
5077QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
5078 : QRhiSwapChain(rhi), rt(rhi, this), rtRight(rhi, this), cb(rhi)
5079{
5080 backBufferTex = nullptr;
5081 backBufferRtv = nullptr;
5082 for (int i = 0; i < BUFFER_COUNT; ++i) {
5083 msaaTex[i] = nullptr;
5084 msaaRtv[i] = nullptr;
5085 }
5086}
5087
5092
5094{
5095 if (backBufferRtv) {
5096 backBufferRtv->Release();
5097 backBufferRtv = nullptr;
5098 }
5099 if (backBufferRtvRight) {
5100 backBufferRtvRight->Release();
5101 backBufferRtvRight = nullptr;
5102 }
5103 if (backBufferTex) {
5104 backBufferTex->Release();
5105 backBufferTex = nullptr;
5106 }
5107 for (int i = 0; i < BUFFER_COUNT; ++i) {
5108 if (msaaRtv[i]) {
5109 msaaRtv[i]->Release();
5110 msaaRtv[i] = nullptr;
5111 }
5112 if (msaaTex[i]) {
5113 msaaTex[i]->Release();
5114 msaaTex[i] = nullptr;
5115 }
5116 }
5117}
5118
5120{
5121 if (!swapChain)
5122 return;
5123
5125
5126 timestamps.destroy();
5127
5128 swapChain->Release();
5129 swapChain = nullptr;
5130
5131 if (dcompVisual) {
5132 dcompVisual->Release();
5133 dcompVisual = nullptr;
5134 }
5135
5136 if (dcompTarget) {
5137 dcompTarget->Release();
5138 dcompTarget = nullptr;
5139 }
5140
5141 if (frameLatencyWaitableObject) {
5142 CloseHandle(frameLatencyWaitableObject);
5143 frameLatencyWaitableObject = nullptr;
5144 }
5145
5146 QDxgiVSyncService::instance()->unregisterWindow(window);
5147
5148 QRHI_RES_RHI(QRhiD3D11);
5149 if (rhiD) {
5150 rhiD->unregisterResource(this);
5151 // See Deferred Destruction Issues with Flip Presentation Swap Chains in
5152 // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush
5153 rhiD->context->Flush();
5154 }
5155}
5156
5158{
5159 return &cb;
5160}
5161
5166
5168{
5169 return targetBuffer == StereoTargetBuffer::LeftBuffer? &rt: &rtRight;
5170}
5171
5173{
5174 Q_ASSERT(m_window);
5175 return m_window->size() * m_window->devicePixelRatio();
5176}
5177
5179{
5180 if (f == SDR)
5181 return true;
5182
5183 if (!m_window) {
5184 qWarning("Attempted to call isFormatSupported() without a window set");
5185 return false;
5186 }
5187
5188 QRHI_RES_RHI(QRhiD3D11);
5189 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window))
5190 return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
5191
5192 return false;
5193}
5194
5196{
5197 QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
5198 // Must use m_window, not window, given this may be called before createOrResize().
5199 if (m_window) {
5200 QRHI_RES_RHI(QRhiD3D11);
5201 info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window);
5202 }
5203 return info;
5204}
5205
5207{
5208 QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
5209 QRHI_RES_RHI(QRhiD3D11);
5210 rhiD->registerResource(rpD, false);
5211 return rpD;
5212}
5213
5214bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
5215 ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
5216{
5217 D3D11_TEXTURE2D_DESC desc = {};
5218 desc.Width = UINT(size.width());
5219 desc.Height = UINT(size.height());
5220 desc.MipLevels = 1;
5221 desc.ArraySize = 1;
5222 desc.Format = format;
5223 desc.SampleDesc = sampleDesc;
5224 desc.Usage = D3D11_USAGE_DEFAULT;
5225 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
5226
5227 QRHI_RES_RHI(QRhiD3D11);
5228 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex);
5229 if (FAILED(hr)) {
5230 qWarning("Failed to create color buffer texture: %s",
5231 qPrintable(QSystemError::windowsComString(hr)));
5232 return false;
5233 }
5234
5235 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5236 rtvDesc.Format = format;
5237 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
5238 hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv);
5239 if (FAILED(hr)) {
5240 qWarning("Failed to create color buffer rtv: %s",
5241 qPrintable(QSystemError::windowsComString(hr)));
5242 (*tex)->Release();
5243 *tex = nullptr;
5244 return false;
5245 }
5246
5247 return true;
5248}
5249
5251{
5252 if (dcompDevice)
5253 return true;
5254
5255 qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
5256 dcompDevice = QRhiD3D::createDirectCompositionDevice();
5257 return dcompDevice ? true : false;
5258}
5259
5260static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
5261static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
5262
5264{
5265 // Can be called multiple times due to window resizes - that is not the
5266 // same as a simple destroy+create (as with other resources). Just need to
5267 // resize the buffers then.
5268
5269 const bool needsRegistration = !window || window != m_window;
5270 const bool stereo = m_window->format().stereo();
5271
5272 // except if the window actually changes
5273 if (window && window != m_window)
5274 destroy();
5275
5276 window = m_window;
5277 m_currentPixelSize = surfacePixelSize();
5278 pixelSize = m_currentPixelSize;
5279
5280 if (pixelSize.isEmpty())
5281 return false;
5282
5283 HWND hwnd = reinterpret_cast<HWND>(window->winId());
5284 HRESULT hr;
5285
5286 QRHI_RES_RHI(QRhiD3D11);
5287
5288 if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
5290 if (!dcompTarget) {
5291 hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
5292 if (FAILED(hr)) {
5293 qWarning("Failed to create Direct Compsition target for the window: %s",
5294 qPrintable(QSystemError::windowsComString(hr)));
5295 }
5296 }
5297 if (dcompTarget && !dcompVisual) {
5298 hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
5299 if (FAILED(hr)) {
5300 qWarning("Failed to create DirectComposition visual: %s",
5301 qPrintable(QSystemError::windowsComString(hr)));
5302 }
5303 }
5304 }
5305 // simple consistency check
5306 if (window->requestedFormat().alphaBufferSize() <= 0)
5307 qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
5308 "This may lead to problems.");
5309 }
5310
5311 swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
5312 swapChainFlags = 0;
5313
5314 // A non-flip swapchain can do Present(0) as expected without
5315 // ALLOW_TEARING, and ALLOW_TEARING is not compatible with it at all so the
5316 // flag must not be set then. Whereas for flip we should use it, if
5317 // supported, to get better results for 'unthrottled' presentation.
5318 if (swapInterval == 0 && rhiD->supportsAllowTearing)
5319 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
5320
5321 // maxFrameLatency 0 means no waitable object usage.
5322 // Ignore it also when NoVSync is on, and when using WARP.
5323 const bool useFrameLatencyWaitableObject = rhiD->maxFrameLatency != 0
5324 && swapInterval != 0
5325 && rhiD->driverInfoStruct.deviceType != QRhiDriverInfo::CpuDevice;
5326
5327 if (useFrameLatencyWaitableObject) {
5328 // the flag is not supported in real fullscreen on D3D11, but perhaps that's fine since we only do borderless
5329 swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
5330 }
5331
5332 if (!swapChain) {
5333 sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
5334 colorFormat = DEFAULT_FORMAT;
5335 srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
5336
5337 DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
5338 if (m_format != SDR) {
5339 if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) {
5340 // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
5341 switch (m_format) {
5342 case HDRExtendedSrgbLinear:
5343 colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
5344 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
5345 srgbAdjustedColorFormat = colorFormat;
5346 break;
5347 case HDR10:
5348 colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
5349 hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
5350 srgbAdjustedColorFormat = colorFormat;
5351 break;
5352 default:
5353 break;
5354 }
5355 } else {
5356 // This happens also when Use HDR is set to Off in the Windows
5357 // Display settings. Show a helpful warning, but continue with the
5358 // default non-HDR format.
5359 qWarning("The output associated with the window is not HDR capable "
5360 "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
5361 }
5362 }
5363
5364 // We use a FLIP model swapchain which implies a buffer count of 2
5365 // (as opposed to the old DISCARD with back buffer count == 1).
5366 // This makes no difference for the rest of the stuff except that
5367 // automatic MSAA is unsupported and needs to be implemented via a
5368 // custom multisample render target and an explicit resolve.
5369
5370 DXGI_SWAP_CHAIN_DESC1 desc = {};
5371 desc.Width = UINT(pixelSize.width());
5372 desc.Height = UINT(pixelSize.height());
5373 desc.Format = colorFormat;
5374 desc.SampleDesc.Count = 1;
5375 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
5376 desc.BufferCount = BUFFER_COUNT;
5377 desc.Flags = swapChainFlags;
5378 desc.Scaling = rhiD->useLegacySwapchainModel ? DXGI_SCALING_STRETCH : DXGI_SCALING_NONE;
5379 desc.SwapEffect = rhiD->useLegacySwapchainModel ? DXGI_SWAP_EFFECT_DISCARD : DXGI_SWAP_EFFECT_FLIP_DISCARD;
5380 desc.Stereo = stereo;
5381
5382 if (dcompVisual) {
5383 // With DirectComposition setting AlphaMode to STRAIGHT fails the
5384 // swapchain creation, whereas the result seems to be identical
5385 // with any of the other values, including IGNORE. (?)
5386 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
5387
5388 // DirectComposition has its own limitations, cannot use
5389 // SCALING_NONE. So with semi-transparency requested we are forced
5390 // to SCALING_STRETCH.
5391 desc.Scaling = DXGI_SCALING_STRETCH;
5392 }
5393
5394 IDXGIFactory2 *fac = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory);
5395 IDXGISwapChain1 *sc1;
5396
5397 if (dcompVisual)
5398 hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
5399 else
5400 hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
5401
5402 // If failed and we tried a HDR format, then try with SDR. This
5403 // matches other backends, such as Vulkan where if the format is
5404 // not supported, the default one is used instead.
5405 if (FAILED(hr) && m_format != SDR) {
5406 colorFormat = DEFAULT_FORMAT;
5407 desc.Format = DEFAULT_FORMAT;
5408 if (dcompVisual)
5409 hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
5410 else
5411 hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
5412 }
5413
5414 if (SUCCEEDED(hr)) {
5415 swapChain = sc1;
5416 IDXGISwapChain3 *sc3 = nullptr;
5417 if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
5418 if (m_format != SDR) {
5419 hr = sc3->SetColorSpace1(hdrColorSpace);
5420 if (FAILED(hr))
5421 qWarning("Failed to set color space on swapchain: %s",
5422 qPrintable(QSystemError::windowsComString(hr)));
5423 }
5424 if (useFrameLatencyWaitableObject) {
5425 sc3->SetMaximumFrameLatency(rhiD->maxFrameLatency);
5426 frameLatencyWaitableObject = sc3->GetFrameLatencyWaitableObject();
5427 }
5428 sc3->Release();
5429 } else {
5430 if (m_format != SDR)
5431 qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
5432 if (useFrameLatencyWaitableObject) {
5433 IDXGISwapChain2 *sc2 = nullptr;
5434 if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain2), reinterpret_cast<void **>(&sc2)))) {
5435 sc2->SetMaximumFrameLatency(rhiD->maxFrameLatency);
5436 frameLatencyWaitableObject = sc2->GetFrameLatencyWaitableObject();
5437 sc2->Release();
5438 } else { // this cannot really happen since we require DXGIFactory2
5439 qWarning("IDXGISwapChain2 not available, FrameLatencyWaitableObject cannot be used");
5440 }
5441 }
5442 }
5443 if (dcompVisual) {
5444 hr = dcompVisual->SetContent(sc1);
5445 if (SUCCEEDED(hr)) {
5446 hr = dcompTarget->SetRoot(dcompVisual);
5447 if (FAILED(hr)) {
5448 qWarning("Failed to associate Direct Composition visual with the target: %s",
5449 qPrintable(QSystemError::windowsComString(hr)));
5450 }
5451 } else {
5452 qWarning("Failed to set content for Direct Composition visual: %s",
5453 qPrintable(QSystemError::windowsComString(hr)));
5454 }
5455 } else {
5456 // disable Alt+Enter; not relevant when using DirectComposition
5457 rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
5458 }
5459 }
5460 if (FAILED(hr)) {
5461 qWarning("Failed to create D3D11 swapchain: %s"
5462 " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
5463 qPrintable(QSystemError::windowsComString(hr)),
5464 desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
5465 desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
5466 return false;
5467 }
5468 } else {
5470 // flip model -> buffer count is the real buffer count, not 1 like with the legacy modes
5471 hr = swapChain->ResizeBuffers(UINT(BUFFER_COUNT), UINT(pixelSize.width()), UINT(pixelSize.height()),
5472 colorFormat, swapChainFlags);
5473 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
5474 qWarning("Device loss detected in ResizeBuffers()");
5475 rhiD->deviceLost = true;
5476 return false;
5477 } else if (FAILED(hr)) {
5478 qWarning("Failed to resize D3D11 swapchain: %s",
5479 qPrintable(QSystemError::windowsComString(hr)));
5480 return false;
5481 }
5482 }
5483
5484 // This looks odd (for FLIP_*, esp. compared with backends for Vulkan
5485 // & co.) but the backbuffer is always at index 0, with magic underneath.
5486 // Some explanation from
5487 // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
5488 //
5489 // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
5490 // Every call to Present implicitly changed the resource identity of the
5491 // returned interface. Direct3D 12 no longer supports that implicit
5492 // resource identity change, due to the CPU overhead required and the
5493 // flexible resource descriptor design. As a result, the application must
5494 // manually call GetBuffer for every each buffer created with the
5495 // swapchain."
5496
5497 // So just query index 0 once (per resize) and be done with it.
5498 hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&backBufferTex));
5499 if (FAILED(hr)) {
5500 qWarning("Failed to query swapchain backbuffer: %s",
5501 qPrintable(QSystemError::windowsComString(hr)));
5502 return false;
5503 }
5504 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
5505 rtvDesc.Format = srgbAdjustedColorFormat;
5506 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
5507 hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
5508 if (FAILED(hr)) {
5509 qWarning("Failed to create rtv for swapchain backbuffer: %s",
5510 qPrintable(QSystemError::windowsComString(hr)));
5511 return false;
5512 }
5513
5514 if (stereo) {
5515 // Create a second render target view for the right eye
5516 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
5517 rtvDesc.Texture2DArray.FirstArraySlice = 1;
5518 rtvDesc.Texture2DArray.ArraySize = 1;
5519 hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtvRight);
5520 if (FAILED(hr)) {
5521 qWarning("Failed to create rtv for swapchain backbuffer (right eye): %s",
5522 qPrintable(QSystemError::windowsComString(hr)));
5523 return false;
5524 }
5525 }
5526
5527 // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
5528 for (int i = 0; i < BUFFER_COUNT; ++i) {
5529 if (sampleDesc.Count > 1) {
5530 if (!newColorBuffer(pixelSize, srgbAdjustedColorFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
5531 return false;
5532 }
5533 }
5534
5535 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
5536 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
5537 m_depthStencil->sampleCount(), m_sampleCount);
5538 }
5539 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
5540 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
5541 m_depthStencil->setPixelSize(pixelSize);
5542 if (!m_depthStencil->create())
5543 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
5544 pixelSize.width(), pixelSize.height());
5545 } else {
5546 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
5547 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
5548 pixelSize.width(), pixelSize.height());
5549 }
5550 }
5551
5552 currentFrameSlot = 0;
5553 lastFrameLatencyWaitSlot = -1; // wait already in the first frame, as instructed in the dxgi docs
5554 frameCount = 0;
5555 ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
5556
5557 rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
5558 QD3D11SwapChainRenderTarget *rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rt);
5559 rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
5560 rtD->d.pixelSize = pixelSize;
5561 rtD->d.dpr = float(window->devicePixelRatio());
5562 rtD->d.sampleCount = int(sampleDesc.Count);
5563 rtD->d.colorAttCount = 1;
5564 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
5565
5566 if (stereo) {
5567 rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rtRight);
5568 rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
5569 rtD->d.pixelSize = pixelSize;
5570 rtD->d.dpr = float(window->devicePixelRatio());
5571 rtD->d.sampleCount = int(sampleDesc.Count);
5572 rtD->d.colorAttCount = 1;
5573 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
5574 rtD->d.rtv[0] = backBufferRtvRight;
5575 rtD->d.dsv = ds ? ds->dsv : nullptr;
5576 }
5577
5578 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps)) {
5579 timestamps.prepare(rhiD);
5580 // timestamp queries are optional so we can go on even if they failed
5581 }
5582
5583 QDxgiVSyncService::instance()->registerWindow(window);
5584
5585 if (needsRegistration)
5586 rhiD->registerResource(this);
5587
5588 return true;
5589}
5590
5591bool RenderTargetUavUpdateState::update(QD3D11RenderTargetData *data, ID3D11UnorderedAccessView *const *uavs, int count)
5592{
5593 bool ret = false;
5594 if (dsv != data->dsv) {
5595 dsv = data->dsv;
5596 ret = true;
5597 }
5598 for (int i = 0; i < data->colorAttCount; i++) {
5599 ret |= rtv[i] != data->rtv[i];
5600 rtv[i] = data->rtv[i];
5601 }
5603 ret |= rtv[i] != nullptr;
5604 rtv[i] = nullptr;
5605 }
5606 for (int i = 0; i < count; i++) {
5607 ret |= uav[i] != uavs[i];
5608 uav[i] = uavs[i];
5609 }
5610 for (int i = count; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; i++) {
5611 ret |= uav[i] != nullptr;
5612 uav[i] = nullptr;
5613 }
5614 return ret;
5615}
5616
5617
5618QT_END_NAMESPACE
QRhiDriverInfo info() const override
const char * constData() const
Definition qrhi_p.h:366
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:591
void fillDriverInfo(QRhiDriverInfo *info, const DXGI_ADAPTER_DESC1 &desc)
@ UnBounded
Definition qrhi_p.h:279
@ Bounded
Definition qrhi_p.h:280
#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:1834
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1555
bool update(QD3D11RenderTargetData *data, ID3D11UnorderedAccessView *const *uavs=nullptr, int count=0)