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