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