Warning: That file was not part of the compilation database. It may have many parsing errors.

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