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, &params);
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, &timestamps[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, &timestamps[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(&timestampSwapChain->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, &timestampDisjointQuery[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, &timestampQuery[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