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