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