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