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