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