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