1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Gui module
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36
37 #include "qrhid3d11_p_p.h"
38 #include "qshader_p.h"
39 #include "cs_tdr_p.h"
40 #include <QWindow>
41 #include <QOperatingSystemVersion>
42 #include <qmath.h>
43 #include <private/qsystemlibrary_p.h>
44
45 #include <d3dcompiler.h>
46 #include <comdef.h>
47
48 QT_BEGIN_NAMESPACE
49
50 /*
51 Direct3D 11 backend. Provides a double-buffered flip model (FLIP_DISCARD)
52 swapchain. Textures and "static" buffers are USAGE_DEFAULT, leaving it to
53 UpdateSubResource to upload the data in any way it sees fit. "Dynamic"
54 buffers are USAGE_DYNAMIC and updating is done by mapping with WRITE_DISCARD.
55 (so here QRhiBuffer keeps a copy of the buffer contents and all of it is
56 memcpy'd every time, leaving the rest (juggling with the memory area Map
57 returns) to the driver).
58 */
59
60 /*!
61 \class QRhiD3D11InitParams
62 \internal
63 \inmodule QtGui
64 \brief Direct3D 11 specific initialization parameters.
65
66 A D3D11-based QRhi needs no special parameters for initialization. If
67 desired, enableDebugLayer can be set to \c true to enable the Direct3D
68 debug layer. This can be useful during development, but should be avoided
69 in production builds.
70
71 \badcode
72 QRhiD3D11InitParams params;
73 params.enableDebugLayer = true;
74 rhi = QRhi::create(QRhi::D3D11, ¶ms);
75 \endcode
76
77 \note QRhiSwapChain should only be used in combination with QWindow
78 instances that have their surface type set to QSurface::OpenGLSurface.
79 There are currently no Direct3D specifics in the Windows platform support
80 of Qt and therefore there is no separate QSurface type available.
81
82 \section2 Working with existing Direct3D 11 devices
83
84 When interoperating with another graphics engine, it may be necessary to
85 get a QRhi instance that uses the same Direct3D device. This can be
86 achieved by passing a pointer to a QRhiD3D11NativeHandles to
87 QRhi::create(). Both the device and the device context must be set to a
88 non-null value then.
89
90 The QRhi does not take ownership of any of the external objects.
91
92 \note QRhi works with immediate contexts only. Deferred contexts are not
93 used in any way.
94
95 \note Regardless of using an imported or a QRhi-created device context, the
96 \c ID3D11DeviceContext1 interface (Direct3D 11.1) must be supported.
97 Initialization will fail otherwise.
98 */
99
100 /*!
101 \class QRhiD3D11NativeHandles
102 \internal
103 \inmodule QtGui
104 \brief Holds the D3D device and device context used by the QRhi.
105
106 \note The class uses \c{void *} as the type since including the COM-based
107 \c{d3d11.h} headers is not acceptable here. The actual types are
108 \c{ID3D11Device *} and \c{ID3D11DeviceContext *}.
109 */
110
111 // help mingw with its ancient sdk headers
112 #ifndef DXGI_ADAPTER_FLAG_SOFTWARE
113 #define DXGI_ADAPTER_FLAG_SOFTWARE 2
114 #endif
115
116 #ifndef D3D11_1_UAV_SLOT_COUNT
117 #define D3D11_1_UAV_SLOT_COUNT 64
118 #endif
119
QRhiD3D11(QRhiD3D11InitParams * params,QRhiD3D11NativeHandles * importDevice)120 QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice)
121 : ofr(this),
122 deviceCurse(this)
123 {
124 debugLayer = params->enableDebugLayer;
125
126 deviceCurse.framesToActivate = params->framesUntilKillingDeviceViaTdr;
127 deviceCurse.permanent = params->repeatDeviceKill;
128
129 importedDevice = importDevice != nullptr;
130 if (importedDevice) {
131 dev = reinterpret_cast<ID3D11Device *>(importDevice->dev);
132 if (dev) {
133 ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importDevice->context);
134 if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
135 // get rid of the ref added by QueryInterface
136 ctx->Release();
137 } else {
138 qWarning("ID3D11DeviceContext1 not supported by context, cannot import");
139 importedDevice = false;
140 }
141 } else {
142 qWarning("No ID3D11Device given, cannot import");
143 importedDevice = false;
144 }
145 }
146 }
147
comErrorMessage(HRESULT hr)148 static QString comErrorMessage(HRESULT hr)
149 {
150 #ifndef Q_OS_WINRT
151 const _com_error comError(hr);
152 #else
153 const _com_error comError(hr, nullptr);
154 #endif
155 QString result = QLatin1String("Error 0x") + QString::number(ulong(hr), 16);
156 if (const wchar_t *msg = comError.ErrorMessage())
157 result += QLatin1String(": ") + QString::fromWCharArray(msg);
158 return result;
159 }
160
161 template <class Int>
aligned(Int v,Int byteAlign)162 inline Int aligned(Int v, Int byteAlign)
163 {
164 return (v + byteAlign - 1) & ~(byteAlign - 1);
165 }
166
createDXGIFactory2()167 static IDXGIFactory1 *createDXGIFactory2()
168 {
169 IDXGIFactory1 *result = nullptr;
170 if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
171 using PtrCreateDXGIFactory2 = HRESULT (WINAPI *)(UINT, REFIID, void **);
172 QSystemLibrary dxgilib(QStringLiteral("dxgi"));
173 if (auto createDXGIFactory2 = reinterpret_cast<PtrCreateDXGIFactory2>(dxgilib.resolve("CreateDXGIFactory2"))) {
174 const HRESULT hr = createDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&result));
175 if (FAILED(hr)) {
176 qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
177 result = nullptr;
178 }
179 } else {
180 qWarning("Unable to resolve CreateDXGIFactory2()");
181 }
182 }
183 return result;
184 }
185
createDXGIFactory1()186 static IDXGIFactory1 *createDXGIFactory1()
187 {
188 IDXGIFactory1 *result = nullptr;
189 const HRESULT hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&result));
190 if (FAILED(hr)) {
191 qWarning("CreateDXGIFactory1() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
192 result = nullptr;
193 }
194 return result;
195 }
196
create(QRhi::Flags flags)197 bool QRhiD3D11::create(QRhi::Flags flags)
198 {
199 Q_UNUSED(flags);
200
201 uint devFlags = 0;
202 if (debugLayer)
203 devFlags |= D3D11_CREATE_DEVICE_DEBUG;
204
205 dxgiFactory = createDXGIFactory2();
206 if (dxgiFactory != nullptr) {
207 hasDxgi2 = true;
208 supportsFlipDiscardSwapchain = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
209 && !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
210 } else {
211 dxgiFactory = createDXGIFactory1();
212 hasDxgi2 = false;
213 supportsFlipDiscardSwapchain = false;
214 }
215
216 if (dxgiFactory == nullptr)
217 return false;
218
219 qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s",
220 hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false");
221
222 if (!importedDevice) {
223 IDXGIAdapter1 *adapterToUse = nullptr;
224 IDXGIAdapter1 *adapter;
225 int requestedAdapterIndex = -1;
226 if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
227 requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
228
229 if (requestedAdapterIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
230 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
231 DXGI_ADAPTER_DESC1 desc;
232 adapter->GetDesc1(&desc);
233 adapter->Release();
234 if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
235 requestedAdapterIndex = adapterIndex;
236 break;
237 }
238 }
239 }
240
241 for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
242 DXGI_ADAPTER_DESC1 desc;
243 adapter->GetDesc1(&desc);
244 const QString name = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description));
245 qCDebug(QRHI_LOG_INFO, "Adapter %d: '%s' (vendor 0x%X device 0x%X flags 0x%X)",
246 adapterIndex,
247 qPrintable(name),
248 desc.VendorId,
249 desc.DeviceId,
250 desc.Flags);
251 if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
252 adapterToUse = adapter;
253 qCDebug(QRHI_LOG_INFO, " using this adapter");
254 } else {
255 adapter->Release();
256 }
257 }
258 if (!adapterToUse) {
259 qWarning("No adapter");
260 return false;
261 }
262
263 ID3D11DeviceContext *ctx = nullptr;
264 HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags,
265 nullptr, 0, D3D11_SDK_VERSION,
266 &dev, &featureLevel, &ctx);
267 adapterToUse->Release();
268 if (FAILED(hr)) {
269 qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr)));
270 return false;
271 }
272 if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
273 ctx->Release();
274 } else {
275 qWarning("ID3D11DeviceContext1 not supported");
276 return false;
277 }
278 } else {
279 Q_ASSERT(dev && context);
280 featureLevel = dev->GetFeatureLevel();
281 }
282
283 if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
284 annotations = nullptr;
285
286 deviceLost = false;
287
288 nativeHandlesStruct.dev = dev;
289 nativeHandlesStruct.context = context;
290
291 if (deviceCurse.framesToActivate > 0)
292 deviceCurse.initResources();
293
294 return true;
295 }
296
clearShaderCache()297 void QRhiD3D11::clearShaderCache()
298 {
299 for (Shader &s : m_shaderCache)
300 s.s->Release();
301
302 m_shaderCache.clear();
303 }
304
destroy()305 void QRhiD3D11::destroy()
306 {
307 finishActiveReadbacks();
308
309 clearShaderCache();
310
311 deviceCurse.releaseResources();
312
313 if (annotations) {
314 annotations->Release();
315 annotations = nullptr;
316 }
317
318 if (!importedDevice) {
319 if (context) {
320 context->Release();
321 context = nullptr;
322 }
323 if (dev) {
324 dev->Release();
325 dev = nullptr;
326 }
327 }
328
329 if (dxgiFactory) {
330 dxgiFactory->Release();
331 dxgiFactory = nullptr;
332 }
333 }
334
reportLiveObjects(ID3D11Device * device)335 void QRhiD3D11::reportLiveObjects(ID3D11Device *device)
336 {
337 // this works only when params.enableDebugLayer was true
338 ID3D11Debug *debug;
339 if (SUCCEEDED(device->QueryInterface(IID_ID3D11Debug, reinterpret_cast<void **>(&debug)))) {
340 debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
341 debug->Release();
342 }
343 }
344
supportedSampleCounts() const345 QVector<int> QRhiD3D11::supportedSampleCounts() const
346 {
347 return { 1, 2, 4, 8 };
348 }
349
effectiveSampleCount(int sampleCount) const350 DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const
351 {
352 DXGI_SAMPLE_DESC desc;
353 desc.Count = 1;
354 desc.Quality = 0;
355
356 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
357 int s = qBound(1, sampleCount, 64);
358
359 if (!supportedSampleCounts().contains(s)) {
360 qWarning("Attempted to set unsupported sample count %d", sampleCount);
361 return desc;
362 }
363
364 desc.Count = UINT(s);
365 if (s > 1)
366 desc.Quality = UINT(D3D11_STANDARD_MULTISAMPLE_PATTERN);
367 else
368 desc.Quality = 0;
369
370 return desc;
371 }
372
createSwapChain()373 QRhiSwapChain *QRhiD3D11::createSwapChain()
374 {
375 return new QD3D11SwapChain(this);
376 }
377
createBuffer(QRhiBuffer::Type type,QRhiBuffer::UsageFlags usage,int size)378 QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
379 {
380 return new QD3D11Buffer(this, type, usage, size);
381 }
382
ubufAlignment() const383 int QRhiD3D11::ubufAlignment() const
384 {
385 return 256;
386 }
387
isYUpInFramebuffer() const388 bool QRhiD3D11::isYUpInFramebuffer() const
389 {
390 return false;
391 }
392
isYUpInNDC() const393 bool QRhiD3D11::isYUpInNDC() const
394 {
395 return true;
396 }
397
isClipDepthZeroToOne() const398 bool QRhiD3D11::isClipDepthZeroToOne() const
399 {
400 return true;
401 }
402
clipSpaceCorrMatrix() const403 QMatrix4x4 QRhiD3D11::clipSpaceCorrMatrix() const
404 {
405 // Like with Vulkan, but Y is already good.
406
407 static QMatrix4x4 m;
408 if (m.isIdentity()) {
409 // NB the ctor takes row-major
410 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
411 0.0f, 1.0f, 0.0f, 0.0f,
412 0.0f, 0.0f, 0.5f, 0.5f,
413 0.0f, 0.0f, 0.0f, 1.0f);
414 }
415 return m;
416 }
417
isTextureFormatSupported(QRhiTexture::Format format,QRhiTexture::Flags flags) const418 bool QRhiD3D11::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
419 {
420 Q_UNUSED(flags);
421
422 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12)
423 return false;
424
425 return true;
426 }
427
isFeatureSupported(QRhi::Feature feature) const428 bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
429 {
430 switch (feature) {
431 case QRhi::MultisampleTexture:
432 return true;
433 case QRhi::MultisampleRenderBuffer:
434 return true;
435 case QRhi::DebugMarkers:
436 return annotations != nullptr;
437 case QRhi::Timestamps:
438 return true;
439 case QRhi::Instancing:
440 return true;
441 case QRhi::CustomInstanceStepRate:
442 return true;
443 case QRhi::PrimitiveRestart:
444 return true;
445 case QRhi::NonDynamicUniformBuffers:
446 return false; // because UpdateSubresource cannot deal with this
447 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
448 return true;
449 case QRhi::NPOTTextureRepeat:
450 return true;
451 case QRhi::RedOrAlpha8IsRed:
452 return true;
453 case QRhi::ElementIndexUint:
454 return true;
455 case QRhi::Compute:
456 return true;
457 case QRhi::WideLines:
458 return false;
459 case QRhi::VertexShaderPointSize:
460 return false;
461 case QRhi::BaseVertex:
462 return true;
463 case QRhi::BaseInstance:
464 return true;
465 case QRhi::TriangleFanTopology:
466 return false;
467 case QRhi::ReadBackNonUniformBuffer:
468 return true;
469 case QRhi::ReadBackNonBaseMipLevel:
470 return true;
471 case QRhi::TexelFetch:
472 return true;
473 default:
474 Q_UNREACHABLE();
475 return false;
476 }
477 }
478
resourceLimit(QRhi::ResourceLimit limit) const479 int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
480 {
481 switch (limit) {
482 case QRhi::TextureSizeMin:
483 return 1;
484 case QRhi::TextureSizeMax:
485 return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
486 case QRhi::MaxColorAttachments:
487 return 8;
488 case QRhi::FramesInFlight:
489 // From our perspective. What D3D does internally is another question
490 // (there could be pipelining, helped f.ex. by our MAP_DISCARD based
491 // uniform buffer update strategy), but that's out of our hands and
492 // does not concern us here.
493 return 1;
494 case QRhi::MaxAsyncReadbackFrames:
495 return 1;
496 default:
497 Q_UNREACHABLE();
498 return 0;
499 }
500 }
501
nativeHandles()502 const QRhiNativeHandles *QRhiD3D11::nativeHandles()
503 {
504 return &nativeHandlesStruct;
505 }
506
sendVMemStatsToProfiler()507 void QRhiD3D11::sendVMemStatsToProfiler()
508 {
509 // nothing to do here
510 }
511
makeThreadLocalNativeContextCurrent()512 bool QRhiD3D11::makeThreadLocalNativeContextCurrent()
513 {
514 // not applicable
515 return false;
516 }
517
releaseCachedResources()518 void QRhiD3D11::releaseCachedResources()
519 {
520 clearShaderCache();
521 }
522
isDeviceLost() const523 bool QRhiD3D11::isDeviceLost() const
524 {
525 return deviceLost;
526 }
527
createRenderBuffer(QRhiRenderBuffer::Type type,const QSize & pixelSize,int sampleCount,QRhiRenderBuffer::Flags flags)528 QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
529 int sampleCount, QRhiRenderBuffer::Flags flags)
530 {
531 return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags);
532 }
533
createTexture(QRhiTexture::Format format,const QSize & pixelSize,int sampleCount,QRhiTexture::Flags flags)534 QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
535 int sampleCount, QRhiTexture::Flags flags)
536 {
537 return new QD3D11Texture(this, format, pixelSize, sampleCount, flags);
538 }
539
createSampler(QRhiSampler::Filter magFilter,QRhiSampler::Filter minFilter,QRhiSampler::Filter mipmapMode,QRhiSampler::AddressMode u,QRhiSampler::AddressMode v,QRhiSampler::AddressMode w)540 QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
541 QRhiSampler::Filter mipmapMode,
542 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
543 {
544 return new QD3D11Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
545 }
546
createTextureRenderTarget(const QRhiTextureRenderTargetDescription & desc,QRhiTextureRenderTarget::Flags flags)547 QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
548 QRhiTextureRenderTarget::Flags flags)
549 {
550 return new QD3D11TextureRenderTarget(this, desc, flags);
551 }
552
createGraphicsPipeline()553 QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
554 {
555 return new QD3D11GraphicsPipeline(this);
556 }
557
createComputePipeline()558 QRhiComputePipeline *QRhiD3D11::createComputePipeline()
559 {
560 return new QD3D11ComputePipeline(this);
561 }
562
createShaderResourceBindings()563 QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings()
564 {
565 return new QD3D11ShaderResourceBindings(this);
566 }
567
setGraphicsPipeline(QRhiCommandBuffer * cb,QRhiGraphicsPipeline * ps)568 void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
569 {
570 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
571 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
572 QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, ps);
573 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
574
575 if (pipelineChanged) {
576 cbD->currentGraphicsPipeline = ps;
577 cbD->currentComputePipeline = nullptr;
578 cbD->currentPipelineGeneration = psD->generation;
579
580 QD3D11CommandBuffer::Command cmd;
581 cmd.cmd = QD3D11CommandBuffer::Command::BindGraphicsPipeline;
582 cmd.args.bindGraphicsPipeline.ps = psD;
583 cbD->commands.append(cmd);
584 }
585 }
586
587 static const int RBM_SUPPORTED_STAGES = 3;
588 static const int RBM_VERTEX = 0;
589 static const int RBM_FRAGMENT = 1;
590 static const int RBM_COMPUTE = 2;
591
setShaderResources(QRhiCommandBuffer * cb,QRhiShaderResourceBindings * srb,int dynamicOffsetCount,const QRhiCommandBuffer::DynamicOffset * dynamicOffsets)592 void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
593 int dynamicOffsetCount,
594 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
595 {
596 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
597 Q_ASSERT(cbD->recordingPass != QD3D11CommandBuffer::NoPass);
598 QD3D11GraphicsPipeline *gfxPsD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
599 QD3D11ComputePipeline *compPsD = QRHI_RES(QD3D11ComputePipeline, cbD->currentComputePipeline);
600
601 if (!srb) {
602 if (gfxPsD)
603 srb = gfxPsD->m_shaderResourceBindings;
604 else
605 srb = compPsD->m_shaderResourceBindings;
606 }
607
608 QD3D11ShaderResourceBindings *srbD = QRHI_RES(QD3D11ShaderResourceBindings, srb);
609
610 bool hasDynamicOffsetInSrb = false;
611 bool srbUpdate = false;
612 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
613 const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
614 QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
615 switch (b->type) {
616 case QRhiShaderResourceBinding::UniformBuffer:
617 {
618 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
619 if (bufD->m_type == QRhiBuffer::Dynamic)
620 executeBufferHostWrites(bufD);
621
622 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
623 srbUpdate = true;
624 bd.ubuf.id = bufD->m_id;
625 bd.ubuf.generation = bufD->generation;
626 }
627
628 if (b->u.ubuf.hasDynamicOffset)
629 hasDynamicOffsetInSrb = true;
630 }
631 break;
632 case QRhiShaderResourceBinding::SampledTexture:
633 {
634 const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
635 if (bd.stex.count != data->count) {
636 bd.stex.count = data->count;
637 srbUpdate = true;
638 }
639 for (int elem = 0; elem < data->count; ++elem) {
640 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
641 QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
642 if (texD->generation != bd.stex.d[elem].texGeneration
643 || texD->m_id != bd.stex.d[elem].texId
644 || samplerD->generation != bd.stex.d[elem].samplerGeneration
645 || samplerD->m_id != bd.stex.d[elem].samplerId)
646 {
647 srbUpdate = true;
648 bd.stex.d[elem].texId = texD->m_id;
649 bd.stex.d[elem].texGeneration = texD->generation;
650 bd.stex.d[elem].samplerId = samplerD->m_id;
651 bd.stex.d[elem].samplerGeneration = samplerD->generation;
652 }
653 }
654 }
655 break;
656 case QRhiShaderResourceBinding::ImageLoad:
657 case QRhiShaderResourceBinding::ImageStore:
658 case QRhiShaderResourceBinding::ImageLoadStore:
659 {
660 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
661 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
662 srbUpdate = true;
663 bd.simage.id = texD->m_id;
664 bd.simage.generation = texD->generation;
665 }
666 }
667 break;
668 case QRhiShaderResourceBinding::BufferLoad:
669 case QRhiShaderResourceBinding::BufferStore:
670 case QRhiShaderResourceBinding::BufferLoadStore:
671 {
672 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
673 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
674 srbUpdate = true;
675 bd.sbuf.id = bufD->m_id;
676 bd.sbuf.generation = bufD->generation;
677 }
678 }
679 break;
680 default:
681 Q_UNREACHABLE();
682 break;
683 }
684 }
685
686 if (srbUpdate) {
687 const QShader::NativeResourceBindingMap *resBindMaps[RBM_SUPPORTED_STAGES];
688 memset(resBindMaps, 0, sizeof(resBindMaps));
689 if (gfxPsD) {
690 resBindMaps[RBM_VERTEX] = &gfxPsD->vs.nativeResourceBindingMap;
691 resBindMaps[RBM_FRAGMENT] = &gfxPsD->fs.nativeResourceBindingMap;
692 } else {
693 resBindMaps[RBM_COMPUTE] = &compPsD->cs.nativeResourceBindingMap;
694 }
695 updateShaderResourceBindings(srbD, resBindMaps);
696 }
697
698 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
699 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
700
701 if (srbChanged || srbRebuilt || srbUpdate || hasDynamicOffsetInSrb) {
702 if (gfxPsD) {
703 cbD->currentGraphicsSrb = srb;
704 cbD->currentComputeSrb = nullptr;
705 } else {
706 cbD->currentGraphicsSrb = nullptr;
707 cbD->currentComputeSrb = srb;
708 }
709 cbD->currentSrbGeneration = srbD->generation;
710
711 QD3D11CommandBuffer::Command cmd;
712 cmd.cmd = QD3D11CommandBuffer::Command::BindShaderResources;
713 cmd.args.bindShaderResources.srb = srbD;
714 // dynamic offsets have to be applied at the time of executing the bind
715 // operations, not here
716 cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbRebuilt && !srbUpdate && hasDynamicOffsetInSrb;
717 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
718 if (hasDynamicOffsetInSrb) {
719 if (dynamicOffsetCount < QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS) {
720 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
721 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
722 for (int i = 0; i < dynamicOffsetCount; ++i) {
723 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
724 const uint binding = uint(dynOfs.first);
725 Q_ASSERT(aligned(dynOfs.second, quint32(256)) == dynOfs.second);
726 const uint offsetInConstants = dynOfs.second / 16;
727 *p++ = binding;
728 *p++ = offsetInConstants;
729 }
730 } else {
731 qWarning("Too many dynamic offsets (%d, max is %d)",
732 dynamicOffsetCount, QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS);
733 }
734 }
735
736 cbD->commands.append(cmd);
737 }
738 }
739
setVertexInput(QRhiCommandBuffer * cb,int startBinding,int bindingCount,const QRhiCommandBuffer::VertexInput * bindings,QRhiBuffer * indexBuf,quint32 indexOffset,QRhiCommandBuffer::IndexFormat indexFormat)740 void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb,
741 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
742 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
743 {
744 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
745 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
746
747 bool needsBindVBuf = false;
748 for (int i = 0; i < bindingCount; ++i) {
749 const int inputSlot = startBinding + i;
750 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
751 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
752 if (bufD->m_type == QRhiBuffer::Dynamic)
753 executeBufferHostWrites(bufD);
754
755 if (cbD->currentVertexBuffers[inputSlot] != bufD->buffer
756 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
757 {
758 needsBindVBuf = true;
759 cbD->currentVertexBuffers[inputSlot] = bufD->buffer;
760 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
761 }
762 }
763
764 if (needsBindVBuf) {
765 QD3D11CommandBuffer::Command cmd;
766 cmd.cmd = QD3D11CommandBuffer::Command::BindVertexBuffers;
767 cmd.args.bindVertexBuffers.startSlot = startBinding;
768 cmd.args.bindVertexBuffers.slotCount = bindingCount;
769 QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
770 const QRhiVertexInputLayout &inputLayout(psD->m_vertexInputLayout);
771 const int inputBindingCount = inputLayout.cendBindings() - inputLayout.cbeginBindings();
772 for (int i = 0, ie = qMin(bindingCount, inputBindingCount); i != ie; ++i) {
773 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
774 cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer;
775 cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second;
776 cmd.args.bindVertexBuffers.strides[i] = inputLayout.bindingAt(i)->stride();
777 }
778 cbD->commands.append(cmd);
779 }
780
781 if (indexBuf) {
782 QD3D11Buffer *ibufD = QRHI_RES(QD3D11Buffer, indexBuf);
783 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
784 if (ibufD->m_type == QRhiBuffer::Dynamic)
785 executeBufferHostWrites(ibufD);
786
787 const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
788 : DXGI_FORMAT_R32_UINT;
789 if (cbD->currentIndexBuffer != ibufD->buffer
790 || cbD->currentIndexOffset != indexOffset
791 || cbD->currentIndexFormat != dxgiFormat)
792 {
793 cbD->currentIndexBuffer = ibufD->buffer;
794 cbD->currentIndexOffset = indexOffset;
795 cbD->currentIndexFormat = dxgiFormat;
796
797 QD3D11CommandBuffer::Command cmd;
798 cmd.cmd = QD3D11CommandBuffer::Command::BindIndexBuffer;
799 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
800 cmd.args.bindIndexBuffer.offset = indexOffset;
801 cmd.args.bindIndexBuffer.format = dxgiFormat;
802 cbD->commands.append(cmd);
803 }
804 }
805 }
806
setViewport(QRhiCommandBuffer * cb,const QRhiViewport & viewport)807 void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
808 {
809 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
810 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
811 Q_ASSERT(cbD->currentTarget);
812 const QSize outputSize = cbD->currentTarget->pixelSize();
813
814 QD3D11CommandBuffer::Command cmd;
815 cmd.cmd = QD3D11CommandBuffer::Command::Viewport;
816
817 // d3d expects top-left, QRhiViewport is bottom-left
818 float x, y, w, h;
819 if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
820 return;
821
822 cmd.args.viewport.x = x;
823 cmd.args.viewport.y = y;
824 cmd.args.viewport.w = w;
825 cmd.args.viewport.h = h;
826 cmd.args.viewport.d0 = viewport.minDepth();
827 cmd.args.viewport.d1 = viewport.maxDepth();
828 cbD->commands.append(cmd);
829 }
830
setScissor(QRhiCommandBuffer * cb,const QRhiScissor & scissor)831 void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
832 {
833 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
834 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
835 Q_ASSERT(cbD->currentTarget);
836 const QSize outputSize = cbD->currentTarget->pixelSize();
837
838 QD3D11CommandBuffer::Command cmd;
839 cmd.cmd = QD3D11CommandBuffer::Command::Scissor;
840
841 // d3d expects top-left, QRhiScissor is bottom-left
842 int x, y, w, h;
843 if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
844 return;
845
846 cmd.args.scissor.x = x;
847 cmd.args.scissor.y = y;
848 cmd.args.scissor.w = w;
849 cmd.args.scissor.h = h;
850 cbD->commands.append(cmd);
851 }
852
setBlendConstants(QRhiCommandBuffer * cb,const QColor & c)853 void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
854 {
855 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
856 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
857
858 QD3D11CommandBuffer::Command cmd;
859 cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants;
860 cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
861 cmd.args.blendConstants.c[0] = float(c.redF());
862 cmd.args.blendConstants.c[1] = float(c.greenF());
863 cmd.args.blendConstants.c[2] = float(c.blueF());
864 cmd.args.blendConstants.c[3] = float(c.alphaF());
865 cbD->commands.append(cmd);
866 }
867
setStencilRef(QRhiCommandBuffer * cb,quint32 refValue)868 void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
869 {
870 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
871 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
872
873 QD3D11CommandBuffer::Command cmd;
874 cmd.cmd = QD3D11CommandBuffer::Command::StencilRef;
875 cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
876 cmd.args.stencilRef.ref = refValue;
877 cbD->commands.append(cmd);
878 }
879
draw(QRhiCommandBuffer * cb,quint32 vertexCount,quint32 instanceCount,quint32 firstVertex,quint32 firstInstance)880 void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
881 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
882 {
883 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
884 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
885
886 QD3D11CommandBuffer::Command cmd;
887 cmd.cmd = QD3D11CommandBuffer::Command::Draw;
888 cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
889 cmd.args.draw.vertexCount = vertexCount;
890 cmd.args.draw.instanceCount = instanceCount;
891 cmd.args.draw.firstVertex = firstVertex;
892 cmd.args.draw.firstInstance = firstInstance;
893 cbD->commands.append(cmd);
894 }
895
drawIndexed(QRhiCommandBuffer * cb,quint32 indexCount,quint32 instanceCount,quint32 firstIndex,qint32 vertexOffset,quint32 firstInstance)896 void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
897 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
898 {
899 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
900 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
901
902 QD3D11CommandBuffer::Command cmd;
903 cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed;
904 cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
905 cmd.args.drawIndexed.indexCount = indexCount;
906 cmd.args.drawIndexed.instanceCount = instanceCount;
907 cmd.args.drawIndexed.firstIndex = firstIndex;
908 cmd.args.drawIndexed.vertexOffset = vertexOffset;
909 cmd.args.drawIndexed.firstInstance = firstInstance;
910 cbD->commands.append(cmd);
911 }
912
debugMarkBegin(QRhiCommandBuffer * cb,const QByteArray & name)913 void QRhiD3D11::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
914 {
915 if (!debugMarkers || !annotations)
916 return;
917
918 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
919 QD3D11CommandBuffer::Command cmd;
920 cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkBegin;
921 strncpy(cmd.args.debugMark.s, name.constData(), sizeof(cmd.args.debugMark.s));
922 cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
923 cbD->commands.append(cmd);
924 }
925
debugMarkEnd(QRhiCommandBuffer * cb)926 void QRhiD3D11::debugMarkEnd(QRhiCommandBuffer *cb)
927 {
928 if (!debugMarkers || !annotations)
929 return;
930
931 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
932 QD3D11CommandBuffer::Command cmd;
933 cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkEnd;
934 cbD->commands.append(cmd);
935 }
936
debugMarkMsg(QRhiCommandBuffer * cb,const QByteArray & msg)937 void QRhiD3D11::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
938 {
939 if (!debugMarkers || !annotations)
940 return;
941
942 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
943 QD3D11CommandBuffer::Command cmd;
944 cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkMsg;
945 strncpy(cmd.args.debugMark.s, msg.constData(), sizeof(cmd.args.debugMark.s));
946 cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
947 cbD->commands.append(cmd);
948 }
949
nativeHandles(QRhiCommandBuffer * cb)950 const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
951 {
952 Q_UNUSED(cb);
953 return nullptr;
954 }
955
beginExternal(QRhiCommandBuffer * cb)956 void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
957 {
958 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
959 // no timestampSwapChain, in order to avoid timestamp mess
960 executeCommandBuffer(cbD);
961 cbD->resetCommands();
962 }
963
endExternal(QRhiCommandBuffer * cb)964 void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
965 {
966 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
967 Q_ASSERT(cbD->commands.isEmpty());
968 cbD->resetCachedState();
969 if (cbD->currentTarget) { // could be compute, no rendertarget then
970 QD3D11CommandBuffer::Command fbCmd;
971 fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget;
972 fbCmd.args.setRenderTarget.rt = cbD->currentTarget;
973 cbD->commands.append(fbCmd);
974 }
975 }
976
beginFrame(QRhiSwapChain * swapChain,QRhi::BeginFrameFlags flags)977 QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
978 {
979 Q_UNUSED(flags);
980
981 QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
982 contextState.currentSwapChain = swapChainD;
983 const int currentFrameSlot = swapChainD->currentFrameSlot;
984 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
985
986 if (swapChainD->timestampActive[currentFrameSlot]) {
987 ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
988 const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
989 ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
990 ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
991 quint64 timestamps[2];
992 D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
993 bool ok = true;
994 ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
995 ok &= context->GetData(tsEnd, ×tamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
996 // this above is often not ready, not even in frame_where_recorded+2,
997 // not clear why. so make the whole thing async and do not touch the
998 // queries until they are finally all available in frame this+2 or
999 // this+4 or ...
1000 ok &= context->GetData(tsStart, ×tamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
1001 if (ok) {
1002 if (!dj.Disjoint && dj.Frequency) {
1003 const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
1004 // finally got a value, just report it, the profiler cares about min/max/avg anyway
1005 QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
1006 }
1007 swapChainD->timestampActive[currentFrameSlot] = false;
1008 } // else leave timestampActive set to true, will retry in a subsequent beginFrame
1009 }
1010
1011 swapChainD->cb.resetState();
1012
1013 swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
1014 swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
1015 swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
1016
1017 QRHI_PROF_F(beginSwapChainFrame(swapChain));
1018
1019 finishActiveReadbacks();
1020
1021 return QRhi::FrameOpSuccess;
1022 }
1023
endFrame(QRhiSwapChain * swapChain,QRhi::EndFrameFlags flags)1024 QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1025 {
1026 QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
1027 Q_ASSERT(contextState.currentSwapChain = swapChainD);
1028 const int currentFrameSlot = swapChainD->currentFrameSlot;
1029
1030 ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
1031 const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
1032 ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
1033 ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
1034 const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestampActive[currentFrameSlot];
1035
1036 // send all commands to the context
1037 if (recordTimestamps)
1038 executeCommandBuffer(&swapChainD->cb, swapChainD);
1039 else
1040 executeCommandBuffer(&swapChainD->cb);
1041
1042 if (swapChainD->sampleDesc.Count > 1) {
1043 context->ResolveSubresource(swapChainD->backBufferTex, 0,
1044 swapChainD->msaaTex[currentFrameSlot], 0,
1045 swapChainD->colorFormat);
1046 }
1047
1048 // this is here because we want to include the time spent on the resolve as well
1049 if (recordTimestamps) {
1050 context->End(tsEnd);
1051 context->End(tsDisjoint);
1052 swapChainD->timestampActive[currentFrameSlot] = true;
1053 }
1054
1055 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1056 // this must be done before the Present
1057 QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
1058
1059 if (!flags.testFlag(QRhi::SkipPresent)) {
1060 const UINT presentFlags = 0;
1061 HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
1062 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
1063 qWarning("Device loss detected in Present()");
1064 deviceLost = true;
1065 return QRhi::FrameOpDeviceLost;
1066 } else if (FAILED(hr)) {
1067 qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
1068 return QRhi::FrameOpError;
1069 }
1070
1071 // move on to the next buffer
1072 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
1073 } else {
1074 context->Flush();
1075 }
1076
1077 swapChainD->frameCount += 1;
1078 contextState.currentSwapChain = nullptr;
1079
1080 if (deviceCurse.framesToActivate > 0) {
1081 deviceCurse.framesLeft -= 1;
1082 if (deviceCurse.framesLeft == 0) {
1083 deviceCurse.framesLeft = deviceCurse.framesToActivate;
1084 if (!deviceCurse.permanent)
1085 deviceCurse.framesToActivate = -1;
1086
1087 deviceCurse.activate();
1088 } else if (deviceCurse.framesLeft % 100 == 0) {
1089 qDebug("Impending doom: %d frames left", deviceCurse.framesLeft);
1090 }
1091 }
1092
1093 return QRhi::FrameOpSuccess;
1094 }
1095
beginOffscreenFrame(QRhiCommandBuffer ** cb,QRhi::BeginFrameFlags flags)1096 QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
1097 {
1098 Q_UNUSED(flags);
1099 ofr.active = true;
1100
1101 ofr.cbWrapper.resetState();
1102 *cb = &ofr.cbWrapper;
1103
1104 return QRhi::FrameOpSuccess;
1105 }
1106
endOffscreenFrame(QRhi::EndFrameFlags flags)1107 QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
1108 {
1109 Q_UNUSED(flags);
1110 ofr.active = false;
1111
1112 executeCommandBuffer(&ofr.cbWrapper);
1113
1114 finishActiveReadbacks();
1115
1116 return QRhi::FrameOpSuccess;
1117 }
1118
toD3DTextureFormat(QRhiTexture::Format format,QRhiTexture::Flags flags)1119 static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
1120 {
1121 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1122 switch (format) {
1123 case QRhiTexture::RGBA8:
1124 return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
1125 case QRhiTexture::BGRA8:
1126 return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
1127 case QRhiTexture::R8:
1128 return DXGI_FORMAT_R8_UNORM;
1129 case QRhiTexture::R16:
1130 return DXGI_FORMAT_R16_UNORM;
1131 case QRhiTexture::RED_OR_ALPHA8:
1132 return DXGI_FORMAT_R8_UNORM;
1133
1134 case QRhiTexture::RGBA16F:
1135 return DXGI_FORMAT_R16G16B16A16_FLOAT;
1136 case QRhiTexture::RGBA32F:
1137 return DXGI_FORMAT_R32G32B32A32_FLOAT;
1138 case QRhiTexture::R16F:
1139 return DXGI_FORMAT_R16_FLOAT;
1140 case QRhiTexture::R32F:
1141 return DXGI_FORMAT_R32_FLOAT;
1142
1143 case QRhiTexture::D16:
1144 return DXGI_FORMAT_R16_TYPELESS;
1145 case QRhiTexture::D32F:
1146 return DXGI_FORMAT_R32_TYPELESS;
1147
1148 case QRhiTexture::BC1:
1149 return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
1150 case QRhiTexture::BC2:
1151 return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
1152 case QRhiTexture::BC3:
1153 return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
1154 case QRhiTexture::BC4:
1155 return DXGI_FORMAT_BC4_UNORM;
1156 case QRhiTexture::BC5:
1157 return DXGI_FORMAT_BC5_UNORM;
1158 case QRhiTexture::BC6H:
1159 return DXGI_FORMAT_BC6H_UF16;
1160 case QRhiTexture::BC7:
1161 return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
1162
1163 case QRhiTexture::ETC2_RGB8:
1164 case QRhiTexture::ETC2_RGB8A1:
1165 case QRhiTexture::ETC2_RGBA8:
1166 qWarning("QRhiD3D11 does not support ETC2 textures");
1167 return DXGI_FORMAT_R8G8B8A8_UNORM;
1168
1169 case QRhiTexture::ASTC_4x4:
1170 case QRhiTexture::ASTC_5x4:
1171 case QRhiTexture::ASTC_5x5:
1172 case QRhiTexture::ASTC_6x5:
1173 case QRhiTexture::ASTC_6x6:
1174 case QRhiTexture::ASTC_8x5:
1175 case QRhiTexture::ASTC_8x6:
1176 case QRhiTexture::ASTC_8x8:
1177 case QRhiTexture::ASTC_10x5:
1178 case QRhiTexture::ASTC_10x6:
1179 case QRhiTexture::ASTC_10x8:
1180 case QRhiTexture::ASTC_10x10:
1181 case QRhiTexture::ASTC_12x10:
1182 case QRhiTexture::ASTC_12x12:
1183 qWarning("QRhiD3D11 does not support ASTC textures");
1184 return DXGI_FORMAT_R8G8B8A8_UNORM;
1185
1186 default:
1187 Q_UNREACHABLE();
1188 return DXGI_FORMAT_R8G8B8A8_UNORM;
1189 }
1190 }
1191
colorTextureFormatFromDxgiFormat(DXGI_FORMAT format,QRhiTexture::Flags * flags)1192 static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
1193 {
1194 switch (format) {
1195 case DXGI_FORMAT_R8G8B8A8_UNORM:
1196 return QRhiTexture::RGBA8;
1197 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
1198 if (flags)
1199 (*flags) |= QRhiTexture::sRGB;
1200 return QRhiTexture::RGBA8;
1201 case DXGI_FORMAT_B8G8R8A8_UNORM:
1202 return QRhiTexture::BGRA8;
1203 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
1204 if (flags)
1205 (*flags) |= QRhiTexture::sRGB;
1206 return QRhiTexture::BGRA8;
1207 case DXGI_FORMAT_R8_UNORM:
1208 return QRhiTexture::R8;
1209 case DXGI_FORMAT_R16_UNORM:
1210 return QRhiTexture::R16;
1211 default: // this cannot assert, must warn and return unknown
1212 qWarning("DXGI_FORMAT %d is not a recognized uncompressed color format", format);
1213 break;
1214 }
1215 return QRhiTexture::UnknownFormat;
1216 }
1217
isDepthTextureFormat(QRhiTexture::Format format)1218 static inline bool isDepthTextureFormat(QRhiTexture::Format format)
1219 {
1220 switch (format) {
1221 case QRhiTexture::Format::D16:
1222 case QRhiTexture::Format::D32F:
1223 return true;
1224
1225 default:
1226 return false;
1227 }
1228 }
1229
finish()1230 QRhi::FrameOpResult QRhiD3D11::finish()
1231 {
1232 if (inFrame) {
1233 if (ofr.active) {
1234 Q_ASSERT(!contextState.currentSwapChain);
1235 Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass);
1236 executeCommandBuffer(&ofr.cbWrapper);
1237 ofr.cbWrapper.resetCommands();
1238 } else {
1239 Q_ASSERT(contextState.currentSwapChain);
1240 Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
1241 executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
1242 contextState.currentSwapChain->cb.resetCommands();
1243 }
1244 }
1245
1246 finishActiveReadbacks();
1247
1248 return QRhi::FrameOpSuccess;
1249 }
1250
enqueueSubresUpload(QD3D11Texture * texD,QD3D11CommandBuffer * cbD,int layer,int level,const QRhiTextureSubresourceUploadDescription & subresDesc)1251 void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
1252 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
1253 {
1254 UINT subres = D3D11CalcSubresource(UINT(level), UINT(layer), texD->mipLevelCount);
1255 const QPoint dp = subresDesc.destinationTopLeft();
1256 D3D11_BOX box;
1257 box.front = 0;
1258 // back, right, bottom are exclusive
1259 box.back = 1;
1260 QD3D11CommandBuffer::Command cmd;
1261 cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
1262 cmd.args.updateSubRes.dst = texD->tex;
1263 cmd.args.updateSubRes.dstSubRes = subres;
1264
1265 bool cmdValid = true;
1266 if (!subresDesc.image().isNull()) {
1267 QImage img = subresDesc.image();
1268 QSize size = img.size();
1269 int bpl = img.bytesPerLine();
1270 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
1271 const QPoint sp = subresDesc.sourceTopLeft();
1272 if (!subresDesc.sourceSize().isEmpty())
1273 size = subresDesc.sourceSize();
1274 if (img.depth() == 32) {
1275 const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4;
1276 cmd.args.updateSubRes.src = cbD->retainImage(img) + offset;
1277 } else {
1278 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
1279 bpl = img.bytesPerLine();
1280 cmd.args.updateSubRes.src = cbD->retainImage(img);
1281 }
1282 } else {
1283 cmd.args.updateSubRes.src = cbD->retainImage(img);
1284 }
1285 box.left = UINT(dp.x());
1286 box.top = UINT(dp.y());
1287 box.right = UINT(dp.x() + size.width());
1288 box.bottom = UINT(dp.y() + size.height());
1289 cmd.args.updateSubRes.hasDstBox = true;
1290 cmd.args.updateSubRes.dstBox = box;
1291 cmd.args.updateSubRes.srcRowPitch = UINT(bpl);
1292 } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
1293 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1294 : subresDesc.sourceSize();
1295 quint32 bpl = 0;
1296 QSize blockDim;
1297 compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim);
1298 // Everything must be a multiple of the block width and
1299 // height, so e.g. a mip level of size 2x2 will be 4x4 when it
1300 // comes to the actual data.
1301 box.left = UINT(aligned(dp.x(), blockDim.width()));
1302 box.top = UINT(aligned(dp.y(), blockDim.height()));
1303 box.right = UINT(aligned(dp.x() + size.width(), blockDim.width()));
1304 box.bottom = UINT(aligned(dp.y() + size.height(), blockDim.height()));
1305 cmd.args.updateSubRes.hasDstBox = true;
1306 cmd.args.updateSubRes.dstBox = box;
1307 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1308 cmd.args.updateSubRes.srcRowPitch = bpl;
1309 } else if (!subresDesc.data().isEmpty()) {
1310 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1311 : subresDesc.sourceSize();
1312 quint32 bpl = 0;
1313 textureFormatInfo(texD->m_format, size, &bpl, nullptr);
1314 box.left = UINT(dp.x());
1315 box.top = UINT(dp.y());
1316 box.right = UINT(dp.x() + size.width());
1317 box.bottom = UINT(dp.y() + size.height());
1318 cmd.args.updateSubRes.hasDstBox = true;
1319 cmd.args.updateSubRes.dstBox = box;
1320 cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
1321 cmd.args.updateSubRes.srcRowPitch = bpl;
1322 } else {
1323 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1324 cmdValid = false;
1325 }
1326 if (cmdValid)
1327 cbD->commands.append(cmd);
1328 }
1329
enqueueResourceUpdates(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1330 void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1331 {
1332 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1333 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
1334 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1335
1336 for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
1337 if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
1338 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1339 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1340 memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), size_t(u.data.size()));
1341 bufD->hasPendingDynamicUpdates = true;
1342 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
1343 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1344 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1345 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
1346 QD3D11CommandBuffer::Command cmd;
1347 cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
1348 cmd.args.updateSubRes.dst = bufD->buffer;
1349 cmd.args.updateSubRes.dstSubRes = 0;
1350 cmd.args.updateSubRes.src = cbD->retainData(u.data);
1351 cmd.args.updateSubRes.srcRowPitch = 0;
1352 // Specify the region (even when offset is 0 and all data is provided)
1353 // since the ID3D11Buffer's size is rounded up to be a multiple of 256
1354 // while the data we have has the original size.
1355 D3D11_BOX box;
1356 box.left = UINT(u.offset);
1357 box.top = box.front = 0;
1358 box.back = box.bottom = 1;
1359 box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
1360 cmd.args.updateSubRes.hasDstBox = true;
1361 cmd.args.updateSubRes.dstBox = box;
1362 cbD->commands.append(cmd);
1363 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
1364 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
1365 if (bufD->m_type == QRhiBuffer::Dynamic) {
1366 u.result->data.resize(u.readSize);
1367 memcpy(u.result->data.data(), bufD->dynBuf.constData() + u.offset, size_t(u.readSize));
1368 } else {
1369 BufferReadback readback;
1370 readback.result = u.result;
1371 readback.byteSize = u.readSize;
1372
1373 D3D11_BUFFER_DESC desc;
1374 memset(&desc, 0, sizeof(desc));
1375 desc.ByteWidth = readback.byteSize;
1376 desc.Usage = D3D11_USAGE_STAGING;
1377 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1378 HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf);
1379 if (FAILED(hr)) {
1380 qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
1381 continue;
1382 }
1383 QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.stagingBuf)), bufD, readback.byteSize));
1384
1385 QD3D11CommandBuffer::Command cmd;
1386 cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
1387 cmd.args.copySubRes.dst = readback.stagingBuf;
1388 cmd.args.copySubRes.dstSubRes = 0;
1389 cmd.args.copySubRes.dstX = 0;
1390 cmd.args.copySubRes.dstY = 0;
1391 cmd.args.copySubRes.src = bufD->buffer;
1392 cmd.args.copySubRes.srcSubRes = 0;
1393 cmd.args.copySubRes.hasSrcBox = true;
1394 D3D11_BOX box;
1395 box.left = UINT(u.offset);
1396 box.top = box.front = 0;
1397 box.back = box.bottom = 1;
1398 box.right = UINT(u.offset + u.readSize);
1399 cmd.args.copySubRes.srcBox = box;
1400 cbD->commands.append(cmd);
1401
1402 activeBufferReadbacks.append(readback);
1403 }
1404 if (u.result->completed)
1405 u.result->completed();
1406 }
1407 }
1408
1409 for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
1410 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
1411 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
1412 for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
1413 for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
1414 for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
1415 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
1416 }
1417 }
1418 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
1419 Q_ASSERT(u.src && u.dst);
1420 QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
1421 QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
1422 UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
1423 UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
1424 const QPoint dp = u.desc.destinationTopLeft();
1425 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
1426 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
1427 const QPoint sp = u.desc.sourceTopLeft();
1428 D3D11_BOX srcBox;
1429 srcBox.left = UINT(sp.x());
1430 srcBox.top = UINT(sp.y());
1431 srcBox.front = 0;
1432 // back, right, bottom are exclusive
1433 srcBox.right = srcBox.left + UINT(copySize.width());
1434 srcBox.bottom = srcBox.top + UINT(copySize.height());
1435 srcBox.back = 1;
1436 QD3D11CommandBuffer::Command cmd;
1437 cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
1438 cmd.args.copySubRes.dst = dstD->tex;
1439 cmd.args.copySubRes.dstSubRes = dstSubRes;
1440 cmd.args.copySubRes.dstX = UINT(dp.x());
1441 cmd.args.copySubRes.dstY = UINT(dp.y());
1442 cmd.args.copySubRes.src = srcD->tex;
1443 cmd.args.copySubRes.srcSubRes = srcSubRes;
1444 cmd.args.copySubRes.hasSrcBox = true;
1445 cmd.args.copySubRes.srcBox = srcBox;
1446 cbD->commands.append(cmd);
1447 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
1448 TextureReadback readback;
1449 readback.desc = u.rb;
1450 readback.result = u.result;
1451
1452 ID3D11Resource *src;
1453 DXGI_FORMAT dxgiFormat;
1454 QSize pixelSize;
1455 QRhiTexture::Format format;
1456 UINT subres = 0;
1457 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.rb.texture());
1458 QD3D11SwapChain *swapChainD = nullptr;
1459
1460 if (texD) {
1461 if (texD->sampleDesc.Count > 1) {
1462 qWarning("Multisample texture cannot be read back");
1463 continue;
1464 }
1465 src = texD->tex;
1466 dxgiFormat = texD->dxgiFormat;
1467 pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
1468 format = texD->m_format;
1469 subres = D3D11CalcSubresource(UINT(u.rb.level()), UINT(u.rb.layer()), texD->mipLevelCount);
1470 } else {
1471 Q_ASSERT(contextState.currentSwapChain);
1472 swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
1473 if (swapChainD->sampleDesc.Count > 1) {
1474 // Unlike with textures, reading back a multisample swapchain image
1475 // has to be supported. Insert a resolve.
1476 QD3D11CommandBuffer::Command rcmd;
1477 rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
1478 rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
1479 rcmd.args.resolveSubRes.dstSubRes = 0;
1480 rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
1481 rcmd.args.resolveSubRes.srcSubRes = 0;
1482 rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
1483 cbD->commands.append(rcmd);
1484 }
1485 src = swapChainD->backBufferTex;
1486 dxgiFormat = swapChainD->colorFormat;
1487 pixelSize = swapChainD->pixelSize;
1488 format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
1489 if (format == QRhiTexture::UnknownFormat)
1490 continue;
1491 }
1492 quint32 byteSize = 0;
1493 quint32 bpl = 0;
1494 textureFormatInfo(format, pixelSize, &bpl, &byteSize);
1495
1496 D3D11_TEXTURE2D_DESC desc;
1497 memset(&desc, 0, sizeof(desc));
1498 desc.Width = UINT(pixelSize.width());
1499 desc.Height = UINT(pixelSize.height());
1500 desc.MipLevels = 1;
1501 desc.ArraySize = 1;
1502 desc.Format = dxgiFormat;
1503 desc.SampleDesc.Count = 1;
1504 desc.Usage = D3D11_USAGE_STAGING;
1505 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1506 ID3D11Texture2D *stagingTex;
1507 HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
1508 if (FAILED(hr)) {
1509 qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr)));
1510 return;
1511 }
1512 QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)),
1513 texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
1514 byteSize));
1515
1516 QD3D11CommandBuffer::Command cmd;
1517 cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
1518 cmd.args.copySubRes.dst = stagingTex;
1519 cmd.args.copySubRes.dstSubRes = 0;
1520 cmd.args.copySubRes.dstX = 0;
1521 cmd.args.copySubRes.dstY = 0;
1522 cmd.args.copySubRes.src = src;
1523 cmd.args.copySubRes.srcSubRes = subres;
1524 cmd.args.copySubRes.hasSrcBox = false;
1525 cbD->commands.append(cmd);
1526
1527 readback.stagingTex = stagingTex;
1528 readback.byteSize = byteSize;
1529 readback.bpl = bpl;
1530 readback.pixelSize = pixelSize;
1531 readback.format = format;
1532
1533 activeTextureReadbacks.append(readback);
1534 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
1535 Q_ASSERT(u.dst->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
1536 QD3D11CommandBuffer::Command cmd;
1537 cmd.cmd = QD3D11CommandBuffer::Command::GenMip;
1538 cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.dst)->srv;
1539 cbD->commands.append(cmd);
1540 }
1541 }
1542
1543 ud->free();
1544 }
1545
finishActiveReadbacks()1546 void QRhiD3D11::finishActiveReadbacks()
1547 {
1548 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
1549 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1550
1551 for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
1552 const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]);
1553 readback.result->format = readback.format;
1554 readback.result->pixelSize = readback.pixelSize;
1555
1556 D3D11_MAPPED_SUBRESOURCE mp;
1557 HRESULT hr = context->Map(readback.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
1558 if (SUCCEEDED(hr)) {
1559 readback.result->data.resize(int(readback.byteSize));
1560 // nothing says the rows are tightly packed in the texture, must take
1561 // the stride into account
1562 char *dst = readback.result->data.data();
1563 char *src = static_cast<char *>(mp.pData);
1564 for (int y = 0, h = readback.pixelSize.height(); y != h; ++y) {
1565 memcpy(dst, src, readback.bpl);
1566 dst += readback.bpl;
1567 src += mp.RowPitch;
1568 }
1569 context->Unmap(readback.stagingTex, 0);
1570 } else {
1571 qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
1572 }
1573
1574 readback.stagingTex->Release();
1575 QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingTex))));
1576
1577 if (readback.result->completed)
1578 completedCallbacks.append(readback.result->completed);
1579
1580 activeTextureReadbacks.removeAt(i);
1581 }
1582
1583 for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) {
1584 const QRhiD3D11::BufferReadback &readback(activeBufferReadbacks[i]);
1585
1586 D3D11_MAPPED_SUBRESOURCE mp;
1587 HRESULT hr = context->Map(readback.stagingBuf, 0, D3D11_MAP_READ, 0, &mp);
1588 if (SUCCEEDED(hr)) {
1589 readback.result->data.resize(int(readback.byteSize));
1590 memcpy(readback.result->data.data(), mp.pData, readback.byteSize);
1591 context->Unmap(readback.stagingBuf, 0);
1592 } else {
1593 qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
1594 }
1595
1596 readback.stagingBuf->Release();
1597 QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingBuf))));
1598
1599 if (readback.result->completed)
1600 completedCallbacks.append(readback.result->completed);
1601
1602 activeBufferReadbacks.removeAt(i);
1603 }
1604
1605 for (auto f : completedCallbacks)
1606 f();
1607 }
1608
rtData(QRhiRenderTarget * rt)1609 static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
1610 {
1611 switch (rt->resourceType()) {
1612 case QRhiResource::RenderTarget:
1613 return &QRHI_RES(QD3D11ReferenceRenderTarget, rt)->d;
1614 case QRhiResource::TextureRenderTarget:
1615 return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d;
1616 default:
1617 Q_UNREACHABLE();
1618 return nullptr;
1619 }
1620 }
1621
resourceUpdate(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1622 void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1623 {
1624 Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass);
1625
1626 enqueueResourceUpdates(cb, resourceUpdates);
1627 }
1628
beginPass(QRhiCommandBuffer * cb,QRhiRenderTarget * rt,const QColor & colorClearValue,const QRhiDepthStencilClearValue & depthStencilClearValue,QRhiResourceUpdateBatch * resourceUpdates)1629 void QRhiD3D11::beginPass(QRhiCommandBuffer *cb,
1630 QRhiRenderTarget *rt,
1631 const QColor &colorClearValue,
1632 const QRhiDepthStencilClearValue &depthStencilClearValue,
1633 QRhiResourceUpdateBatch *resourceUpdates)
1634 {
1635 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1636 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass);
1637
1638 if (resourceUpdates)
1639 enqueueResourceUpdates(cb, resourceUpdates);
1640
1641 bool wantsColorClear = true;
1642 bool wantsDsClear = true;
1643 QD3D11RenderTargetData *rtD = rtData(rt);
1644 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
1645 QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, rt);
1646 wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
1647 wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
1648 }
1649
1650 QD3D11CommandBuffer::Command fbCmd;
1651 fbCmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources;
1652 cbD->commands.append(fbCmd);
1653 fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget;
1654 fbCmd.args.setRenderTarget.rt = rt;
1655 cbD->commands.append(fbCmd);
1656
1657 QD3D11CommandBuffer::Command clearCmd;
1658 clearCmd.cmd = QD3D11CommandBuffer::Command::Clear;
1659 clearCmd.args.clear.rt = rt;
1660 clearCmd.args.clear.mask = 0;
1661 if (rtD->colorAttCount && wantsColorClear)
1662 clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color;
1663 if (rtD->dsAttCount && wantsDsClear)
1664 clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Depth | QD3D11CommandBuffer::Command::Stencil;
1665
1666 clearCmd.args.clear.c[0] = float(colorClearValue.redF());
1667 clearCmd.args.clear.c[1] = float(colorClearValue.greenF());
1668 clearCmd.args.clear.c[2] = float(colorClearValue.blueF());
1669 clearCmd.args.clear.c[3] = float(colorClearValue.alphaF());
1670 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
1671 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
1672 cbD->commands.append(clearCmd);
1673
1674 cbD->recordingPass = QD3D11CommandBuffer::RenderPass;
1675 cbD->currentTarget = rt;
1676
1677 cbD->resetCachedShaderResourceState();
1678 }
1679
endPass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1680 void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1681 {
1682 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1683 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
1684
1685 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1686 QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
1687 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
1688 it != itEnd; ++it)
1689 {
1690 const QRhiColorAttachment &colorAtt(*it);
1691 if (!colorAtt.resolveTexture())
1692 continue;
1693
1694 QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture());
1695 QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture());
1696 QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer());
1697 Q_ASSERT(srcTexD || srcRbD);
1698 QD3D11CommandBuffer::Command cmd;
1699 cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
1700 cmd.args.resolveSubRes.dst = dstTexD->tex;
1701 cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()),
1702 UINT(colorAtt.resolveLayer()),
1703 dstTexD->mipLevelCount);
1704 if (srcTexD) {
1705 cmd.args.resolveSubRes.src = srcTexD->tex;
1706 if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
1707 qWarning("Resolve source and destination formats do not match");
1708 continue;
1709 }
1710 if (srcTexD->sampleDesc.Count <= 1) {
1711 qWarning("Cannot resolve a non-multisample texture");
1712 continue;
1713 }
1714 if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
1715 qWarning("Resolve source and destination sizes do not match");
1716 continue;
1717 }
1718 } else {
1719 cmd.args.resolveSubRes.src = srcRbD->tex;
1720 if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
1721 qWarning("Resolve source and destination formats do not match");
1722 continue;
1723 }
1724 if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
1725 qWarning("Resolve source and destination sizes do not match");
1726 continue;
1727 }
1728 }
1729 cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1);
1730 cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
1731 cbD->commands.append(cmd);
1732 }
1733 }
1734
1735 cbD->recordingPass = QD3D11CommandBuffer::NoPass;
1736 cbD->currentTarget = nullptr;
1737
1738 if (resourceUpdates)
1739 enqueueResourceUpdates(cb, resourceUpdates);
1740 }
1741
beginComputePass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1742 void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1743 {
1744 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1745 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass);
1746
1747 if (resourceUpdates)
1748 enqueueResourceUpdates(cb, resourceUpdates);
1749
1750 QD3D11CommandBuffer::Command cmd;
1751 cmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources;
1752 cbD->commands.append(cmd);
1753
1754 cbD->recordingPass = QD3D11CommandBuffer::ComputePass;
1755
1756 cbD->resetCachedShaderResourceState();
1757 }
1758
endComputePass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1759 void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1760 {
1761 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1762 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
1763
1764 cbD->recordingPass = QD3D11CommandBuffer::NoPass;
1765
1766 if (resourceUpdates)
1767 enqueueResourceUpdates(cb, resourceUpdates);
1768 }
1769
setComputePipeline(QRhiCommandBuffer * cb,QRhiComputePipeline * ps)1770 void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
1771 {
1772 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1773 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
1774 QD3D11ComputePipeline *psD = QRHI_RES(QD3D11ComputePipeline, ps);
1775 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
1776
1777 if (pipelineChanged) {
1778 cbD->currentGraphicsPipeline = nullptr;
1779 cbD->currentComputePipeline = psD;
1780 cbD->currentPipelineGeneration = psD->generation;
1781
1782 QD3D11CommandBuffer::Command cmd;
1783 cmd.cmd = QD3D11CommandBuffer::Command::BindComputePipeline;
1784 cmd.args.bindComputePipeline.ps = psD;
1785 cbD->commands.append(cmd);
1786 }
1787 }
1788
dispatch(QRhiCommandBuffer * cb,int x,int y,int z)1789 void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
1790 {
1791 QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
1792 Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
1793
1794 QD3D11CommandBuffer::Command cmd;
1795 cmd.cmd = QD3D11CommandBuffer::Command::Dispatch;
1796 cmd.args.dispatch.x = UINT(x);
1797 cmd.args.dispatch.y = UINT(y);
1798 cmd.args.dispatch.z = UINT(z);
1799 cbD->commands.append(cmd);
1800 }
1801
mapBinding(int binding,int stageIndex,const QShader::NativeResourceBindingMap * nativeResourceBindingMaps[])1802 static inline QPair<int, int> mapBinding(int binding,
1803 int stageIndex,
1804 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
1805 {
1806 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1807 if (!map || map->isEmpty())
1808 return { binding, binding }; // old QShader versions do not have this map, assume 1:1 mapping then
1809
1810 auto it = map->constFind(binding);
1811 if (it != map->cend())
1812 return *it;
1813
1814 // Hitting this path is normal too. It is not given that the resource is
1815 // present in the shaders for all the stages specified by the visibility
1816 // mask in the QRhiShaderResourceBinding.
1817 return { -1, -1 };
1818 }
1819
updateShaderResourceBindings(QD3D11ShaderResourceBindings * srbD,const QShader::NativeResourceBindingMap * nativeResourceBindingMaps[])1820 void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
1821 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
1822 {
1823 srbD->vsubufs.clear();
1824 srbD->vsubufoffsets.clear();
1825 srbD->vsubufsizes.clear();
1826
1827 srbD->fsubufs.clear();
1828 srbD->fsubufoffsets.clear();
1829 srbD->fsubufsizes.clear();
1830
1831 srbD->csubufs.clear();
1832 srbD->csubufoffsets.clear();
1833 srbD->csubufsizes.clear();
1834
1835 srbD->vssamplers.clear();
1836 srbD->vsshaderresources.clear();
1837
1838 srbD->fssamplers.clear();
1839 srbD->fsshaderresources.clear();
1840
1841 srbD->cssamplers.clear();
1842 srbD->csshaderresources.clear();
1843
1844 srbD->csUAVs.clear();
1845
1846 struct Stage {
1847 struct Buffer {
1848 int breg; // b0, b1, ...
1849 ID3D11Buffer *buffer;
1850 uint offsetInConstants;
1851 uint sizeInConstants;
1852 };
1853 struct Texture {
1854 int treg; // t0, t1, ...
1855 ID3D11ShaderResourceView *srv;
1856 };
1857 struct Sampler {
1858 int sreg; // s0, s1, ...
1859 ID3D11SamplerState *sampler;
1860 };
1861 struct Uav {
1862 int ureg;
1863 ID3D11UnorderedAccessView *uav;
1864 };
1865 QVarLengthArray<Buffer, 8> buffers;
1866 QVarLengthArray<Texture, 8> textures;
1867 QVarLengthArray<Sampler, 8> samplers;
1868 QVarLengthArray<Uav, 8> uavs;
1869 } res[RBM_SUPPORTED_STAGES];
1870
1871 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1872 const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
1873 QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
1874 switch (b->type) {
1875 case QRhiShaderResourceBinding::UniformBuffer:
1876 {
1877 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
1878 Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset);
1879 bd.ubuf.id = bufD->m_id;
1880 bd.ubuf.generation = bufD->generation;
1881 // dynamic ubuf offsets are not considered here, those are baked in
1882 // at a later stage, which is good as vsubufoffsets and friends are
1883 // per-srb, not per-setShaderResources call
1884 const uint offsetInConstants = uint(b->u.ubuf.offset) / 16;
1885 // size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
1886 // We can round up if needed since the buffers's actual size
1887 // (ByteWidth) is always a multiple of 256.
1888 const uint sizeInConstants = uint(aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256) / 16);
1889 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
1890 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
1891 if (nativeBinding.first >= 0)
1892 res[RBM_VERTEX].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
1893 }
1894 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
1895 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
1896 if (nativeBinding.first >= 0)
1897 res[RBM_FRAGMENT].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
1898 }
1899 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
1900 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
1901 if (nativeBinding.first >= 0)
1902 res[RBM_COMPUTE].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
1903 }
1904 }
1905 break;
1906 case QRhiShaderResourceBinding::SampledTexture:
1907 {
1908 const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
1909 bd.stex.count = data->count;
1910 const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
1911 const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
1912 const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
1913 // if SPIR-V binding b is mapped to tN and sN in HLSL, and it
1914 // is an array, then it will use tN, tN+1, tN+2, ..., and sN,
1915 // sN+1, sN+2, ...
1916 for (int elem = 0; elem < data->count; ++elem) {
1917 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex);
1918 QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler);
1919 bd.stex.d[elem].texId = texD->m_id;
1920 bd.stex.d[elem].texGeneration = texD->generation;
1921 bd.stex.d[elem].samplerId = samplerD->m_id;
1922 bd.stex.d[elem].samplerGeneration = samplerD->generation;
1923 if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
1924 if (nativeBindingVert.first >= 0 && nativeBindingVert.second >= 0) {
1925 res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv });
1926 res[RBM_VERTEX].samplers.append({ nativeBindingVert.second + elem, samplerD->samplerState });
1927 }
1928 }
1929 if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
1930 if (nativeBindingFrag.first >= 0 && nativeBindingFrag.second >= 0) {
1931 res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
1932 res[RBM_FRAGMENT].samplers.append({ nativeBindingFrag.second + elem, samplerD->samplerState });
1933 }
1934 }
1935 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
1936 if (nativeBindingComp.first >= 0 && nativeBindingComp.second >= 0) {
1937 res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
1938 res[RBM_COMPUTE].samplers.append({ nativeBindingComp.second + elem, samplerD->samplerState });
1939 }
1940 }
1941 }
1942 }
1943 break;
1944 case QRhiShaderResourceBinding::ImageLoad:
1945 case QRhiShaderResourceBinding::ImageStore:
1946 case QRhiShaderResourceBinding::ImageLoadStore:
1947 {
1948 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
1949 bd.simage.id = texD->m_id;
1950 bd.simage.generation = texD->generation;
1951 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
1952 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
1953 if (nativeBinding.first >= 0) {
1954 ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
1955 if (uav)
1956 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
1957 }
1958 } else {
1959 qWarning("Unordered access only supported at compute stage");
1960 }
1961 }
1962 break;
1963 case QRhiShaderResourceBinding::BufferLoad:
1964 case QRhiShaderResourceBinding::BufferStore:
1965 case QRhiShaderResourceBinding::BufferLoadStore:
1966 {
1967 QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
1968 bd.sbuf.id = bufD->m_id;
1969 bd.sbuf.generation = bufD->generation;
1970 if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
1971 QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
1972 if (nativeBinding.first >= 0) {
1973 ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView();
1974 if (uav)
1975 res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
1976 }
1977 } else {
1978 qWarning("Unordered access only supported at compute stage");
1979 }
1980 }
1981 break;
1982 default:
1983 Q_UNREACHABLE();
1984 break;
1985 }
1986 }
1987
1988 // QRhiBatchedBindings works with the native bindings and expects
1989 // sorted input. The pre-sorted QRhiShaderResourceBinding list (based
1990 // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
1991 // have to sort here every time.
1992 for (int stage = 0; stage < RBM_SUPPORTED_STAGES; ++stage) {
1993 std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) {
1994 return a.breg < b.breg;
1995 });
1996 std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) {
1997 return a.treg < b.treg;
1998 });
1999 std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) {
2000 return a.sreg < b.sreg;
2001 });
2002 std::sort(res[stage].uavs.begin(), res[stage].uavs.end(), [](const Stage::Uav &a, const Stage::Uav &b) {
2003 return a.ureg < b.ureg;
2004 });
2005 }
2006
2007 for (const Stage::Buffer &buf : qAsConst(res[RBM_VERTEX].buffers)) {
2008 srbD->vsubufs.feed(buf.breg, buf.buffer);
2009 srbD->vsubufoffsets.feed(buf.breg, buf.offsetInConstants);
2010 srbD->vsubufsizes.feed(buf.breg, buf.sizeInConstants);
2011 }
2012 srbD->vsubufs.finish();
2013 srbD->vsubufoffsets.finish();
2014 srbD->vsubufsizes.finish();
2015
2016 for (const Stage::Buffer &buf : qAsConst(res[RBM_FRAGMENT].buffers)) {
2017 srbD->fsubufs.feed(buf.breg, buf.buffer);
2018 srbD->fsubufoffsets.feed(buf.breg, buf.offsetInConstants);
2019 srbD->fsubufsizes.feed(buf.breg, buf.sizeInConstants);
2020 }
2021 srbD->fsubufs.finish();
2022 srbD->fsubufoffsets.finish();
2023 srbD->fsubufsizes.finish();
2024
2025 for (const Stage::Buffer &buf : qAsConst(res[RBM_COMPUTE].buffers)) {
2026 srbD->csubufs.feed(buf.breg, buf.buffer);
2027 srbD->csubufoffsets.feed(buf.breg, buf.offsetInConstants);
2028 srbD->csubufsizes.feed(buf.breg, buf.sizeInConstants);
2029 }
2030 srbD->csubufs.finish();
2031 srbD->csubufoffsets.finish();
2032 srbD->csubufsizes.finish();
2033
2034 for (const Stage::Texture &t : qAsConst(res[RBM_VERTEX].textures))
2035 srbD->vsshaderresources.feed(t.treg, t.srv);
2036 for (const Stage::Sampler &s : qAsConst(res[RBM_VERTEX].samplers))
2037 srbD->vssamplers.feed(s.sreg, s.sampler);
2038 srbD->vssamplers.finish();
2039 srbD->vsshaderresources.finish();
2040
2041 for (const Stage::Texture &t : qAsConst(res[RBM_FRAGMENT].textures))
2042 srbD->fsshaderresources.feed(t.treg, t.srv);
2043 for (const Stage::Sampler &s : qAsConst(res[RBM_FRAGMENT].samplers))
2044 srbD->fssamplers.feed(s.sreg, s.sampler);
2045 srbD->fssamplers.finish();
2046 srbD->fsshaderresources.finish();
2047
2048 for (const Stage::Texture &t : qAsConst(res[RBM_COMPUTE].textures))
2049 srbD->csshaderresources.feed(t.treg, t.srv);
2050 for (const Stage::Sampler &s : qAsConst(res[RBM_COMPUTE].samplers))
2051 srbD->cssamplers.feed(s.sreg, s.sampler);
2052 srbD->cssamplers.finish();
2053 srbD->csshaderresources.finish();
2054
2055 for (const Stage::Uav &u : qAsConst(res[RBM_COMPUTE].uavs))
2056 srbD->csUAVs.feed(u.ureg, u.uav);
2057 srbD->csUAVs.finish();
2058 }
2059
executeBufferHostWrites(QD3D11Buffer * bufD)2060 void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
2061 {
2062 if (!bufD->hasPendingDynamicUpdates)
2063 return;
2064
2065 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2066 bufD->hasPendingDynamicUpdates = false;
2067 D3D11_MAPPED_SUBRESOURCE mp;
2068 HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
2069 if (SUCCEEDED(hr)) {
2070 memcpy(mp.pData, bufD->dynBuf.constData(), size_t(bufD->dynBuf.size()));
2071 context->Unmap(bufD->buffer, 0);
2072 } else {
2073 qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
2074 }
2075 }
2076
applyDynamicOffsets(QVarLengthArray<UINT,4> * offsets,int batchIndex,QRhiBatchedBindings<ID3D11Buffer * > * ubufs,QRhiBatchedBindings<UINT> * ubufoffsets,const uint * dynOfsPairs,int dynOfsPairCount)2077 static void applyDynamicOffsets(QVarLengthArray<UINT, 4> *offsets,
2078 int batchIndex,
2079 QRhiBatchedBindings<ID3D11Buffer *> *ubufs,
2080 QRhiBatchedBindings<UINT> *ubufoffsets,
2081 const uint *dynOfsPairs, int dynOfsPairCount)
2082 {
2083 const int count = ubufs->batches[batchIndex].resources.count();
2084 const UINT startBinding = ubufs->batches[batchIndex].startBinding;
2085 *offsets = ubufoffsets->batches[batchIndex].resources;
2086 for (int b = 0; b < count; ++b) {
2087 for (int di = 0; di < dynOfsPairCount; ++di) {
2088 const uint binding = dynOfsPairs[2 * di];
2089 if (binding == startBinding + UINT(b)) {
2090 const uint offsetInConstants = dynOfsPairs[2 * di + 1];
2091 (*offsets)[b] = offsetInConstants;
2092 break;
2093 }
2094 }
2095 }
2096 }
2097
clampedResourceCount(uint startSlot,int countSlots,uint maxSlots,const char * resType)2098 static inline uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType)
2099 {
2100 if (startSlot + countSlots > maxSlots) {
2101 qWarning("Not enough D3D11 %s slots to bind %d resources starting at slot %d, max slots is %d",
2102 resType, countSlots, startSlot, maxSlots);
2103 countSlots = maxSlots > startSlot ? maxSlots - startSlot : 0;
2104 }
2105 return countSlots;
2106 }
2107
bindShaderResources(QD3D11ShaderResourceBindings * srbD,const uint * dynOfsPairs,int dynOfsPairCount,bool offsetOnlyChange)2108 void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
2109 const uint *dynOfsPairs, int dynOfsPairCount,
2110 bool offsetOnlyChange)
2111 {
2112 if (!offsetOnlyChange) {
2113 for (const auto &batch : srbD->vssamplers.batches) {
2114 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2115 D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "VS sampler");
2116 if (count)
2117 context->VSSetSamplers(batch.startBinding, count, batch.resources.constData());
2118 }
2119
2120 for (const auto &batch : srbD->vsshaderresources.batches) {
2121 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2122 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "VS SRV");
2123 if (count) {
2124 context->VSSetShaderResources(batch.startBinding, count, batch.resources.constData());
2125 contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding,
2126 int(batch.startBinding + count) - 1);
2127 }
2128 }
2129
2130 for (const auto &batch : srbD->fssamplers.batches) {
2131 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2132 D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "PS sampler");
2133 if (count)
2134 context->PSSetSamplers(batch.startBinding, count, batch.resources.constData());
2135 }
2136
2137 for (const auto &batch : srbD->fsshaderresources.batches) {
2138 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2139 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "PS SRV");
2140 if (count) {
2141 context->PSSetShaderResources(batch.startBinding, count, batch.resources.constData());
2142 contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding,
2143 int(batch.startBinding + count) - 1);
2144 }
2145 }
2146
2147 for (const auto &batch : srbD->cssamplers.batches) {
2148 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2149 D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "CS sampler");
2150 if (count)
2151 context->CSSetSamplers(batch.startBinding, count, batch.resources.constData());
2152 }
2153
2154 for (const auto &batch : srbD->csshaderresources.batches) {
2155 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2156 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "CS SRV");
2157 if (count) {
2158 context->CSSetShaderResources(batch.startBinding, count, batch.resources.constData());
2159 contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding,
2160 int(batch.startBinding + count) - 1);
2161 }
2162 }
2163 }
2164
2165 for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) {
2166 const uint count = clampedResourceCount(srbD->vsubufs.batches[i].startBinding,
2167 srbD->vsubufs.batches[i].resources.count(),
2168 D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
2169 "VS cbuf");
2170 if (count) {
2171 if (!dynOfsPairCount) {
2172 context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
2173 count,
2174 srbD->vsubufs.batches[i].resources.constData(),
2175 srbD->vsubufoffsets.batches[i].resources.constData(),
2176 srbD->vsubufsizes.batches[i].resources.constData());
2177 } else {
2178 QVarLengthArray<UINT, 4> offsets;
2179 applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount);
2180 context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
2181 count,
2182 srbD->vsubufs.batches[i].resources.constData(),
2183 offsets.constData(),
2184 srbD->vsubufsizes.batches[i].resources.constData());
2185 }
2186 }
2187 }
2188
2189 for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) {
2190 const uint count = clampedResourceCount(srbD->fsubufs.batches[i].startBinding,
2191 srbD->fsubufs.batches[i].resources.count(),
2192 D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
2193 "PS cbuf");
2194 if (count) {
2195 if (!dynOfsPairCount) {
2196 context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
2197 count,
2198 srbD->fsubufs.batches[i].resources.constData(),
2199 srbD->fsubufoffsets.batches[i].resources.constData(),
2200 srbD->fsubufsizes.batches[i].resources.constData());
2201 } else {
2202 QVarLengthArray<UINT, 4> offsets;
2203 applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount);
2204 context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
2205 count,
2206 srbD->fsubufs.batches[i].resources.constData(),
2207 offsets.constData(),
2208 srbD->fsubufsizes.batches[i].resources.constData());
2209 }
2210 }
2211 }
2212
2213 for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) {
2214 const uint count = clampedResourceCount(srbD->csubufs.batches[i].startBinding,
2215 srbD->csubufs.batches[i].resources.count(),
2216 D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
2217 "CS cbuf");
2218 if (count) {
2219 if (!dynOfsPairCount) {
2220 context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
2221 count,
2222 srbD->csubufs.batches[i].resources.constData(),
2223 srbD->csubufoffsets.batches[i].resources.constData(),
2224 srbD->csubufsizes.batches[i].resources.constData());
2225 } else {
2226 QVarLengthArray<UINT, 4> offsets;
2227 applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount);
2228 context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
2229 count,
2230 srbD->csubufs.batches[i].resources.constData(),
2231 offsets.constData(),
2232 srbD->csubufsizes.batches[i].resources.constData());
2233 }
2234 }
2235 }
2236
2237 for (const auto &batch : srbD->csUAVs.batches) {
2238 const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
2239 D3D11_1_UAV_SLOT_COUNT, "CS UAV");
2240 if (count) {
2241 context->CSSetUnorderedAccessViews(batch.startBinding,
2242 count,
2243 batch.resources.constData(),
2244 nullptr);
2245 contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding,
2246 int(batch.startBinding + count) - 1);
2247 }
2248 }
2249 }
2250
resetShaderResources()2251 void QRhiD3D11::resetShaderResources()
2252 {
2253 // Output cannot be bound on input etc.
2254
2255 if (contextState.vsHasIndexBufferBound) {
2256 context->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
2257 contextState.vsHasIndexBufferBound = false;
2258 }
2259
2260 if (contextState.vsHighestActiveVertexBufferBinding >= 0) {
2261 const int count = contextState.vsHighestActiveVertexBufferBinding + 1;
2262 QVarLengthArray<ID3D11Buffer *, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullbufs(count);
2263 for (int i = 0; i < count; ++i)
2264 nullbufs[i] = nullptr;
2265 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullstrides(count);
2266 for (int i = 0; i < count; ++i)
2267 nullstrides[i] = 0;
2268 QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nulloffsets(count);
2269 for (int i = 0; i < count; ++i)
2270 nulloffsets[i] = 0;
2271 context->IASetVertexBuffers(0, UINT(count), nullbufs.constData(), nullstrides.constData(), nulloffsets.constData());
2272 contextState.vsHighestActiveVertexBufferBinding = -1;
2273 }
2274
2275 int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
2276 nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
2277 nullsrvCount += 1;
2278 if (nullsrvCount > 0) {
2279 QVarLengthArray<ID3D11ShaderResourceView *,
2280 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nullsrvs(nullsrvCount);
2281 for (int i = 0; i < nullsrvs.count(); ++i)
2282 nullsrvs[i] = nullptr;
2283 if (contextState.vsHighestActiveSrvBinding >= 0) {
2284 context->VSSetShaderResources(0, UINT(contextState.vsHighestActiveSrvBinding + 1), nullsrvs.constData());
2285 contextState.vsHighestActiveSrvBinding = -1;
2286 }
2287 if (contextState.fsHighestActiveSrvBinding >= 0) {
2288 context->PSSetShaderResources(0, UINT(contextState.fsHighestActiveSrvBinding + 1), nullsrvs.constData());
2289 contextState.fsHighestActiveSrvBinding = -1;
2290 }
2291 if (contextState.csHighestActiveSrvBinding >= 0) {
2292 context->CSSetShaderResources(0, UINT(contextState.csHighestActiveSrvBinding + 1), nullsrvs.constData());
2293 contextState.csHighestActiveSrvBinding = -1;
2294 }
2295 }
2296
2297 if (contextState.csHighestActiveUavBinding >= 0) {
2298 const int nulluavCount = contextState.csHighestActiveUavBinding + 1;
2299 QVarLengthArray<ID3D11UnorderedAccessView *,
2300 D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nulluavs(nulluavCount);
2301 for (int i = 0; i < nulluavCount; ++i)
2302 nulluavs[i] = nullptr;
2303 context->CSSetUnorderedAccessViews(0, UINT(nulluavCount), nulluavs.constData(), nullptr);
2304 contextState.csHighestActiveUavBinding = -1;
2305 }
2306 }
2307
executeCommandBuffer(QD3D11CommandBuffer * cbD,QD3D11SwapChain * timestampSwapChain)2308 void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain)
2309 {
2310 quint32 stencilRef = 0;
2311 float blendConstants[] = { 1, 1, 1, 1 };
2312
2313 if (timestampSwapChain) {
2314 const int currentFrameSlot = timestampSwapChain->currentFrameSlot;
2315 ID3D11Query *tsDisjoint = timestampSwapChain->timestampDisjointQuery[currentFrameSlot];
2316 const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
2317 ID3D11Query *tsStart = timestampSwapChain->timestampQuery[tsIdx];
2318 if (tsDisjoint && tsStart && !timestampSwapChain->timestampActive[currentFrameSlot]) {
2319 // The timestamps seem to include vsync time with Present(1), except
2320 // when running on a non-primary gpu. This is not ideal. So try working
2321 // it around by issuing a semi-fake OMSetRenderTargets early and
2322 // writing the first timestamp only afterwards.
2323 context->Begin(tsDisjoint);
2324 QD3D11RenderTargetData *rtD = rtData(×tampSwapChain->rt);
2325 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
2326 context->End(tsStart); // just record a timestamp, no Begin needed
2327 }
2328 }
2329
2330 for (const QD3D11CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
2331 switch (cmd.cmd) {
2332 case QD3D11CommandBuffer::Command::ResetShaderResources:
2333 resetShaderResources();
2334 break;
2335 case QD3D11CommandBuffer::Command::SetRenderTarget:
2336 {
2337 QD3D11RenderTargetData *rtD = rtData(cmd.args.setRenderTarget.rt);
2338 context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
2339 }
2340 break;
2341 case QD3D11CommandBuffer::Command::Clear:
2342 {
2343 QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt);
2344 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) {
2345 for (int i = 0; i < rtD->colorAttCount; ++i)
2346 context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c);
2347 }
2348 uint ds = 0;
2349 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth)
2350 ds |= D3D11_CLEAR_DEPTH;
2351 if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil)
2352 ds |= D3D11_CLEAR_STENCIL;
2353 if (ds)
2354 context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, UINT8(cmd.args.clear.s));
2355 }
2356 break;
2357 case QD3D11CommandBuffer::Command::Viewport:
2358 {
2359 D3D11_VIEWPORT v;
2360 v.TopLeftX = cmd.args.viewport.x;
2361 v.TopLeftY = cmd.args.viewport.y;
2362 v.Width = cmd.args.viewport.w;
2363 v.Height = cmd.args.viewport.h;
2364 v.MinDepth = cmd.args.viewport.d0;
2365 v.MaxDepth = cmd.args.viewport.d1;
2366 context->RSSetViewports(1, &v);
2367 }
2368 break;
2369 case QD3D11CommandBuffer::Command::Scissor:
2370 {
2371 D3D11_RECT r;
2372 r.left = cmd.args.scissor.x;
2373 r.top = cmd.args.scissor.y;
2374 // right and bottom are exclusive
2375 r.right = cmd.args.scissor.x + cmd.args.scissor.w;
2376 r.bottom = cmd.args.scissor.y + cmd.args.scissor.h;
2377 context->RSSetScissorRects(1, &r);
2378 }
2379 break;
2380 case QD3D11CommandBuffer::Command::BindVertexBuffers:
2381 contextState.vsHighestActiveVertexBufferBinding = qMax<int>(
2382 contextState.vsHighestActiveVertexBufferBinding,
2383 cmd.args.bindVertexBuffers.startSlot + cmd.args.bindVertexBuffers.slotCount - 1);
2384 context->IASetVertexBuffers(UINT(cmd.args.bindVertexBuffers.startSlot),
2385 UINT(cmd.args.bindVertexBuffers.slotCount),
2386 cmd.args.bindVertexBuffers.buffers,
2387 cmd.args.bindVertexBuffers.strides,
2388 cmd.args.bindVertexBuffers.offsets);
2389 break;
2390 case QD3D11CommandBuffer::Command::BindIndexBuffer:
2391 contextState.vsHasIndexBufferBound = true;
2392 context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer,
2393 cmd.args.bindIndexBuffer.format,
2394 cmd.args.bindIndexBuffer.offset);
2395 break;
2396 case QD3D11CommandBuffer::Command::BindGraphicsPipeline:
2397 {
2398 QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
2399 context->VSSetShader(psD->vs.shader, nullptr, 0);
2400 context->PSSetShader(psD->fs.shader, nullptr, 0);
2401 context->IASetPrimitiveTopology(psD->d3dTopology);
2402 context->IASetInputLayout(psD->inputLayout);
2403 context->OMSetDepthStencilState(psD->dsState, stencilRef);
2404 context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff);
2405 context->RSSetState(psD->rastState);
2406 }
2407 break;
2408 case QD3D11CommandBuffer::Command::BindShaderResources:
2409 bindShaderResources(cmd.args.bindShaderResources.srb,
2410 cmd.args.bindShaderResources.dynamicOffsetPairs,
2411 cmd.args.bindShaderResources.dynamicOffsetCount,
2412 cmd.args.bindShaderResources.offsetOnlyChange);
2413 break;
2414 case QD3D11CommandBuffer::Command::StencilRef:
2415 stencilRef = cmd.args.stencilRef.ref;
2416 context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef);
2417 break;
2418 case QD3D11CommandBuffer::Command::BlendConstants:
2419 memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float));
2420 context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff);
2421 break;
2422 case QD3D11CommandBuffer::Command::Draw:
2423 if (cmd.args.draw.ps) {
2424 if (cmd.args.draw.instanceCount == 1)
2425 context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex);
2426 else
2427 context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
2428 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
2429 } else {
2430 qWarning("No graphics pipeline active for draw; ignored");
2431 }
2432 break;
2433 case QD3D11CommandBuffer::Command::DrawIndexed:
2434 if (cmd.args.drawIndexed.ps) {
2435 if (cmd.args.drawIndexed.instanceCount == 1)
2436 context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex,
2437 cmd.args.drawIndexed.vertexOffset);
2438 else
2439 context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
2440 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
2441 cmd.args.drawIndexed.firstInstance);
2442 } else {
2443 qWarning("No graphics pipeline active for drawIndexed; ignored");
2444 }
2445 break;
2446 case QD3D11CommandBuffer::Command::UpdateSubRes:
2447 context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes,
2448 cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr,
2449 cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0);
2450 break;
2451 case QD3D11CommandBuffer::Command::CopySubRes:
2452 context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
2453 cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0,
2454 cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
2455 cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
2456 break;
2457 case QD3D11CommandBuffer::Command::ResolveSubRes:
2458 context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes,
2459 cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
2460 cmd.args.resolveSubRes.format);
2461 break;
2462 case QD3D11CommandBuffer::Command::GenMip:
2463 context->GenerateMips(cmd.args.genMip.srv);
2464 break;
2465 case QD3D11CommandBuffer::Command::DebugMarkBegin:
2466 annotations->BeginEvent(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
2467 break;
2468 case QD3D11CommandBuffer::Command::DebugMarkEnd:
2469 annotations->EndEvent();
2470 break;
2471 case QD3D11CommandBuffer::Command::DebugMarkMsg:
2472 annotations->SetMarker(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
2473 break;
2474 case QD3D11CommandBuffer::Command::BindComputePipeline:
2475 context->CSSetShader(cmd.args.bindComputePipeline.ps->cs.shader, nullptr, 0);
2476 break;
2477 case QD3D11CommandBuffer::Command::Dispatch:
2478 context->Dispatch(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
2479 break;
2480 default:
2481 break;
2482 }
2483 }
2484 }
2485
QD3D11Buffer(QRhiImplementation * rhi,Type type,UsageFlags usage,int size)2486 QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
2487 : QRhiBuffer(rhi, type, usage, size)
2488 {
2489 }
2490
~QD3D11Buffer()2491 QD3D11Buffer::~QD3D11Buffer()
2492 {
2493 release();
2494 }
2495
release()2496 void QD3D11Buffer::release()
2497 {
2498 if (!buffer)
2499 return;
2500
2501 dynBuf.clear();
2502
2503 buffer->Release();
2504 buffer = nullptr;
2505
2506 if (uav) {
2507 uav->Release();
2508 uav = nullptr;
2509 }
2510
2511 QRHI_RES_RHI(QRhiD3D11);
2512 QRHI_PROF;
2513 QRHI_PROF_F(releaseBuffer(this));
2514 rhiD->unregisterResource(this);
2515 }
2516
toD3DBufferUsage(QRhiBuffer::UsageFlags usage)2517 static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
2518 {
2519 int u = 0;
2520 if (usage.testFlag(QRhiBuffer::VertexBuffer))
2521 u |= D3D11_BIND_VERTEX_BUFFER;
2522 if (usage.testFlag(QRhiBuffer::IndexBuffer))
2523 u |= D3D11_BIND_INDEX_BUFFER;
2524 if (usage.testFlag(QRhiBuffer::UniformBuffer))
2525 u |= D3D11_BIND_CONSTANT_BUFFER;
2526 if (usage.testFlag(QRhiBuffer::StorageBuffer))
2527 u |= D3D11_BIND_UNORDERED_ACCESS;
2528 return uint(u);
2529 }
2530
build()2531 bool QD3D11Buffer::build()
2532 {
2533 if (buffer)
2534 release();
2535
2536 if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
2537 qWarning("UniformBuffer must always be combined with Dynamic on D3D11");
2538 return false;
2539 }
2540
2541 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
2542 qWarning("StorageBuffer cannot be combined with Dynamic");
2543 return false;
2544 }
2545
2546 const int nonZeroSize = m_size <= 0 ? 256 : m_size;
2547 const int roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256 : 4);
2548
2549 D3D11_BUFFER_DESC desc;
2550 memset(&desc, 0, sizeof(desc));
2551 desc.ByteWidth = UINT(roundedSize);
2552 desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
2553 desc.BindFlags = toD3DBufferUsage(m_usage);
2554 desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
2555 desc.MiscFlags = m_usage.testFlag(QRhiBuffer::StorageBuffer) ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0;
2556
2557 QRHI_RES_RHI(QRhiD3D11);
2558 HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
2559 if (FAILED(hr)) {
2560 qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
2561 return false;
2562 }
2563
2564 if (m_type == Dynamic) {
2565 dynBuf.resize(m_size);
2566 hasPendingDynamicUpdates = false;
2567 }
2568
2569 if (!m_objectName.isEmpty())
2570 buffer->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
2571
2572 QRHI_PROF;
2573 QRHI_PROF_F(newBuffer(this, quint32(roundedSize), m_type == Dynamic ? 2 : 1, m_type == Dynamic ? 1 : 0));
2574
2575 generation += 1;
2576 rhiD->registerResource(this);
2577 return true;
2578 }
2579
nativeBuffer()2580 QRhiBuffer::NativeBuffer QD3D11Buffer::nativeBuffer()
2581 {
2582 if (m_type == Dynamic) {
2583 QRHI_RES_RHI(QRhiD3D11);
2584 rhiD->executeBufferHostWrites(this);
2585 }
2586 return { { &buffer }, 1 };
2587 }
2588
unorderedAccessView()2589 ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView()
2590 {
2591 if (uav)
2592 return uav;
2593
2594 // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
2595 D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
2596 memset(&desc, 0, sizeof(desc));
2597 desc.Format = DXGI_FORMAT_R32_TYPELESS;
2598 desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
2599 desc.Buffer.FirstElement = 0;
2600 desc.Buffer.NumElements = UINT(aligned(m_size, 4) / 4);
2601 desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
2602
2603 QRHI_RES_RHI(QRhiD3D11);
2604 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav);
2605 if (FAILED(hr)) {
2606 qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
2607 return nullptr;
2608 }
2609
2610 return uav;
2611 }
2612
QD3D11RenderBuffer(QRhiImplementation * rhi,Type type,const QSize & pixelSize,int sampleCount,QRhiRenderBuffer::Flags flags)2613 QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
2614 int sampleCount, QRhiRenderBuffer::Flags flags)
2615 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
2616 {
2617 }
2618
~QD3D11RenderBuffer()2619 QD3D11RenderBuffer::~QD3D11RenderBuffer()
2620 {
2621 release();
2622 }
2623
release()2624 void QD3D11RenderBuffer::release()
2625 {
2626 if (!tex)
2627 return;
2628
2629 if (dsv) {
2630 dsv->Release();
2631 dsv = nullptr;
2632 }
2633
2634 if (rtv) {
2635 rtv->Release();
2636 rtv = nullptr;
2637 }
2638
2639 tex->Release();
2640 tex = nullptr;
2641
2642 QRHI_RES_RHI(QRhiD3D11);
2643 QRHI_PROF;
2644 QRHI_PROF_F(releaseRenderBuffer(this));
2645 rhiD->unregisterResource(this);
2646 }
2647
build()2648 bool QD3D11RenderBuffer::build()
2649 {
2650 if (tex)
2651 release();
2652
2653 if (m_pixelSize.isEmpty())
2654 return false;
2655
2656 QRHI_RES_RHI(QRhiD3D11);
2657 sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
2658
2659 D3D11_TEXTURE2D_DESC desc;
2660 memset(&desc, 0, sizeof(desc));
2661 desc.Width = UINT(m_pixelSize.width());
2662 desc.Height = UINT(m_pixelSize.height());
2663 desc.MipLevels = 1;
2664 desc.ArraySize = 1;
2665 desc.SampleDesc = sampleDesc;
2666 desc.Usage = D3D11_USAGE_DEFAULT;
2667
2668 if (m_type == Color) {
2669 dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
2670 desc.Format = dxgiFormat;
2671 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
2672 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
2673 if (FAILED(hr)) {
2674 qWarning("Failed to create color renderbuffer: %s", qPrintable(comErrorMessage(hr)));
2675 return false;
2676 }
2677 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
2678 memset(&rtvDesc, 0, sizeof(rtvDesc));
2679 rtvDesc.Format = dxgiFormat;
2680 rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS
2681 : D3D11_RTV_DIMENSION_TEXTURE2D;
2682 hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv);
2683 if (FAILED(hr)) {
2684 qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
2685 return false;
2686 }
2687 } else if (m_type == DepthStencil) {
2688 dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
2689 desc.Format = dxgiFormat;
2690 desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
2691 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
2692 if (FAILED(hr)) {
2693 qWarning("Failed to create depth-stencil buffer: %s", qPrintable(comErrorMessage(hr)));
2694 return false;
2695 }
2696 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
2697 memset(&dsvDesc, 0, sizeof(dsvDesc));
2698 dsvDesc.Format = dxgiFormat;
2699 dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
2700 : D3D11_DSV_DIMENSION_TEXTURE2D;
2701 hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv);
2702 if (FAILED(hr)) {
2703 qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
2704 return false;
2705 }
2706 } else {
2707 return false;
2708 }
2709
2710 if (!m_objectName.isEmpty())
2711 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
2712
2713 QRHI_PROF;
2714 QRHI_PROF_F(newRenderBuffer(this, false, false, int(sampleDesc.Count)));
2715
2716 rhiD->registerResource(this);
2717 return true;
2718 }
2719
backingFormat() const2720 QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
2721 {
2722 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
2723 }
2724
QD3D11Texture(QRhiImplementation * rhi,Format format,const QSize & pixelSize,int sampleCount,Flags flags)2725 QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
2726 int sampleCount, Flags flags)
2727 : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
2728 {
2729 for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
2730 perLevelViews[i] = nullptr;
2731 }
2732
~QD3D11Texture()2733 QD3D11Texture::~QD3D11Texture()
2734 {
2735 release();
2736 }
2737
release()2738 void QD3D11Texture::release()
2739 {
2740 if (!tex)
2741 return;
2742
2743 if (srv) {
2744 srv->Release();
2745 srv = nullptr;
2746 }
2747
2748 for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
2749 if (perLevelViews[i]) {
2750 perLevelViews[i]->Release();
2751 perLevelViews[i] = nullptr;
2752 }
2753 }
2754
2755 if (owns)
2756 tex->Release();
2757
2758 tex = nullptr;
2759
2760 QRHI_RES_RHI(QRhiD3D11);
2761 QRHI_PROF;
2762 QRHI_PROF_F(releaseTexture(this));
2763 rhiD->unregisterResource(this);
2764 }
2765
toD3DDepthTextureSRVFormat(QRhiTexture::Format format)2766 static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
2767 {
2768 switch (format) {
2769 case QRhiTexture::Format::D16:
2770 return DXGI_FORMAT_R16_FLOAT;
2771 case QRhiTexture::Format::D32F:
2772 return DXGI_FORMAT_R32_FLOAT;
2773 default:
2774 Q_UNREACHABLE();
2775 return DXGI_FORMAT_R32_FLOAT;
2776 }
2777 }
2778
toD3DDepthTextureDSVFormat(QRhiTexture::Format format)2779 static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
2780 {
2781 switch (format) {
2782 case QRhiTexture::Format::D16:
2783 return DXGI_FORMAT_D16_UNORM;
2784 case QRhiTexture::Format::D32F:
2785 return DXGI_FORMAT_D32_FLOAT;
2786 default:
2787 Q_UNREACHABLE();
2788 return DXGI_FORMAT_D32_FLOAT;
2789 }
2790 }
2791
prepareBuild(QSize * adjustedSize)2792 bool QD3D11Texture::prepareBuild(QSize *adjustedSize)
2793 {
2794 if (tex)
2795 release();
2796
2797 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
2798 const bool isDepth = isDepthTextureFormat(m_format);
2799 const bool isCube = m_flags.testFlag(CubeMap);
2800 const bool hasMipMaps = m_flags.testFlag(MipMapped);
2801
2802 QRHI_RES_RHI(QRhiD3D11);
2803 dxgiFormat = toD3DTextureFormat(m_format, m_flags);
2804 mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
2805 sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
2806 if (sampleDesc.Count > 1) {
2807 if (isCube) {
2808 qWarning("Cubemap texture cannot be multisample");
2809 return false;
2810 }
2811 if (hasMipMaps) {
2812 qWarning("Multisample texture cannot have mipmaps");
2813 return false;
2814 }
2815 }
2816 if (isDepth && hasMipMaps) {
2817 qWarning("Depth texture cannot have mipmaps");
2818 return false;
2819 }
2820
2821 if (adjustedSize)
2822 *adjustedSize = size;
2823
2824 return true;
2825 }
2826
finishBuild()2827 bool QD3D11Texture::finishBuild()
2828 {
2829 QRHI_RES_RHI(QRhiD3D11);
2830 const bool isDepth = isDepthTextureFormat(m_format);
2831 const bool isCube = m_flags.testFlag(CubeMap);
2832
2833 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
2834 memset(&srvDesc, 0, sizeof(srvDesc));
2835 srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
2836 if (isCube) {
2837 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
2838 srvDesc.TextureCube.MipLevels = mipLevelCount;
2839 } else {
2840 if (sampleDesc.Count > 1) {
2841 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
2842 } else {
2843 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
2844 srvDesc.Texture2D.MipLevels = mipLevelCount;
2845 }
2846 }
2847
2848 HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv);
2849 if (FAILED(hr)) {
2850 qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
2851 return false;
2852 }
2853
2854 generation += 1;
2855 return true;
2856 }
2857
build()2858 bool QD3D11Texture::build()
2859 {
2860 QSize size;
2861 if (!prepareBuild(&size))
2862 return false;
2863
2864 const bool isDepth = isDepthTextureFormat(m_format);
2865 const bool isCube = m_flags.testFlag(CubeMap);
2866
2867 uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
2868 uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
2869 if (m_flags.testFlag(RenderTarget)) {
2870 if (isDepth)
2871 bindFlags |= D3D11_BIND_DEPTH_STENCIL;
2872 else
2873 bindFlags |= D3D11_BIND_RENDER_TARGET;
2874 }
2875 if (m_flags.testFlag(UsedWithGenerateMips)) {
2876 if (isDepth) {
2877 qWarning("Depth texture cannot have mipmaps generated");
2878 return false;
2879 }
2880 bindFlags |= D3D11_BIND_RENDER_TARGET;
2881 miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
2882 }
2883 if (m_flags.testFlag(UsedWithLoadStore))
2884 bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
2885
2886 D3D11_TEXTURE2D_DESC desc;
2887 memset(&desc, 0, sizeof(desc));
2888 desc.Width = UINT(size.width());
2889 desc.Height = UINT(size.height());
2890 desc.MipLevels = mipLevelCount;
2891 desc.ArraySize = isCube ? 6 : 1;
2892 desc.Format = dxgiFormat;
2893 desc.SampleDesc = sampleDesc;
2894 desc.Usage = D3D11_USAGE_DEFAULT;
2895 desc.BindFlags = bindFlags;
2896 desc.MiscFlags = miscFlags;
2897
2898 QRHI_RES_RHI(QRhiD3D11);
2899 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
2900 if (FAILED(hr)) {
2901 qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr)));
2902 return false;
2903 }
2904
2905 if (!finishBuild())
2906 return false;
2907
2908 if (!m_objectName.isEmpty())
2909 tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
2910
2911 QRHI_PROF;
2912 QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count)));
2913
2914 owns = true;
2915 rhiD->registerResource(this);
2916 return true;
2917 }
2918
buildFrom(QRhiTexture::NativeTexture src)2919 bool QD3D11Texture::buildFrom(QRhiTexture::NativeTexture src)
2920 {
2921 auto *srcTex = static_cast<ID3D11Texture2D * const *>(src.object);
2922 if (!srcTex || !*srcTex)
2923 return false;
2924
2925 if (!prepareBuild())
2926 return false;
2927
2928 tex = *srcTex;
2929
2930 if (!finishBuild())
2931 return false;
2932
2933 QRHI_PROF;
2934 QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, int(sampleDesc.Count)));
2935
2936 owns = false;
2937 QRHI_RES_RHI(QRhiD3D11);
2938 rhiD->registerResource(this);
2939 return true;
2940 }
2941
nativeTexture()2942 QRhiTexture::NativeTexture QD3D11Texture::nativeTexture()
2943 {
2944 return {&tex, 0};
2945 }
2946
unorderedAccessViewForLevel(int level)2947 ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
2948 {
2949 if (perLevelViews[level])
2950 return perLevelViews[level];
2951
2952 const bool isCube = m_flags.testFlag(CubeMap);
2953 D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
2954 memset(&desc, 0, sizeof(desc));
2955 desc.Format = dxgiFormat;
2956 if (isCube) {
2957 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
2958 desc.Texture2DArray.MipSlice = UINT(level);
2959 desc.Texture2DArray.FirstArraySlice = 0;
2960 desc.Texture2DArray.ArraySize = 6;
2961 } else {
2962 desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
2963 desc.Texture2D.MipSlice = UINT(level);
2964 }
2965
2966 QRHI_RES_RHI(QRhiD3D11);
2967 ID3D11UnorderedAccessView *uav = nullptr;
2968 HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav);
2969 if (FAILED(hr)) {
2970 qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
2971 return nullptr;
2972 }
2973
2974 perLevelViews[level] = uav;
2975 return uav;
2976 }
2977
QD3D11Sampler(QRhiImplementation * rhi,Filter magFilter,Filter minFilter,Filter mipmapMode,AddressMode u,AddressMode v,AddressMode w)2978 QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
2979 AddressMode u, AddressMode v, AddressMode w)
2980 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
2981 {
2982 }
2983
~QD3D11Sampler()2984 QD3D11Sampler::~QD3D11Sampler()
2985 {
2986 release();
2987 }
2988
release()2989 void QD3D11Sampler::release()
2990 {
2991 if (!samplerState)
2992 return;
2993
2994 samplerState->Release();
2995 samplerState = nullptr;
2996
2997 QRHI_RES_RHI(QRhiD3D11);
2998 rhiD->unregisterResource(this);
2999 }
3000
toD3DFilter(QRhiSampler::Filter minFilter,QRhiSampler::Filter magFilter,QRhiSampler::Filter mipFilter)3001 static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
3002 {
3003 if (minFilter == QRhiSampler::Nearest) {
3004 if (magFilter == QRhiSampler::Nearest) {
3005 if (mipFilter == QRhiSampler::Linear)
3006 return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
3007 else
3008 return D3D11_FILTER_MIN_MAG_MIP_POINT;
3009 } else {
3010 if (mipFilter == QRhiSampler::Linear)
3011 return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
3012 else
3013 return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
3014 }
3015 } else {
3016 if (magFilter == QRhiSampler::Nearest) {
3017 if (mipFilter == QRhiSampler::Linear)
3018 return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
3019 else
3020 return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
3021 } else {
3022 if (mipFilter == QRhiSampler::Linear)
3023 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3024 else
3025 return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
3026 }
3027 }
3028
3029 Q_UNREACHABLE();
3030 return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
3031 }
3032
toD3DAddressMode(QRhiSampler::AddressMode m)3033 static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
3034 {
3035 switch (m) {
3036 case QRhiSampler::Repeat:
3037 return D3D11_TEXTURE_ADDRESS_WRAP;
3038 case QRhiSampler::ClampToEdge:
3039 return D3D11_TEXTURE_ADDRESS_CLAMP;
3040 case QRhiSampler::Mirror:
3041 return D3D11_TEXTURE_ADDRESS_MIRROR;
3042 default:
3043 Q_UNREACHABLE();
3044 return D3D11_TEXTURE_ADDRESS_CLAMP;
3045 }
3046 }
3047
toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)3048 static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
3049 {
3050 switch (op) {
3051 case QRhiSampler::Never:
3052 return D3D11_COMPARISON_NEVER;
3053 case QRhiSampler::Less:
3054 return D3D11_COMPARISON_LESS;
3055 case QRhiSampler::Equal:
3056 return D3D11_COMPARISON_EQUAL;
3057 case QRhiSampler::LessOrEqual:
3058 return D3D11_COMPARISON_LESS_EQUAL;
3059 case QRhiSampler::Greater:
3060 return D3D11_COMPARISON_GREATER;
3061 case QRhiSampler::NotEqual:
3062 return D3D11_COMPARISON_NOT_EQUAL;
3063 case QRhiSampler::GreaterOrEqual:
3064 return D3D11_COMPARISON_GREATER_EQUAL;
3065 case QRhiSampler::Always:
3066 return D3D11_COMPARISON_ALWAYS;
3067 default:
3068 Q_UNREACHABLE();
3069 return D3D11_COMPARISON_NEVER;
3070 }
3071 }
3072
build()3073 bool QD3D11Sampler::build()
3074 {
3075 if (samplerState)
3076 release();
3077
3078 D3D11_SAMPLER_DESC desc;
3079 memset(&desc, 0, sizeof(desc));
3080 desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
3081 if (m_compareOp != Never)
3082 desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
3083 desc.AddressU = toD3DAddressMode(m_addressU);
3084 desc.AddressV = toD3DAddressMode(m_addressV);
3085 desc.AddressW = toD3DAddressMode(m_addressW);
3086 desc.MaxAnisotropy = 1.0f;
3087 desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
3088 desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f;
3089
3090 QRHI_RES_RHI(QRhiD3D11);
3091 HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
3092 if (FAILED(hr)) {
3093 qWarning("Failed to create sampler state: %s", qPrintable(comErrorMessage(hr)));
3094 return false;
3095 }
3096
3097 generation += 1;
3098 rhiD->registerResource(this);
3099 return true;
3100 }
3101
3102 // dummy, no Vulkan-style RenderPass+Framebuffer concept here
QD3D11RenderPassDescriptor(QRhiImplementation * rhi)3103 QD3D11RenderPassDescriptor::QD3D11RenderPassDescriptor(QRhiImplementation *rhi)
3104 : QRhiRenderPassDescriptor(rhi)
3105 {
3106 }
3107
~QD3D11RenderPassDescriptor()3108 QD3D11RenderPassDescriptor::~QD3D11RenderPassDescriptor()
3109 {
3110 release();
3111 }
3112
release()3113 void QD3D11RenderPassDescriptor::release()
3114 {
3115 // nothing to do here
3116 }
3117
isCompatible(const QRhiRenderPassDescriptor * other) const3118 bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
3119 {
3120 Q_UNUSED(other);
3121 return true;
3122 }
3123
QD3D11ReferenceRenderTarget(QRhiImplementation * rhi)3124 QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
3125 : QRhiRenderTarget(rhi),
3126 d(rhi)
3127 {
3128 }
3129
~QD3D11ReferenceRenderTarget()3130 QD3D11ReferenceRenderTarget::~QD3D11ReferenceRenderTarget()
3131 {
3132 release();
3133 }
3134
release()3135 void QD3D11ReferenceRenderTarget::release()
3136 {
3137 // nothing to do here
3138 }
3139
pixelSize() const3140 QSize QD3D11ReferenceRenderTarget::pixelSize() const
3141 {
3142 return d.pixelSize;
3143 }
3144
devicePixelRatio() const3145 float QD3D11ReferenceRenderTarget::devicePixelRatio() const
3146 {
3147 return d.dpr;
3148 }
3149
sampleCount() const3150 int QD3D11ReferenceRenderTarget::sampleCount() const
3151 {
3152 return d.sampleCount;
3153 }
3154
QD3D11TextureRenderTarget(QRhiImplementation * rhi,const QRhiTextureRenderTargetDescription & desc,Flags flags)3155 QD3D11TextureRenderTarget::QD3D11TextureRenderTarget(QRhiImplementation *rhi,
3156 const QRhiTextureRenderTargetDescription &desc,
3157 Flags flags)
3158 : QRhiTextureRenderTarget(rhi, desc, flags),
3159 d(rhi)
3160 {
3161 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
3162 ownsRtv[i] = false;
3163 rtv[i] = nullptr;
3164 }
3165 }
3166
~QD3D11TextureRenderTarget()3167 QD3D11TextureRenderTarget::~QD3D11TextureRenderTarget()
3168 {
3169 release();
3170 }
3171
release()3172 void QD3D11TextureRenderTarget::release()
3173 {
3174 QRHI_RES_RHI(QRhiD3D11);
3175
3176 if (!rtv[0] && !dsv)
3177 return;
3178
3179 if (dsv) {
3180 if (ownsDsv)
3181 dsv->Release();
3182 dsv = nullptr;
3183 }
3184
3185 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
3186 if (rtv[i]) {
3187 if (ownsRtv[i])
3188 rtv[i]->Release();
3189 rtv[i] = nullptr;
3190 }
3191 }
3192
3193 rhiD->unregisterResource(this);
3194 }
3195
newCompatibleRenderPassDescriptor()3196 QRhiRenderPassDescriptor *QD3D11TextureRenderTarget::newCompatibleRenderPassDescriptor()
3197 {
3198 return new QD3D11RenderPassDescriptor(m_rhi);
3199 }
3200
build()3201 bool QD3D11TextureRenderTarget::build()
3202 {
3203 if (rtv[0] || dsv)
3204 release();
3205
3206 const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
3207 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
3208 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
3209 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
3210
3211 QRHI_RES_RHI(QRhiD3D11);
3212
3213 d.colorAttCount = 0;
3214 int attIndex = 0;
3215 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
3216 d.colorAttCount += 1;
3217 const QRhiColorAttachment &colorAtt(*it);
3218 QRhiTexture *texture = colorAtt.texture();
3219 QRhiRenderBuffer *rb = colorAtt.renderBuffer();
3220 Q_ASSERT(texture || rb);
3221 if (texture) {
3222 QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
3223 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
3224 memset(&rtvDesc, 0, sizeof(rtvDesc));
3225 rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
3226 if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
3227 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
3228 rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
3229 rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
3230 rtvDesc.Texture2DArray.ArraySize = 1;
3231 } else {
3232 if (texD->sampleDesc.Count > 1) {
3233 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
3234 } else {
3235 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
3236 rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
3237 }
3238 }
3239 HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[attIndex]);
3240 if (FAILED(hr)) {
3241 qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
3242 return false;
3243 }
3244 ownsRtv[attIndex] = true;
3245 if (attIndex == 0) {
3246 d.pixelSize = texD->pixelSize();
3247 d.sampleCount = int(texD->sampleDesc.Count);
3248 }
3249 } else if (rb) {
3250 QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
3251 ownsRtv[attIndex] = false;
3252 rtv[attIndex] = rbD->rtv;
3253 if (attIndex == 0) {
3254 d.pixelSize = rbD->pixelSize();
3255 d.sampleCount = int(rbD->sampleDesc.Count);
3256 }
3257 }
3258 }
3259 d.dpr = 1;
3260
3261 if (hasDepthStencil) {
3262 if (m_desc.depthTexture()) {
3263 ownsDsv = true;
3264 QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture());
3265 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
3266 memset(&dsvDesc, 0, sizeof(dsvDesc));
3267 dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
3268 dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
3269 : D3D11_DSV_DIMENSION_TEXTURE2D;
3270 HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
3271 if (FAILED(hr)) {
3272 qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
3273 return false;
3274 }
3275 if (d.colorAttCount == 0) {
3276 d.pixelSize = depthTexD->pixelSize();
3277 d.sampleCount = int(depthTexD->sampleDesc.Count);
3278 }
3279 } else {
3280 ownsDsv = false;
3281 QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer());
3282 dsv = depthRbD->dsv;
3283 if (d.colorAttCount == 0) {
3284 d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
3285 d.sampleCount = int(depthRbD->sampleDesc.Count);
3286 }
3287 }
3288 d.dsAttCount = 1;
3289 } else {
3290 d.dsAttCount = 0;
3291 }
3292
3293 for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
3294 d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr;
3295
3296 d.dsv = dsv;
3297 d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
3298
3299 rhiD->registerResource(this);
3300 return true;
3301 }
3302
pixelSize() const3303 QSize QD3D11TextureRenderTarget::pixelSize() const
3304 {
3305 return d.pixelSize;
3306 }
3307
devicePixelRatio() const3308 float QD3D11TextureRenderTarget::devicePixelRatio() const
3309 {
3310 return d.dpr;
3311 }
3312
sampleCount() const3313 int QD3D11TextureRenderTarget::sampleCount() const
3314 {
3315 return d.sampleCount;
3316 }
3317
QD3D11ShaderResourceBindings(QRhiImplementation * rhi)3318 QD3D11ShaderResourceBindings::QD3D11ShaderResourceBindings(QRhiImplementation *rhi)
3319 : QRhiShaderResourceBindings(rhi)
3320 {
3321 }
3322
~QD3D11ShaderResourceBindings()3323 QD3D11ShaderResourceBindings::~QD3D11ShaderResourceBindings()
3324 {
3325 release();
3326 }
3327
release()3328 void QD3D11ShaderResourceBindings::release()
3329 {
3330 sortedBindings.clear();
3331 boundResourceData.clear();
3332 }
3333
build()3334 bool QD3D11ShaderResourceBindings::build()
3335 {
3336 if (!sortedBindings.isEmpty())
3337 release();
3338
3339 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
3340 std::sort(sortedBindings.begin(), sortedBindings.end(),
3341 [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
3342 {
3343 return a.data()->binding < b.data()->binding;
3344 });
3345
3346 boundResourceData.resize(sortedBindings.count());
3347
3348 for (BoundResourceData &bd : boundResourceData)
3349 memset(&bd, 0, sizeof(BoundResourceData));
3350
3351 generation += 1;
3352 return true;
3353 }
3354
QD3D11GraphicsPipeline(QRhiImplementation * rhi)3355 QD3D11GraphicsPipeline::QD3D11GraphicsPipeline(QRhiImplementation *rhi)
3356 : QRhiGraphicsPipeline(rhi)
3357 {
3358 }
3359
~QD3D11GraphicsPipeline()3360 QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline()
3361 {
3362 release();
3363 }
3364
release()3365 void QD3D11GraphicsPipeline::release()
3366 {
3367 QRHI_RES_RHI(QRhiD3D11);
3368
3369 if (!dsState)
3370 return;
3371
3372 dsState->Release();
3373 dsState = nullptr;
3374
3375 if (blendState) {
3376 blendState->Release();
3377 blendState = nullptr;
3378 }
3379
3380 if (inputLayout) {
3381 inputLayout->Release();
3382 inputLayout = nullptr;
3383 }
3384
3385 if (rastState) {
3386 rastState->Release();
3387 rastState = nullptr;
3388 }
3389
3390 if (vs.shader) {
3391 vs.shader->Release();
3392 vs.shader = nullptr;
3393 }
3394 vs.nativeResourceBindingMap.clear();
3395
3396 if (fs.shader) {
3397 fs.shader->Release();
3398 fs.shader = nullptr;
3399 }
3400 fs.nativeResourceBindingMap.clear();
3401
3402 rhiD->unregisterResource(this);
3403 }
3404
toD3DCullMode(QRhiGraphicsPipeline::CullMode c)3405 static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
3406 {
3407 switch (c) {
3408 case QRhiGraphicsPipeline::None:
3409 return D3D11_CULL_NONE;
3410 case QRhiGraphicsPipeline::Front:
3411 return D3D11_CULL_FRONT;
3412 case QRhiGraphicsPipeline::Back:
3413 return D3D11_CULL_BACK;
3414 default:
3415 Q_UNREACHABLE();
3416 return D3D11_CULL_NONE;
3417 }
3418 }
3419
toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)3420 static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
3421 {
3422 switch (op) {
3423 case QRhiGraphicsPipeline::Never:
3424 return D3D11_COMPARISON_NEVER;
3425 case QRhiGraphicsPipeline::Less:
3426 return D3D11_COMPARISON_LESS;
3427 case QRhiGraphicsPipeline::Equal:
3428 return D3D11_COMPARISON_EQUAL;
3429 case QRhiGraphicsPipeline::LessOrEqual:
3430 return D3D11_COMPARISON_LESS_EQUAL;
3431 case QRhiGraphicsPipeline::Greater:
3432 return D3D11_COMPARISON_GREATER;
3433 case QRhiGraphicsPipeline::NotEqual:
3434 return D3D11_COMPARISON_NOT_EQUAL;
3435 case QRhiGraphicsPipeline::GreaterOrEqual:
3436 return D3D11_COMPARISON_GREATER_EQUAL;
3437 case QRhiGraphicsPipeline::Always:
3438 return D3D11_COMPARISON_ALWAYS;
3439 default:
3440 Q_UNREACHABLE();
3441 return D3D11_COMPARISON_ALWAYS;
3442 }
3443 }
3444
toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)3445 static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
3446 {
3447 switch (op) {
3448 case QRhiGraphicsPipeline::StencilZero:
3449 return D3D11_STENCIL_OP_ZERO;
3450 case QRhiGraphicsPipeline::Keep:
3451 return D3D11_STENCIL_OP_KEEP;
3452 case QRhiGraphicsPipeline::Replace:
3453 return D3D11_STENCIL_OP_REPLACE;
3454 case QRhiGraphicsPipeline::IncrementAndClamp:
3455 return D3D11_STENCIL_OP_INCR_SAT;
3456 case QRhiGraphicsPipeline::DecrementAndClamp:
3457 return D3D11_STENCIL_OP_DECR_SAT;
3458 case QRhiGraphicsPipeline::Invert:
3459 return D3D11_STENCIL_OP_INVERT;
3460 case QRhiGraphicsPipeline::IncrementAndWrap:
3461 return D3D11_STENCIL_OP_INCR;
3462 case QRhiGraphicsPipeline::DecrementAndWrap:
3463 return D3D11_STENCIL_OP_DECR;
3464 default:
3465 Q_UNREACHABLE();
3466 return D3D11_STENCIL_OP_KEEP;
3467 }
3468 }
3469
toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)3470 static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
3471 {
3472 switch (format) {
3473 case QRhiVertexInputAttribute::Float4:
3474 return DXGI_FORMAT_R32G32B32A32_FLOAT;
3475 case QRhiVertexInputAttribute::Float3:
3476 return DXGI_FORMAT_R32G32B32_FLOAT;
3477 case QRhiVertexInputAttribute::Float2:
3478 return DXGI_FORMAT_R32G32_FLOAT;
3479 case QRhiVertexInputAttribute::Float:
3480 return DXGI_FORMAT_R32_FLOAT;
3481 case QRhiVertexInputAttribute::UNormByte4:
3482 return DXGI_FORMAT_R8G8B8A8_UNORM;
3483 case QRhiVertexInputAttribute::UNormByte2:
3484 return DXGI_FORMAT_R8G8_UNORM;
3485 case QRhiVertexInputAttribute::UNormByte:
3486 return DXGI_FORMAT_R8_UNORM;
3487 default:
3488 Q_UNREACHABLE();
3489 return DXGI_FORMAT_R32G32B32A32_FLOAT;
3490 }
3491 }
3492
toD3DTopology(QRhiGraphicsPipeline::Topology t)3493 static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t)
3494 {
3495 switch (t) {
3496 case QRhiGraphicsPipeline::Triangles:
3497 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
3498 case QRhiGraphicsPipeline::TriangleStrip:
3499 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
3500 case QRhiGraphicsPipeline::Lines:
3501 return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
3502 case QRhiGraphicsPipeline::LineStrip:
3503 return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
3504 case QRhiGraphicsPipeline::Points:
3505 return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
3506 default:
3507 Q_UNREACHABLE();
3508 return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
3509 }
3510 }
3511
toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)3512 static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
3513 {
3514 UINT8 f = 0;
3515 if (c.testFlag(QRhiGraphicsPipeline::R))
3516 f |= D3D11_COLOR_WRITE_ENABLE_RED;
3517 if (c.testFlag(QRhiGraphicsPipeline::G))
3518 f |= D3D11_COLOR_WRITE_ENABLE_GREEN;
3519 if (c.testFlag(QRhiGraphicsPipeline::B))
3520 f |= D3D11_COLOR_WRITE_ENABLE_BLUE;
3521 if (c.testFlag(QRhiGraphicsPipeline::A))
3522 f |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
3523 return f;
3524 }
3525
toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f,bool rgb)3526 static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
3527 {
3528 // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
3529 // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
3530 // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
3531 // set as an alpha src/dest factor), but for D3D we have to take care of it
3532 // ourselves. Hence the rgb argument.
3533
3534 switch (f) {
3535 case QRhiGraphicsPipeline::Zero:
3536 return D3D11_BLEND_ZERO;
3537 case QRhiGraphicsPipeline::One:
3538 return D3D11_BLEND_ONE;
3539 case QRhiGraphicsPipeline::SrcColor:
3540 return rgb ? D3D11_BLEND_SRC_COLOR : D3D11_BLEND_SRC_ALPHA;
3541 case QRhiGraphicsPipeline::OneMinusSrcColor:
3542 return rgb ? D3D11_BLEND_INV_SRC_COLOR : D3D11_BLEND_INV_SRC_ALPHA;
3543 case QRhiGraphicsPipeline::DstColor:
3544 return rgb ? D3D11_BLEND_DEST_COLOR : D3D11_BLEND_DEST_ALPHA;
3545 case QRhiGraphicsPipeline::OneMinusDstColor:
3546 return rgb ? D3D11_BLEND_INV_DEST_COLOR : D3D11_BLEND_INV_DEST_ALPHA;
3547 case QRhiGraphicsPipeline::SrcAlpha:
3548 return D3D11_BLEND_SRC_ALPHA;
3549 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
3550 return D3D11_BLEND_INV_SRC_ALPHA;
3551 case QRhiGraphicsPipeline::DstAlpha:
3552 return D3D11_BLEND_DEST_ALPHA;
3553 case QRhiGraphicsPipeline::OneMinusDstAlpha:
3554 return D3D11_BLEND_INV_DEST_ALPHA;
3555 case QRhiGraphicsPipeline::ConstantColor:
3556 case QRhiGraphicsPipeline::ConstantAlpha:
3557 return D3D11_BLEND_BLEND_FACTOR;
3558 case QRhiGraphicsPipeline::OneMinusConstantColor:
3559 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
3560 return D3D11_BLEND_INV_BLEND_FACTOR;
3561 case QRhiGraphicsPipeline::SrcAlphaSaturate:
3562 return D3D11_BLEND_SRC_ALPHA_SAT;
3563 case QRhiGraphicsPipeline::Src1Color:
3564 return rgb ? D3D11_BLEND_SRC1_COLOR : D3D11_BLEND_SRC1_ALPHA;
3565 case QRhiGraphicsPipeline::OneMinusSrc1Color:
3566 return rgb ? D3D11_BLEND_INV_SRC1_COLOR : D3D11_BLEND_INV_SRC1_ALPHA;
3567 case QRhiGraphicsPipeline::Src1Alpha:
3568 return D3D11_BLEND_SRC1_ALPHA;
3569 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
3570 return D3D11_BLEND_INV_SRC1_ALPHA;
3571 default:
3572 Q_UNREACHABLE();
3573 return D3D11_BLEND_ZERO;
3574 }
3575 }
3576
toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)3577 static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
3578 {
3579 switch (op) {
3580 case QRhiGraphicsPipeline::Add:
3581 return D3D11_BLEND_OP_ADD;
3582 case QRhiGraphicsPipeline::Subtract:
3583 return D3D11_BLEND_OP_SUBTRACT;
3584 case QRhiGraphicsPipeline::ReverseSubtract:
3585 return D3D11_BLEND_OP_REV_SUBTRACT;
3586 case QRhiGraphicsPipeline::Min:
3587 return D3D11_BLEND_OP_MIN;
3588 case QRhiGraphicsPipeline::Max:
3589 return D3D11_BLEND_OP_MAX;
3590 default:
3591 Q_UNREACHABLE();
3592 return D3D11_BLEND_OP_ADD;
3593 }
3594 }
3595
resolveD3DCompile()3596 static pD3DCompile resolveD3DCompile()
3597 {
3598 for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) {
3599 QSystemLibrary library(libraryName);
3600 if (library.load()) {
3601 if (auto symbol = library.resolve("D3DCompile"))
3602 return reinterpret_cast<pD3DCompile>(symbol);
3603 }
3604 }
3605 return nullptr;
3606 }
3607
compileHlslShaderSource(const QShader & shader,QShader::Variant shaderVariant,QString * error,QShaderKey * usedShaderKey)3608 static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey)
3609 {
3610 QShaderKey key = { QShader::DxbcShader, 50, shaderVariant };
3611 QShaderCode dxbc = shader.shader(key);
3612 if (!dxbc.shader().isEmpty()) {
3613 if (usedShaderKey)
3614 *usedShaderKey = key;
3615 return dxbc.shader();
3616 }
3617
3618 key = { QShader::HlslShader, 50, shaderVariant };
3619 QShaderCode hlslSource = shader.shader(key);
3620 if (hlslSource.shader().isEmpty()) {
3621 qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
3622 return QByteArray();
3623 }
3624
3625 const char *target;
3626 switch (shader.stage()) {
3627 case QShader::VertexStage:
3628 target = "vs_5_0";
3629 break;
3630 case QShader::TessellationControlStage:
3631 target = "hs_5_0";
3632 break;
3633 case QShader::TessellationEvaluationStage:
3634 target = "ds_5_0";
3635 break;
3636 case QShader::GeometryStage:
3637 target = "gs_5_0";
3638 break;
3639 case QShader::FragmentStage:
3640 target = "ps_5_0";
3641 break;
3642 case QShader::ComputeStage:
3643 target = "cs_5_0";
3644 break;
3645 default:
3646 Q_UNREACHABLE();
3647 return QByteArray();
3648 }
3649
3650 static const pD3DCompile d3dCompile = resolveD3DCompile();
3651 if (d3dCompile == nullptr) {
3652 qWarning("Unable to resolve function D3DCompile()");
3653 return QByteArray();
3654 }
3655
3656 ID3DBlob *bytecode = nullptr;
3657 ID3DBlob *errors = nullptr;
3658 HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
3659 nullptr, nullptr, nullptr,
3660 hlslSource.entryPoint().constData(), target, 0, 0, &bytecode, &errors);
3661 if (FAILED(hr) || !bytecode) {
3662 qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
3663 if (errors) {
3664 *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
3665 int(errors->GetBufferSize()));
3666 errors->Release();
3667 }
3668 return QByteArray();
3669 }
3670
3671 if (usedShaderKey)
3672 *usedShaderKey = key;
3673
3674 QByteArray result;
3675 result.resize(int(bytecode->GetBufferSize()));
3676 memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
3677 bytecode->Release();
3678 return result;
3679 }
3680
build()3681 bool QD3D11GraphicsPipeline::build()
3682 {
3683 if (dsState)
3684 release();
3685
3686 QRHI_RES_RHI(QRhiD3D11);
3687 if (!rhiD->sanityCheckGraphicsPipeline(this))
3688 return false;
3689
3690 D3D11_RASTERIZER_DESC rastDesc;
3691 memset(&rastDesc, 0, sizeof(rastDesc));
3692 rastDesc.FillMode = D3D11_FILL_SOLID;
3693 rastDesc.CullMode = toD3DCullMode(m_cullMode);
3694 rastDesc.FrontCounterClockwise = m_frontFace == CCW;
3695 rastDesc.DepthBias = m_depthBias;
3696 rastDesc.SlopeScaledDepthBias = m_slopeScaledDepthBias;
3697 rastDesc.DepthClipEnable = true;
3698 rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
3699 rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1;
3700 HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
3701 if (FAILED(hr)) {
3702 qWarning("Failed to create rasterizer state: %s", qPrintable(comErrorMessage(hr)));
3703 return false;
3704 }
3705
3706 D3D11_DEPTH_STENCIL_DESC dsDesc;
3707 memset(&dsDesc, 0, sizeof(dsDesc));
3708 dsDesc.DepthEnable = m_depthTest;
3709 dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
3710 dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
3711 dsDesc.StencilEnable = m_stencilTest;
3712 if (m_stencilTest) {
3713 dsDesc.StencilReadMask = UINT8(m_stencilReadMask);
3714 dsDesc.StencilWriteMask = UINT8(m_stencilWriteMask);
3715 dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
3716 dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
3717 dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
3718 dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
3719 dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
3720 dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
3721 dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
3722 dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
3723 }
3724 hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
3725 if (FAILED(hr)) {
3726 qWarning("Failed to create depth-stencil state: %s", qPrintable(comErrorMessage(hr)));
3727 return false;
3728 }
3729
3730 D3D11_BLEND_DESC blendDesc;
3731 memset(&blendDesc, 0, sizeof(blendDesc));
3732 blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1;
3733 for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
3734 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
3735 D3D11_RENDER_TARGET_BLEND_DESC blend;
3736 memset(&blend, 0, sizeof(blend));
3737 blend.BlendEnable = b.enable;
3738 blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
3739 blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
3740 blend.BlendOp = toD3DBlendOp(b.opColor);
3741 blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
3742 blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
3743 blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
3744 blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
3745 blendDesc.RenderTarget[i] = blend;
3746 }
3747 if (m_targetBlends.isEmpty()) {
3748 D3D11_RENDER_TARGET_BLEND_DESC blend;
3749 memset(&blend, 0, sizeof(blend));
3750 blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
3751 blendDesc.RenderTarget[0] = blend;
3752 }
3753 hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState);
3754 if (FAILED(hr)) {
3755 qWarning("Failed to create blend state: %s", qPrintable(comErrorMessage(hr)));
3756 return false;
3757 }
3758
3759 QByteArray vsByteCode;
3760 for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
3761 auto cacheIt = rhiD->m_shaderCache.constFind(shaderStage);
3762 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
3763 switch (shaderStage.type()) {
3764 case QRhiShaderStage::Vertex:
3765 vs.shader = static_cast<ID3D11VertexShader *>(cacheIt->s);
3766 vs.shader->AddRef();
3767 vsByteCode = cacheIt->bytecode;
3768 vs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
3769 break;
3770 case QRhiShaderStage::Fragment:
3771 fs.shader = static_cast<ID3D11PixelShader *>(cacheIt->s);
3772 fs.shader->AddRef();
3773 fs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
3774 break;
3775 default:
3776 break;
3777 }
3778 } else {
3779 QString error;
3780 QShaderKey shaderKey;
3781 const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error, &shaderKey);
3782 if (bytecode.isEmpty()) {
3783 qWarning("HLSL shader compilation failed: %s", qPrintable(error));
3784 return false;
3785 }
3786
3787 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES) {
3788 // Use the simplest strategy: too many cached shaders -> drop them all.
3789 rhiD->clearShaderCache();
3790 }
3791
3792 switch (shaderStage.type()) {
3793 case QRhiShaderStage::Vertex:
3794 hr = rhiD->dev->CreateVertexShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &vs.shader);
3795 if (FAILED(hr)) {
3796 qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
3797 return false;
3798 }
3799 vsByteCode = bytecode;
3800 if (const QShader::NativeResourceBindingMap *map = shaderStage.shader().nativeResourceBindingMap(shaderKey))
3801 vs.nativeResourceBindingMap = *map;
3802 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs.shader, bytecode, vs.nativeResourceBindingMap));
3803 vs.shader->AddRef();
3804 break;
3805 case QRhiShaderStage::Fragment:
3806 hr = rhiD->dev->CreatePixelShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &fs.shader);
3807 if (FAILED(hr)) {
3808 qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
3809 return false;
3810 }
3811 if (const QShader::NativeResourceBindingMap *map = shaderStage.shader().nativeResourceBindingMap(shaderKey))
3812 fs.nativeResourceBindingMap = *map;
3813 rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(fs.shader, bytecode, fs.nativeResourceBindingMap));
3814 fs.shader->AddRef();
3815 break;
3816 default:
3817 break;
3818 }
3819 }
3820 }
3821
3822 d3dTopology = toD3DTopology(m_topology);
3823
3824 if (!vsByteCode.isEmpty()) {
3825 QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
3826 for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
3827 it != itEnd; ++it)
3828 {
3829 D3D11_INPUT_ELEMENT_DESC desc;
3830 memset(&desc, 0, sizeof(desc));
3831 // the output from SPIRV-Cross uses TEXCOORD<location> as the semantic
3832 desc.SemanticName = "TEXCOORD";
3833 desc.SemanticIndex = UINT(it->location());
3834 desc.Format = toD3DAttributeFormat(it->format());
3835 desc.InputSlot = UINT(it->binding());
3836 desc.AlignedByteOffset = it->offset();
3837 const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
3838 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
3839 desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
3840 desc.InstanceDataStepRate = UINT(inputBinding->instanceStepRate());
3841 } else {
3842 desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
3843 }
3844 inputDescs.append(desc);
3845 }
3846 hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), UINT(inputDescs.count()),
3847 vsByteCode, SIZE_T(vsByteCode.size()), &inputLayout);
3848 if (FAILED(hr)) {
3849 qWarning("Failed to create input layout: %s", qPrintable(comErrorMessage(hr)));
3850 return false;
3851 }
3852 }
3853
3854 generation += 1;
3855 rhiD->registerResource(this);
3856 return true;
3857 }
3858
QD3D11ComputePipeline(QRhiImplementation * rhi)3859 QD3D11ComputePipeline::QD3D11ComputePipeline(QRhiImplementation *rhi)
3860 : QRhiComputePipeline(rhi)
3861 {
3862 }
3863
~QD3D11ComputePipeline()3864 QD3D11ComputePipeline::~QD3D11ComputePipeline()
3865 {
3866 release();
3867 }
3868
release()3869 void QD3D11ComputePipeline::release()
3870 {
3871 QRHI_RES_RHI(QRhiD3D11);
3872
3873 if (!cs.shader)
3874 return;
3875
3876 cs.shader->Release();
3877 cs.shader = nullptr;
3878 cs.nativeResourceBindingMap.clear();
3879
3880 rhiD->unregisterResource(this);
3881 }
3882
build()3883 bool QD3D11ComputePipeline::build()
3884 {
3885 if (cs.shader)
3886 release();
3887
3888 QRHI_RES_RHI(QRhiD3D11);
3889
3890 auto cacheIt = rhiD->m_shaderCache.constFind(m_shaderStage);
3891 if (cacheIt != rhiD->m_shaderCache.constEnd()) {
3892 cs.shader = static_cast<ID3D11ComputeShader *>(cacheIt->s);
3893 cs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
3894 } else {
3895 QString error;
3896 QShaderKey shaderKey;
3897 const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), &error, &shaderKey);
3898 if (bytecode.isEmpty()) {
3899 qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
3900 return false;
3901 }
3902
3903 HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &cs.shader);
3904 if (FAILED(hr)) {
3905 qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
3906 return false;
3907 }
3908
3909 if (const QShader::NativeResourceBindingMap *map = m_shaderStage.shader().nativeResourceBindingMap(shaderKey))
3910 cs.nativeResourceBindingMap = *map;
3911
3912 if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES)
3913 rhiD->clearShaderCache();
3914
3915 rhiD->m_shaderCache.insert(m_shaderStage, QRhiD3D11::Shader(cs.shader, bytecode, cs.nativeResourceBindingMap));
3916 }
3917
3918 cs.shader->AddRef();
3919
3920 generation += 1;
3921 rhiD->registerResource(this);
3922 return true;
3923 }
3924
QD3D11CommandBuffer(QRhiImplementation * rhi)3925 QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi)
3926 : QRhiCommandBuffer(rhi)
3927 {
3928 resetState();
3929 }
3930
~QD3D11CommandBuffer()3931 QD3D11CommandBuffer::~QD3D11CommandBuffer()
3932 {
3933 release();
3934 }
3935
release()3936 void QD3D11CommandBuffer::release()
3937 {
3938 // nothing to do here
3939 }
3940
QD3D11SwapChain(QRhiImplementation * rhi)3941 QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
3942 : QRhiSwapChain(rhi),
3943 rt(rhi),
3944 cb(rhi)
3945 {
3946 backBufferTex = nullptr;
3947 backBufferRtv = nullptr;
3948 for (int i = 0; i < BUFFER_COUNT; ++i) {
3949 msaaTex[i] = nullptr;
3950 msaaRtv[i] = nullptr;
3951 timestampActive[i] = false;
3952 timestampDisjointQuery[i] = nullptr;
3953 timestampQuery[2 * i] = nullptr;
3954 timestampQuery[2 * i + 1] = nullptr;
3955 }
3956 }
3957
~QD3D11SwapChain()3958 QD3D11SwapChain::~QD3D11SwapChain()
3959 {
3960 release();
3961 }
3962
releaseBuffers()3963 void QD3D11SwapChain::releaseBuffers()
3964 {
3965 if (backBufferRtv) {
3966 backBufferRtv->Release();
3967 backBufferRtv = nullptr;
3968 }
3969 if (backBufferTex) {
3970 backBufferTex->Release();
3971 backBufferTex = nullptr;
3972 }
3973 for (int i = 0; i < BUFFER_COUNT; ++i) {
3974 if (msaaRtv[i]) {
3975 msaaRtv[i]->Release();
3976 msaaRtv[i] = nullptr;
3977 }
3978 if (msaaTex[i]) {
3979 msaaTex[i]->Release();
3980 msaaTex[i] = nullptr;
3981 }
3982 }
3983 }
3984
release()3985 void QD3D11SwapChain::release()
3986 {
3987 if (!swapChain)
3988 return;
3989
3990 releaseBuffers();
3991
3992 for (int i = 0; i < BUFFER_COUNT; ++i) {
3993 if (timestampDisjointQuery[i]) {
3994 timestampDisjointQuery[i]->Release();
3995 timestampDisjointQuery[i] = nullptr;
3996 }
3997 for (int j = 0; j < 2; ++j) {
3998 const int idx = BUFFER_COUNT * i + j;
3999 if (timestampQuery[idx]) {
4000 timestampQuery[idx]->Release();
4001 timestampQuery[idx] = nullptr;
4002 }
4003 }
4004 }
4005
4006 swapChain->Release();
4007 swapChain = nullptr;
4008
4009 QRHI_PROF;
4010 QRHI_PROF_F(releaseSwapChain(this));
4011
4012 QRHI_RES_RHI(QRhiD3D11);
4013 rhiD->unregisterResource(this);
4014 }
4015
currentFrameCommandBuffer()4016 QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer()
4017 {
4018 return &cb;
4019 }
4020
currentFrameRenderTarget()4021 QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget()
4022 {
4023 return &rt;
4024 }
4025
surfacePixelSize()4026 QSize QD3D11SwapChain::surfacePixelSize()
4027 {
4028 Q_ASSERT(m_window);
4029 return m_window->size() * m_window->devicePixelRatio();
4030 }
4031
newCompatibleRenderPassDescriptor()4032 QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor()
4033 {
4034 return new QD3D11RenderPassDescriptor(m_rhi);
4035 }
4036
newColorBuffer(const QSize & size,DXGI_FORMAT format,DXGI_SAMPLE_DESC sampleDesc,ID3D11Texture2D ** tex,ID3D11RenderTargetView ** rtv) const4037 bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
4038 ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
4039 {
4040 D3D11_TEXTURE2D_DESC desc;
4041 memset(&desc, 0, sizeof(desc));
4042 desc.Width = UINT(size.width());
4043 desc.Height = UINT(size.height());
4044 desc.MipLevels = 1;
4045 desc.ArraySize = 1;
4046 desc.Format = format;
4047 desc.SampleDesc = sampleDesc;
4048 desc.Usage = D3D11_USAGE_DEFAULT;
4049 desc.BindFlags = D3D11_BIND_RENDER_TARGET;
4050
4051 QRHI_RES_RHI(QRhiD3D11);
4052 HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex);
4053 if (FAILED(hr)) {
4054 qWarning("Failed to create color buffer texture: %s", qPrintable(comErrorMessage(hr)));
4055 return false;
4056 }
4057
4058 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
4059 memset(&rtvDesc, 0, sizeof(rtvDesc));
4060 rtvDesc.Format = format;
4061 rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
4062 hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv);
4063 if (FAILED(hr)) {
4064 qWarning("Failed to create color buffer rtv: %s", qPrintable(comErrorMessage(hr)));
4065 (*tex)->Release();
4066 *tex = nullptr;
4067 return false;
4068 }
4069
4070 return true;
4071 }
4072
buildOrResize()4073 bool QD3D11SwapChain::buildOrResize()
4074 {
4075 // Can be called multiple times due to window resizes - that is not the
4076 // same as a simple release+build (as with other resources). Just need to
4077 // resize the buffers then.
4078
4079 const bool needsRegistration = !window || window != m_window;
4080
4081 // except if the window actually changes
4082 if (window && window != m_window)
4083 release();
4084
4085 window = m_window;
4086 m_currentPixelSize = surfacePixelSize();
4087 pixelSize = m_currentPixelSize;
4088
4089 if (pixelSize.isEmpty())
4090 return false;
4091
4092 colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
4093 const DXGI_FORMAT srgbAdjustedFormat = m_flags.testFlag(sRGB) ?
4094 DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
4095
4096 const UINT swapChainFlags = 0;
4097
4098 QRHI_RES_RHI(QRhiD3D11);
4099 bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain;
4100 if (!swapChain) {
4101 HWND hwnd = reinterpret_cast<HWND>(window->winId());
4102 sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
4103
4104 // Take a shortcut for alpha: our QWindow is OpenGLSurface so whatever
4105 // the platform plugin does to enable transparency for OpenGL window
4106 // will be sufficient for us too on the legacy (DISCARD) path. For
4107 // FLIP_DISCARD we'd need to use DirectComposition (create a
4108 // IDCompositionDevice/Target/Visual), avoid that for now.
4109 if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
4110 useFlipDiscard = false;
4111 if (window->requestedFormat().alphaBufferSize() <= 0)
4112 qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
4113 "This may lead to problems.");
4114 }
4115
4116 HRESULT hr;
4117 if (useFlipDiscard) {
4118 // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
4119 // old DISCARD with back buffer count == 1). This makes no difference for
4120 // the rest of the stuff except that automatic MSAA is unsupported and
4121 // needs to be implemented via a custom multisample render target and an
4122 // explicit resolve.
4123
4124 DXGI_SWAP_CHAIN_DESC1 desc;
4125 memset(&desc, 0, sizeof(desc));
4126 desc.Width = UINT(pixelSize.width());
4127 desc.Height = UINT(pixelSize.height());
4128 desc.Format = colorFormat;
4129 desc.SampleDesc.Count = 1;
4130 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4131 desc.BufferCount = BUFFER_COUNT;
4132 desc.Scaling = DXGI_SCALING_STRETCH;
4133 desc.SwapEffect = DXGI_SWAP_EFFECT(4); // DXGI_SWAP_EFFECT_FLIP_DISCARD
4134 // Do not bother with AlphaMode, if won't work unless we go through
4135 // DirectComposition. Instead, we just take the other (DISCARD)
4136 // path for now when alpha is requested.
4137 desc.Flags = swapChainFlags;
4138
4139 IDXGISwapChain1 *sc1;
4140 hr = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory)->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc,
4141 nullptr, nullptr, &sc1);
4142 if (SUCCEEDED(hr))
4143 swapChain = sc1;
4144 } else {
4145 // Windows 7 for instance. Use DISCARD mode. Regardless, keep on
4146 // using our manual resolve for symmetry with the FLIP_DISCARD code
4147 // path when MSAA is requested.
4148
4149 DXGI_SWAP_CHAIN_DESC desc;
4150 memset(&desc, 0, sizeof(desc));
4151 desc.BufferDesc.Width = UINT(pixelSize.width());
4152 desc.BufferDesc.Height = UINT(pixelSize.height());
4153 desc.BufferDesc.RefreshRate.Numerator = 60;
4154 desc.BufferDesc.RefreshRate.Denominator = 1;
4155 desc.BufferDesc.Format = colorFormat;
4156 desc.SampleDesc.Count = 1;
4157 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4158 desc.BufferCount = 1;
4159 desc.OutputWindow = hwnd;
4160 desc.Windowed = true;
4161 desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
4162 desc.Flags = swapChainFlags;
4163
4164 hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
4165 }
4166 if (FAILED(hr)) {
4167 qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
4168 return false;
4169 }
4170 rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
4171 } else {
4172 releaseBuffers();
4173 const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
4174 HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
4175 colorFormat, swapChainFlags);
4176 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
4177 qWarning("Device loss detected in ResizeBuffers()");
4178 rhiD->deviceLost = true;
4179 return false;
4180 } else if (FAILED(hr)) {
4181 qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
4182 return false;
4183 }
4184 }
4185
4186 // This looks odd (for FLIP_DISCARD, esp. compared with backends for Vulkan
4187 // & co.) but the backbuffer is always at index 0, with magic underneath.
4188 // Some explanation from
4189 // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
4190 //
4191 // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
4192 // Every call to Present implicitly changed the resource identity of the
4193 // returned interface. Direct3D 12 no longer supports that implicit
4194 // resource identity change, due to the CPU overhead required and the
4195 // flexible resource descriptor design. As a result, the application must
4196 // manually call GetBuffer for every each buffer created with the
4197 // swapchain."
4198
4199 // So just query index 0 once (per resize) and be done with it.
4200 HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&backBufferTex));
4201 if (FAILED(hr)) {
4202 qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
4203 return false;
4204 }
4205 D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
4206 memset(&rtvDesc, 0, sizeof(rtvDesc));
4207 rtvDesc.Format = srgbAdjustedFormat;
4208 rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
4209 hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
4210 if (FAILED(hr)) {
4211 qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
4212 return false;
4213 }
4214
4215 // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
4216 for (int i = 0; i < BUFFER_COUNT; ++i) {
4217 if (sampleDesc.Count > 1) {
4218 if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
4219 return false;
4220 }
4221 }
4222
4223 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
4224 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
4225 m_depthStencil->sampleCount(), m_sampleCount);
4226 }
4227 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
4228 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
4229 m_depthStencil->setPixelSize(pixelSize);
4230 if (!m_depthStencil->build())
4231 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
4232 pixelSize.width(), pixelSize.height());
4233 } else {
4234 qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
4235 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
4236 pixelSize.width(), pixelSize.height());
4237 }
4238 }
4239
4240 currentFrameSlot = 0;
4241 frameCount = 0;
4242 ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
4243 swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
4244
4245 QD3D11ReferenceRenderTarget *rtD = QRHI_RES(QD3D11ReferenceRenderTarget, &rt);
4246 rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
4247 rtD->d.pixelSize = pixelSize;
4248 rtD->d.dpr = float(window->devicePixelRatio());
4249 rtD->d.sampleCount = int(sampleDesc.Count);
4250 rtD->d.colorAttCount = 1;
4251 rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
4252
4253 QRHI_PROF;
4254 QRHI_PROF_F(resizeSwapChain(this, BUFFER_COUNT, sampleDesc.Count > 1 ? BUFFER_COUNT : 0, int(sampleDesc.Count)));
4255 if (rhiP) {
4256 D3D11_QUERY_DESC queryDesc;
4257 memset(&queryDesc, 0, sizeof(queryDesc));
4258 for (int i = 0; i < BUFFER_COUNT; ++i) {
4259 if (!timestampDisjointQuery[i]) {
4260 queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
4261 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, ×tampDisjointQuery[i]);
4262 if (FAILED(hr)) {
4263 qWarning("Failed to create timestamp disjoint query: %s", qPrintable(comErrorMessage(hr)));
4264 break;
4265 }
4266 }
4267 queryDesc.Query = D3D11_QUERY_TIMESTAMP;
4268 for (int j = 0; j < 2; ++j) {
4269 const int idx = BUFFER_COUNT * i + j; // one pair per buffer (frame)
4270 if (!timestampQuery[idx]) {
4271 HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, ×tampQuery[idx]);
4272 if (FAILED(hr)) {
4273 qWarning("Failed to create timestamp query: %s", qPrintable(comErrorMessage(hr)));
4274 break;
4275 }
4276 }
4277 }
4278 }
4279 // timestamp queries are optional so we can go on even if they failed
4280 }
4281
4282 if (needsRegistration)
4283 rhiD->registerResource(this);
4284
4285 return true;
4286 }
4287
initResources()4288 void QRhiD3D11::DeviceCurse::initResources()
4289 {
4290 framesLeft = framesToActivate;
4291
4292 HRESULT hr = q->dev->CreateComputeShader(g_killDeviceByTimingOut, sizeof(g_killDeviceByTimingOut), nullptr, &cs);
4293 if (FAILED(hr)) {
4294 qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
4295 return;
4296 }
4297 }
4298
releaseResources()4299 void QRhiD3D11::DeviceCurse::releaseResources()
4300 {
4301 if (cs) {
4302 cs->Release();
4303 cs = nullptr;
4304 }
4305 }
4306
activate()4307 void QRhiD3D11::DeviceCurse::activate()
4308 {
4309 if (!cs)
4310 return;
4311
4312 qDebug("Activating Curse. Goodbye Cruel World.");
4313
4314 q->context->CSSetShader(cs, nullptr, 0);
4315 q->context->Dispatch(256, 1, 1);
4316 }
4317
4318 QT_END_NAMESPACE
4319