summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp214
-rw-r--r--src/gui/rhi/qrhi_p.h7
-rw-r--r--src/gui/rhi/qrhi_p_p.h4
-rw-r--r--src/gui/rhi/qrhid3d11.cpp205
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h12
-rw-r--r--src/gui/rhi/qrhigles2.cpp179
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h25
-rw-r--r--src/gui/rhi/qrhimetal.mm15
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h4
-rw-r--r--src/gui/rhi/qrhinull.cpp15
-rw-r--r--src/gui/rhi/qrhinull_p_p.h4
-rw-r--r--src/gui/rhi/qrhiprofiler.cpp9
-rw-r--r--src/gui/rhi/qrhivulkan.cpp532
-rw-r--r--src/gui/rhi/qrhivulkan_p.h5
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h57
-rw-r--r--src/gui/rhi/qshader.cpp12
-rw-r--r--src/gui/rhi/qshaderdescription.cpp18
17 files changed, 918 insertions, 399 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 7443c0a04f..4414b61d55 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -59,7 +59,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhi
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Accelerated 2D/3D graphics API abstraction.
@@ -274,6 +275,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
QRhi does not expose APIs for resource barriers or image layout
transitions. Such synchronization is done implicitly by the backends, where
applicable (for example, Vulkan), by tracking resource usage as necessary.
+ Buffer and image barriers are inserted before render or compute passes
+ transparently to the application.
\note Resources within a render or compute pass are expected to be bound to
a single usage during that pass. For example, a buffer can be used as
@@ -505,8 +508,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
to issue a \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} with a
non-aligned effective offset may lead to unspecified behavior.
- \value NPOTTextureRepeat Indicates that the \l{QRhiSampler::Repeat}{Repeat}
- mode is supported for textures with a non-power-of-two size.
+ \value NPOTTextureRepeat Indicates that the
+ \l{QRhiSampler::Repeat}{Repeat} wrap mode and mipmap filtering modes are
+ supported for textures with a non-power-of-two size. In practice this can
+ only be false with OpenGL ES 2.0 implementations without
+ \c{GL_OES_texture_npot}.
\value RedOrAlpha8IsRed Indicates that the
\l{QRhiTexture::RED_OR_ALPHA8}{RED_OR_ALPHA8} format maps to a one
@@ -550,6 +556,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\enum QRhi::BeginFrameFlag
Flag values for QRhi::beginFrame()
+
+ \value ExternalContentsInPass Specifies that one or more render or compute
+ passes in this frame will call QRhiCommandBuffer::beginExternal(). Some
+ backends, Vulkan in particular, will fail if this flag is not set and
+ beginExternal() is still called.
*/
/*!
@@ -601,7 +612,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhiInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for backend-specific initialization parameters.
Contains fields that are relevant to all backends.
@@ -609,7 +621,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhiDepthStencilClearValue
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies clear values for a depth or stencil buffer.
*/
@@ -676,7 +689,8 @@ QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
/*!
\class QRhiViewport
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies a viewport rectangle.
Used with QRhiCommandBuffer::setViewport().
@@ -775,7 +789,8 @@ QDebug operator<<(QDebug dbg, const QRhiViewport &v)
/*!
\class QRhiScissor
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies a scissor rectangle.
Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is
@@ -854,7 +869,8 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
/*!
\class QRhiVertexInputBinding
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a vertex input binding.
Specifies the stride (in bytes, must be a multiple of 4), the
@@ -984,7 +1000,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
/*!
\class QRhiVertexInputAttribute
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a single vertex input element.
The members specify the binding number, location, format, and offset for a
@@ -1137,7 +1154,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
/*!
\class QRhiVertexInputLayout
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the layout of vertex inputs consumed by a vertex shader.
The vertex input layout is defined by the collections of
@@ -1196,7 +1214,8 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
/*!
\class QRhiShaderStage
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the type and the shader code for a shader stage in the pipeline.
*/
@@ -1280,7 +1299,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
/*!
\class QRhiColorAttachment
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the a single color attachment of a render target.
A color attachment is either a QRhiTexture or a QRhiRenderBuffer. The
@@ -1338,7 +1358,8 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
/*!
\class QRhiTextureRenderTargetDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the color and depth or depth/stencil attachments of a render target.
A texture render target has zero or more textures as color attachments,
@@ -1393,7 +1414,8 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
/*!
\class QRhiTextureSubresourceUploadDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the source for one mip level in a layer in a texture upload operation.
The source content is specified either as a QImage or as a raw blob. The
@@ -1473,7 +1495,8 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
/*!
\class QRhiTextureUploadEntry
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes one layer (face for cubemaps) in a texture upload operation.
*/
@@ -1501,7 +1524,8 @@ QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
/*!
\class QRhiTextureUploadDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a texture upload operation.
Used with QRhiResourceUpdateBatch::uploadTexture(). That function has two
@@ -1606,7 +1630,8 @@ void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
/*!
\class QRhiTextureCopyDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a texture-to-texture copy operation.
An empty pixelSize() indicates that the entire subresource is to be copied.
@@ -1629,7 +1654,8 @@ void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
/*!
\class QRhiReadbackDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a readback (reading back texture contents from possibly GPU-only memory) operation.
The source of the readback operation is either a QRhiTexture or the
@@ -1675,7 +1701,8 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
/*!
\class QRhiReadbackResult
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the results of a potentially asynchronous readback operation.
When \l completed is set, the function is invoked when the \l data is
@@ -1685,13 +1712,15 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
/*!
\class QRhiNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for classes exposing backend-specific collections of native resource objects.
*/
/*!
\class QRhiResource
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Base class for classes encapsulating native resource objects.
*/
@@ -1810,7 +1839,8 @@ quint64 QRhiResource::globalResourceId() const
/*!
\class QRhiBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Vertex, index, or uniform (constant) buffer resource.
*/
@@ -1911,7 +1941,8 @@ QRhiResource::Type QRhiBuffer::resourceType() const
/*!
\class QRhiRenderBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Renderbuffer resource.
Renderbuffers cannot be sampled or read but have some benefits over
@@ -1987,7 +2018,8 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const
/*!
\class QRhiTexture
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Texture resource.
*/
@@ -2155,7 +2187,8 @@ bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
/*!
\class QRhiSampler
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Sampler resource.
*/
@@ -2217,8 +2250,13 @@ QRhiResource::Type QRhiSampler::resourceType() const
/*!
\class QRhiRenderPassDescriptor
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Render pass resource.
+
+ A render pass, if such a concept exists in the underlying graphics API, is
+ a collection of attachments (color, depth, stencil) and describes how those
+ attachments are used.
*/
/*!
@@ -2238,8 +2276,21 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
}
/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanRenderPassNativeHandles. The returned value is null when exposing
+ the underlying native resources is not supported by the backend.
+
+ \sa QRhiVulkanRenderPassNativeHandles
+ */
+const QRhiNativeHandles *QRhiRenderPassDescriptor::nativeHandles()
+{
+ return nullptr;
+}
+
+/*!
\class QRhiRenderTarget
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Represents an onscreen (swapchain) or offscreen (texture) render target.
*/
@@ -2276,7 +2327,8 @@ QRhiResource::Type QRhiRenderTarget::resourceType() const
/*!
\class QRhiTextureRenderTarget
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Texture render target resource.
A texture render target allows rendering into one or more textures,
@@ -2393,7 +2445,8 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
/*!
\class QRhiShaderResourceBindings
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders.
A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding
@@ -2510,7 +2563,8 @@ bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBind
/*!
\class QRhiShaderResourceBinding
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the shader resource for a single binding point.
A QRhiShaderResourceBinding cannot be constructed directly. Instead, use
@@ -3035,7 +3089,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Graphics pipeline state resource.
\note Setting the shader resource bindings is mandatory. The referenced
@@ -3190,7 +3245,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline::TargetBlend
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the blend state for one color attachment.
Defaults to color write enabled, blending disabled. The blend values are
@@ -3200,7 +3256,8 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline::StencilOpState
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the stencil operation state.
*/
@@ -3255,7 +3312,8 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
/*!
\class QRhiSwapChain
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Swapchain resource.
A swapchain enables presenting rendering results to a surface. A swapchain
@@ -3517,7 +3575,8 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
/*!
\class QRhiComputePipeline
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Compute pipeline state resource.
\note Setting the shader resource bindings is mandatory. The referenced
@@ -3545,7 +3604,8 @@ QRhiComputePipeline::QRhiComputePipeline(QRhiImplementation *rhi)
/*!
\class QRhiCommandBuffer
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Command buffer resource.
Not creatable by applications at the moment. The only ways to obtain a
@@ -3592,6 +3652,42 @@ QRhiResource::Type QRhiCommandBuffer::resourceType() const
return CommandBuffer;
}
+#ifndef QT_NO_DEBUG
+static const char *resourceTypeStr(QRhiResource *res)
+{
+ switch (res->resourceType()) {
+ case QRhiResource::Buffer:
+ return "Buffer";
+ case QRhiResource::Texture:
+ return "Texture";
+ case QRhiResource::Sampler:
+ return "Sampler";
+ case QRhiResource::RenderBuffer:
+ return "RenderBuffer";
+ case QRhiResource::RenderPassDescriptor:
+ return "RenderPassDescriptor";
+ case QRhiResource::RenderTarget:
+ return "RenderTarget";
+ case QRhiResource::TextureRenderTarget:
+ return "TextureRenderTarget";
+ case QRhiResource::ShaderResourceBindings:
+ return "ShaderResourceBindings";
+ case QRhiResource::GraphicsPipeline:
+ return "GraphicsPipeline";
+ case QRhiResource::SwapChain:
+ return "SwapChain";
+ case QRhiResource::ComputePipeline:
+ return "ComputePipeline";
+ case QRhiResource::CommandBuffer:
+ return "CommandBuffer";
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return "";
+}
+#endif
+
QRhiImplementation::~QRhiImplementation()
{
qDeleteAll(resUpdPool);
@@ -3601,10 +3697,10 @@ QRhiImplementation::~QRhiImplementation()
// and freak out for unfreed graphics objects in the derived dtor already.
#ifndef QT_NO_DEBUG
if (!resources.isEmpty()) {
- qWarning("QRhi %p going down with %d unreleased resources. This is not nice.",
+ qWarning("QRhi %p going down with %d unreleased resources that own native graphics objects. This is not nice.",
q, resources.count());
for (QRhiResource *res : qAsConst(resources)) {
- qWarning(" Resource %p (%s)", res, res->m_objectName.constData());
+ qWarning(" %s resource %p (%s)", resourceTypeStr(res), res, res->m_objectName.constData());
res->m_rhi = nullptr;
}
}
@@ -3969,7 +4065,8 @@ void QRhi::runCleanup()
/*!
\class QRhiResourceUpdateBatch
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Records upload and copy type of operations.
With QRhi it is no longer possible to perform copy type of operations at
@@ -4711,6 +4808,11 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
enqueue commands to the current pass' command buffer by calling graphics
API functions directly.
+ \note This is only available when the intent was declared up front in
+ beginFrame(). Therefore this function must only be called when the frame
+ was started with specifying QRhi::ExternalContentsInPass in the flags
+ passed to QRhi::beginFrame().
+
With Vulkan or Metal one can query the native command buffer or encoder
objects via nativeHandles() and enqueue commands to them. With OpenGL or
Direct3D 11 the (device) context can be retrieved from
@@ -4728,6 +4830,16 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
functions (\c set* or \c draw*) must be called on the
QRhiCommandBuffer until endExternal().
+ \warning Some backends may return a native command buffer object from
+ QRhiCommandBuffer::nativeHandles() that is different from the primary one
+ when inside a beginExternal() - endExternal() block. Therefore it is
+ important to (re)query the native command buffer object after calling
+ beginExternal(). In practical terms this means that with Vulkan for example
+ the externally recorded Vulkan commands are placed onto a secondary command
+ buffer (with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT).
+ nativeHandles() returns this secondary command buffer when called between
+ begin/endExternal.
+
\sa endExternal(), nativeHandles()
*/
void QRhiCommandBuffer::beginExternal()
@@ -5034,6 +5146,13 @@ QRhiSwapChain *QRhi::newSwapChain()
/*!
Starts a new frame targeting the next available buffer of \a swapChain.
+ A frame consists of resource updates and one or more render and compute
+ passes.
+
+ \a flags can indicate certain special cases. For example, the fact that
+ QRhiCommandBuffer::beginExternal() will be called within this new frame
+ must be declared up front by setting the ExternalContentsInPass flag.
+
The high level pattern of rendering into a QWindow using a swapchain:
\list
@@ -5061,9 +5180,7 @@ QRhiSwapChain *QRhi::newSwapChain()
\endlist
- \a flags is currently unused.
-
- \sa endFrame()
+ \sa endFrame(), beginOffscreenFrame()
*/
QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags)
{
@@ -5163,7 +5280,8 @@ int QRhi::currentFrameSlot() const
/*!
Starts a new offscreen frame. Provides a command buffer suitable for
- recording rendering commands in \a cb.
+ recording rendering commands in \a cb. \a flags is used to indicate
+ certain special cases, just like with beginFrame().
\note The QRhiCommandBuffer stored to *cb is not owned by the caller.
@@ -5197,14 +5315,14 @@ int QRhi::currentFrameSlot() const
// image data available in rbResult
\endcode
- \sa endOffscreenFrame()
+ \sa endOffscreenFrame(), beginFrame()
*/
-QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags)
{
if (d->inFrame)
qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored");
- QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess;
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb, flags) : FrameOpSuccess;
if (r == FrameOpSuccess)
d->inFrame = true;
@@ -5216,12 +5334,12 @@ QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
\sa beginOffscreenFrame()
*/
-QRhi::FrameOpResult QRhi::endOffscreenFrame()
+QRhi::FrameOpResult QRhi::endOffscreenFrame(EndFrameFlags flags)
{
if (!d->inFrame)
qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored");
- QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess;
+ QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame(flags) : FrameOpSuccess;
d->inFrame = false;
qDeleteAll(d->pendingReleaseAndDestroyResources);
d->pendingReleaseAndDestroyResources.clear();
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index df30817ef4..2d36c19e99 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -843,6 +843,8 @@ class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
public:
QRhiResource::Type resourceType() const override;
+ virtual const QRhiNativeHandles *nativeHandles();
+
protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi);
};
@@ -1324,6 +1326,7 @@ public:
};
enum BeginFrameFlag {
+ ExternalContentsInPass = 0x01
};
Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
@@ -1384,8 +1387,8 @@ public:
bool isRecordingFrame() const;
int currentFrameSlot() const;
- FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb);
- FrameOpResult endOffscreenFrame();
+ FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags = BeginFrameFlags());
+ FrameOpResult endOffscreenFrame(EndFrameFlags flags = EndFrameFlags());
QRhi::FrameOpResult finish();
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index b592fe82f2..0914cf268b 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -95,8 +95,8 @@ public:
virtual QRhiSwapChain *createSwapChain() = 0;
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
- virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
- virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
+ virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
virtual QRhi::FrameOpResult finish() = 0;
virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 0c5df600a0..3e136cdb80 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11InitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Direct3D 11 specific initialization parameters.
A D3D11-based QRhi needs no special parameters for initialization. If
@@ -97,7 +98,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11NativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the D3D device and device context used by the QRhi.
\note The class uses \c{void *} as the type since including the COM-based
@@ -107,7 +109,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiD3D11TextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the D3D texture object that is backing a QRhiTexture instance.
\note The class uses \c{void *} as the type since including the COM-based
@@ -157,6 +160,36 @@ static inline Int aligned(Int v, Int byteAlign)
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
+static IDXGIFactory1 *createDXGIFactory2()
+{
+ IDXGIFactory1 *result = nullptr;
+ if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
+ using PtrCreateDXGIFactory2 = HRESULT (WINAPI *)(UINT, REFIID, void **);
+ QSystemLibrary dxgilib(QStringLiteral("dxgi"));
+ if (auto createDXGIFactory2 = (PtrCreateDXGIFactory2)dxgilib.resolve("CreateDXGIFactory2")) {
+ const HRESULT hr = createDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&result));
+ if (FAILED(hr)) {
+ qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ result = nullptr;
+ }
+ } else {
+ qWarning("Unable to resolve CreateDXGIFactory2()");
+ }
+ }
+ return result;
+}
+
+static IDXGIFactory1 *createDXGIFactory1()
+{
+ IDXGIFactory1 *result = nullptr;
+ const HRESULT hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&result));
+ if (FAILED(hr)) {
+ qWarning("CreateDXGIFactory1() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ result = nullptr;
+ }
+ return result;
+}
+
bool QRhiD3D11::create(QRhi::Flags flags)
{
Q_UNUSED(flags);
@@ -165,19 +198,22 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (debugLayer)
devFlags |= D3D11_CREATE_DEVICE_DEBUG;
- HRESULT hr;
-#if !defined(Q_CC_MINGW)
- hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7;
- if (hasDxgi2)
- hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&dxgiFactory));
- else
-#endif
- hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&dxgiFactory));
+ dxgiFactory = createDXGIFactory2();
+ if (dxgiFactory != nullptr) {
+ hasDxgi2 = true;
+ supportsFlipDiscardSwapchain = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
+ && !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
+ } else {
+ dxgiFactory = createDXGIFactory1();
+ hasDxgi2 = false;
+ supportsFlipDiscardSwapchain = false;
+ }
- if (FAILED(hr)) {
- qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ if (dxgiFactory == nullptr)
return false;
- }
+
+ qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s",
+ hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false");
if (!importedDevice) {
IDXGIAdapter1 *adapterToUse = nullptr;
@@ -837,8 +873,10 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
- flushCommandBuffer();
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ // no timestampSwapChain, in order to avoid timestamp mess
+ executeCommandBuffer(cbD);
+ cbD->resetCommands();
}
void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
@@ -891,7 +929,7 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->cb.resetState();
swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
- swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot];
+ swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
QRHI_PROF_F(beginSwapChainFrame(swapChain));
@@ -920,7 +958,7 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
executeCommandBuffer(&swapChainD->cb);
if (swapChainD->sampleDesc.Count > 1) {
- context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0,
+ context->ResolveSubresource(swapChainD->backBufferTex, 0,
swapChainD->msaaTex[currentFrameSlot], 0,
swapChainD->colorFormat);
}
@@ -953,8 +991,9 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
ofr.active = true;
ofr.cbWrapper.resetState();
@@ -963,8 +1002,9 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame()
+QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
ofr.active = false;
executeCommandBuffer(&ofr.cbWrapper);
@@ -1099,27 +1139,25 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format)
QRhi::FrameOpResult QRhiD3D11::finish()
{
- if (inFrame)
- flushCommandBuffer();
+ if (inFrame) {
+ if (ofr.active) {
+ Q_ASSERT(!contextState.currentSwapChain);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass);
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
+ executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
+ contextState.currentSwapChain->cb.resetCommands();
+ }
+ }
finishActiveReadbacks();
return QRhi::FrameOpSuccess;
}
-void QRhiD3D11::flushCommandBuffer()
-{
- if (ofr.active) {
- Q_ASSERT(!contextState.currentSwapChain);
- executeCommandBuffer(&ofr.cbWrapper);
- ofr.cbWrapper.resetCommands();
- } else {
- Q_ASSERT(contextState.currentSwapChain);
- executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
- contextState.currentSwapChain->cb.resetCommands();
- }
-}
-
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
{
@@ -1304,14 +1342,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
// has to be supported. Insert a resolve.
QD3D11CommandBuffer::Command rcmd;
rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
- rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex;
rcmd.args.resolveSubRes.dstSubRes = 0;
rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
rcmd.args.resolveSubRes.srcSubRes = 0;
rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
cbD->commands.append(rcmd);
}
- src = swapChainD->tex[swapChainD->currentFrameSlot];
+ src = swapChainD->backBufferTex;
dxgiFormat = swapChainD->colorFormat;
pixelSize = swapChainD->pixelSize;
format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
@@ -3503,9 +3541,9 @@ QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
rt(rhi),
cb(rhi)
{
+ backBufferTex = nullptr;
+ backBufferRtv = nullptr;
for (int i = 0; i < BUFFER_COUNT; ++i) {
- tex[i] = nullptr;
- rtv[i] = nullptr;
msaaTex[i] = nullptr;
msaaRtv[i] = nullptr;
timestampActive[i] = false;
@@ -3522,15 +3560,15 @@ QD3D11SwapChain::~QD3D11SwapChain()
void QD3D11SwapChain::releaseBuffers()
{
+ if (backBufferRtv) {
+ backBufferRtv->Release();
+ backBufferRtv = nullptr;
+ }
+ if (backBufferTex) {
+ backBufferTex->Release();
+ backBufferTex = nullptr;
+ }
for (int i = 0; i < BUFFER_COUNT; ++i) {
- if (rtv[i]) {
- rtv[i]->Release();
- rtv[i] = nullptr;
- }
- if (tex[i]) {
- tex[i]->Release();
- tex[i] = nullptr;
- }
if (msaaRtv[i]) {
msaaRtv[i]->Release();
msaaRtv[i] = nullptr;
@@ -3656,18 +3694,19 @@ bool QD3D11SwapChain::buildOrResize()
const UINT swapChainFlags = 0;
QRHI_RES_RHI(QRhiD3D11);
+ const bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain;
if (!swapChain) {
HWND hwnd = reinterpret_cast<HWND>(window->winId());
sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
- // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
- // old DISCARD with back buffer count == 1). This makes no difference for
- // the rest of the stuff except that automatic MSAA is unsupported and
- // needs to be implemented via a custom multisample render target and an
- // explicit resolve.
-
HRESULT hr;
- if (rhiD->hasDxgi2) {
+ if (useFlipDiscard) {
+ // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
+ // old DISCARD with back buffer count == 1). This makes no difference for
+ // the rest of the stuff except that automatic MSAA is unsupported and
+ // needs to be implemented via a custom multisample render target and an
+ // explicit resolve.
+
DXGI_SWAP_CHAIN_DESC1 desc;
memset(&desc, 0, sizeof(desc));
desc.Width = pixelSize.width();
@@ -3690,7 +3729,10 @@ bool QD3D11SwapChain::buildOrResize()
if (SUCCEEDED(hr))
swapChain = sc1;
} else {
- // Windows 7
+ // Windows 7 for instance. Use DISCARD mode. Regardless, keep on
+ // using our manual resolve for symmetry with the FLIP_DISCARD code
+ // path when MSAA is requested.
+
DXGI_SWAP_CHAIN_DESC desc;
memset(&desc, 0, sizeof(desc));
desc.BufferDesc.Width = pixelSize.width();
@@ -3700,10 +3742,10 @@ bool QD3D11SwapChain::buildOrResize()
desc.BufferDesc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- desc.BufferCount = BUFFER_COUNT;
+ desc.BufferCount = 1;
desc.OutputWindow = hwnd;
desc.Windowed = true;
- desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = swapChainFlags;
hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
@@ -3712,30 +3754,49 @@ bool QD3D11SwapChain::buildOrResize()
qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
+ rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
} else {
releaseBuffers();
- HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags);
+ const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
+ HRESULT hr = swapChain->ResizeBuffers(count, pixelSize.width(), pixelSize.height(),
+ colorFormat, swapChainFlags);
if (FAILED(hr)) {
qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
}
+ // This looks odd (for FLIP_DISCARD, esp. compared with backends for Vulkan
+ // & co.) but the backbuffer is always at index 0, with magic underneath.
+ // Some explanation from
+ // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
+ //
+ // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
+ // Every call to Present implicitly changed the resource identity of the
+ // returned interface. Direct3D 12 no longer supports that implicit
+ // resource identity change, due to the CPU overhead required and the
+ // flexible resource descriptor design. As a result, the application must
+ // manually call GetBuffer for every each buffer created with the
+ // swapchain."
+
+ // So just query index 0 once (per resize) and be done with it.
+ HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&backBufferTex));
+ if (FAILED(hr)) {
+ qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = srgbAdjustedFormat;
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
for (int i = 0; i < BUFFER_COUNT; ++i) {
- HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&tex[i]));
- if (FAILED(hr)) {
- qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
- return false;
- }
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
- rtvDesc.Format = srgbAdjustedFormat;
- rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
- hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]);
- if (FAILED(hr)) {
- qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
- return false;
- }
if (sampleDesc.Count > 1) {
if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
return false;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 34c9ff70f8..582146315d 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -473,9 +473,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
imageRetainPool.clear();
}
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+ resetCommands();
resetCachedState();
}
void resetCachedState() {
@@ -523,8 +523,8 @@ struct QD3D11SwapChain : public QRhiSwapChain
DXGI_FORMAT colorFormat;
IDXGISwapChain *swapChain = nullptr;
static const int BUFFER_COUNT = 2;
- ID3D11Texture2D *tex[BUFFER_COUNT];
- ID3D11RenderTargetView *rtv[BUFFER_COUNT];
+ ID3D11Texture2D *backBufferTex;
+ ID3D11RenderTargetView *backBufferRtv;
ID3D11Texture2D *msaaTex[BUFFER_COUNT];
ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
DXGI_SAMPLE_DESC sampleDesc;
@@ -569,8 +569,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -633,7 +633,6 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
- void flushCommandBuffer();
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
@@ -656,6 +655,7 @@ public:
ID3DUserDefinedAnnotation *annotations = nullptr;
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
+ bool supportsFlipDiscardSwapchain = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
struct {
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 379801efbd..f4e711e33e 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiGles2InitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief OpenGL specific initialization parameters.
An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
@@ -130,13 +131,15 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiGles2NativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the OpenGL context used by the QRhi.
*/
/*!
\class QRhiGles2TextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
*/
@@ -268,6 +271,10 @@ QT_BEGIN_NAMESPACE
#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
#endif
+#ifndef GL_POINT_SPRITE
+#define GL_POINT_SPRITE 0x8861
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -424,8 +431,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
&& f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
- caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
- caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
+ && f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
if (caps.gles)
@@ -472,9 +479,15 @@ bool QRhiGles2::create(QRhi::Flags flags)
else
caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
- if (!caps.gles)
+ if (caps.gles)
+ caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
+ else
+ caps.textureCompareMode = true;
+
+ if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
- // else (with gles) this is always on
+ f->glEnable(GL_POINT_SPRITE);
+ } // else (with gles) these are always on
nativeHandlesStruct.context = ctx;
@@ -547,43 +560,6 @@ int QRhiGles2::effectiveSampleCount(int sampleCount) const
return s;
}
-static inline bool isPowerOfTwo(int x)
-{
- // Assumption: x >= 1
- return x == (x & -x);
-}
-
-QSize QRhiGles2::safeTextureSize(const QSize &pixelSize) const
-{
- QSize size = pixelSize.isEmpty() ? QSize(1, 1) : pixelSize;
-
- if (!caps.npotTexture) {
- if (!isPowerOfTwo(size.width())) {
- qWarning("Texture width %d is not a power of two, adjusting",
- size.width());
- size.setWidth(qNextPowerOfTwo(size.width()));
- }
- if (!isPowerOfTwo(size.height())) {
- qWarning("Texture height %d is not a power of two, adjusting",
- size.height());
- size.setHeight(qNextPowerOfTwo(size.height()));
- }
- }
-
- if (size.width() > caps.maxTextureSize) {
- qWarning("Texture width %d exceeds maximum width %d, adjusting",
- size.width(), caps.maxTextureSize);
- size.setWidth(caps.maxTextureSize);
- }
- if (size.height() > caps.maxTextureSize) {
- qWarning("Texture height %d exceeds maximum height %d, adjusting",
- size.height(), caps.maxTextureSize);
- size.setHeight(caps.maxTextureSize);
- }
-
- return size;
-}
-
QRhiSwapChain *QRhiGles2::createSwapChain()
{
return new QGles2SwapChain(this);
@@ -725,7 +701,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
return true;
case QRhi::NPOTTextureRepeat:
- return caps.npotTextureRepeat;
+ return caps.npotTextureFull;
case QRhi::RedOrAlpha8IsRed:
return caps.coreProfile;
case QRhi::ElementIndexUint:
@@ -1118,15 +1094,36 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
- flushCommandBuffer(); // also ensures the context is current
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ if (!ensureContext())
+ return;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ if (!ensureContext(currentSwapChain->surface))
+ return;
+ }
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ executeCommandBuffer(cbD);
+ cbD->resetCommands();
}
void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
- Q_ASSERT(cbD->commands.isEmpty());
+ Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
+
cbD->resetCachedState();
+
+ if (cbD->recordingPass != QGles2CommandBuffer::NoPass) {
+ // Commands that come after this point need a resource tracker and also
+ // a BarriersForPass command enqueued. (the ones we had from
+ // beginPass() are now gone since beginExternal() processed all that
+ // due to calling executeCommandBuffer()).
+ enqueueBarriersForPass(cbD);
+ }
+
if (cbD->currentTarget)
enqueueBindFramebuffer(cbD->currentTarget, cbD);
}
@@ -1187,8 +1184,9 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
if (!ensureContext())
return QRhi::FrameOpError;
@@ -1203,8 +1201,9 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
+QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(ofr.active);
ofr.active = false;
@@ -1220,23 +1219,22 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
QRhi::FrameOpResult QRhiGles2::finish()
{
- return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess;
-}
-
-QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
-{
- if (ofr.active) {
- Q_ASSERT(!currentSwapChain);
- if (!ensureContext())
- return QRhi::FrameOpError;
- executeCommandBuffer(&ofr.cbWrapper);
- ofr.cbWrapper.resetCommands();
- } else {
- Q_ASSERT(currentSwapChain);
- if (!ensureContext(currentSwapChain->surface))
- return QRhi::FrameOpError;
- executeCommandBuffer(&currentSwapChain->cb);
- currentSwapChain->cb.resetCommands();
+ if (inFrame) {
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(currentSwapChain);
+ Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
+ if (!ensureContext(currentSwapChain->surface))
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&currentSwapChain->cb);
+ currentSwapChain->cb.resetCommands();
+ }
}
return QRhi::FrameOpSuccess;
}
@@ -2376,11 +2374,13 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, samplerD->d.glwrapt);
// 3D textures not supported by GLES 2.0 or by us atm...
//f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
- if (samplerD->d.gltexcomparefunc != GL_NEVER) {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
- } else {
- f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ if (caps.textureCompareMode) {
+ if (samplerD->d.gltexcomparefunc != GL_NEVER) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
+ } else {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ }
}
texD->samplerState = samplerD->d;
}
@@ -2505,6 +2505,16 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
return rtD;
}
+void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
+{
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
+ cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
+ cbD->commands.append(cmd);
+}
+
void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
@@ -2519,12 +2529,7 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
// Get a new resource tracker. Then add a command that will generate
// glMemoryBarrier() calls based on that tracker when submitted.
- cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
- QGles2CommandBuffer::Command cmd;
- cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
- cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
- cbD->commands.append(cmd);
+ enqueueBarriersForPass(cbD);
bool wantsColorClear, wantsDsClear;
QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
@@ -2600,12 +2605,7 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
if (resourceUpdates)
enqueueResourceUpdates(cb, resourceUpdates);
- cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
- QGles2CommandBuffer::Command cmd;
- cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
- cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
- cbD->commands.append(cmd);
+ enqueueBarriersForPass(cbD);
cbD->recordingPass = QGles2CommandBuffer::ComputePass;
@@ -2935,7 +2935,7 @@ bool QGles2RenderBuffer::build()
rhiD->f->glGenRenderbuffers(1, &renderbuffer);
rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
- const QSize size = rhiD->safeTextureSize(m_pixelSize);
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
switch (m_type) {
case QRhiRenderBuffer::DepthStencil:
@@ -3032,7 +3032,7 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
if (!rhiD->ensureContext())
return false;
- const QSize size = rhiD->safeTextureSize(m_pixelSize);
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
@@ -3145,18 +3145,19 @@ bool QGles2Texture::build()
for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
for (int level = 0; level != mipLevelCount; ++level) {
const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
- rhiD->f->glTexImage2D(faceTargetBase + layer, level, glsizedintformat,
+ rhiD->f->glTexImage2D(faceTargetBase + layer, level, glintformat,
mipSize.width(), mipSize.height(), 0,
glformat, gltype, nullptr);
}
}
} else {
- rhiD->f->glTexImage2D(target, 0, glsizedintformat, size.width(), size.height(),
+ rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(),
0, glformat, gltype, nullptr);
}
} else {
// Must be specified with immutable storage functions otherwise
- // bindImageTexture may fail.
+ // bindImageTexture may fail. Also, the internal format must be a
+ // sized format here.
rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
}
specified = true;
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 88a6144b42..6da529be92 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -525,19 +525,16 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
}
void resetCommands() {
commands.clear();
- // beginExternal() can lead to calling resetCommands() inside a pass,
- // hence the condition
- if (recordingPass == NoPass) {
- passResTrackers.clear();
- currentPassResTrackerIndex = -1;
- }
dataRetainPool.clear();
imageRetainPool.clear();
+
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
}
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+ resetCommands();
resetCachedState();
}
void resetCachedState() {
@@ -605,8 +602,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -671,7 +668,6 @@ public:
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
- QRhi::FrameOpResult flushCommandBuffer();
void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
@@ -692,8 +688,8 @@ public:
const uint *dynOfsPairs, int dynOfsCount);
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
+ void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
int effectiveSampleCount(int sampleCount) const;
- QSize safeTextureSize(const QSize &size) const;
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage,
QShaderDescription *desc, int *glslVersionUsed);
bool linkProgram(GLuint program);
@@ -717,8 +713,7 @@ public:
maxTextureSize(2048),
maxDrawBuffers(4),
msaaRenderBuffer(false),
- npotTexture(true),
- npotTextureRepeat(true),
+ npotTextureFull(true),
gles(false),
fixedIndexPrimitiveRestart(false),
bgraExternalFormat(false),
@@ -747,8 +742,7 @@ public:
// Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
// the same as multisample textures!
uint msaaRenderBuffer : 1;
- uint npotTexture : 1;
- uint npotTextureRepeat : 1;
+ uint npotTextureFull : 1;
uint gles : 1;
uint fixedIndexPrimitiveRestart : 1;
uint bgraExternalFormat : 1;
@@ -768,6 +762,7 @@ public:
uint instancing : 1;
uint baseVertex : 1;
uint compute : 1;
+ uint textureCompareMode : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index ffb2283ae7..07753c985c 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -1243,8 +1243,10 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
+
currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
if (swapchains.count() > 1) {
for (QMetalSwapChain *sc : qAsConst(swapchains)) {
@@ -1268,8 +1270,9 @@ QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiMetal::endOffscreenFrame()
+QRhi::FrameOpResult QRhiMetal::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(d->ofr.active);
d->ofr.active = false;
@@ -1986,9 +1989,11 @@ bool QMetalBuffer::build()
}
#endif
- // Immutable and Static only has buf[0] and pendingUpdates[0] in use.
- // Dynamic uses all.
- d->slotted = m_type == Dynamic;
+ // Have QMTL_FRAMES_IN_FLIGHT versions regardless of the type, for now.
+ // This is because writing to a Managed buffer (which is what Immutable and
+ // Static maps to on macOS) is not safe when another frame reading from the
+ // same buffer is still in flight.
+ d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer); // except for SSBOs written in the shader
QRHI_RES_RHI(QRhiMetal);
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 8b0256991d..c448865f4d 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -354,8 +354,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 1314e53893..dff6e05268 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -41,7 +41,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Null backend specific initialization parameters.
A Null QRhi needs no special parameters for initialization.
@@ -60,13 +61,15 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Empty.
*/
/*!
\class QRhiNullTextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Empty.
*/
@@ -353,14 +356,16 @@ QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameF
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
+ Q_UNUSED(flags);
*cb = &offscreenCommandBuffer;
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiNull::endOffscreenFrame()
+QRhi::FrameOpResult QRhiNull::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
return QRhi::FrameOpSuccess;
}
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index b0227bc110..bdf0d59724 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -220,8 +220,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp
index 15e3007d49..e74e446a1c 100644
--- a/src/gui/rhi/qrhiprofiler.cpp
+++ b/src/gui/rhi/qrhiprofiler.cpp
@@ -41,7 +41,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Collects resource and timing information from an active QRhi.
@@ -142,7 +143,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler::CpuTime
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains CPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
@@ -155,7 +157,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler::GpuTime
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains GPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 61a1595a50..dfc85fb853 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -58,11 +58,34 @@ QT_BEGIN_NAMESPACE
and a separate, host visible staging buffer is used to upload data to them.
"Dynamic" buffers are in host visible memory and are duplicated (since there
can be 2 frames in flight). This is handled transparently to the application.
+
+ Barriers are generated automatically for each render or compute pass, based
+ on the resources that are used in that pass (in QRhiShaderResourceBindings,
+ vertex inputs, etc.). This implies deferring the recording of the command
+ buffer since the barriers have to be placed at the right place (before the
+ pass), and that can only be done once we know all the things the pass does.
+
+ This in turn has implications for integrating external commands
+ (beginExternal() - direct Vulkan calls - endExternal()) because that is
+ incompatible with this approach by nature. Therefore we support another mode
+ of operation, where each render or compute pass uses one or more secondary
+ command buffers (recorded right away), with each beginExternal() leading to
+ closing the current secondary cb, creating a new secondary cb for the
+ external content, and then starting yet another one in endExternal() for
+ whatever comes afterwards in the pass. This way the primary command buffer
+ only has vkCmdExecuteCommand(s) within a renderpass instance
+ (Begin-EndRenderPass). (i.e. our only subpass is then
+ VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
+ VK_SUBPASS_CONTENTS_INLINE)
+
+ The command buffer management mode is decided on a per frame basis,
+ controlled by the ExternalContentsInPass flag of beginFrame().
*/
/*!
\class QRhiVulkanInitParams
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Vulkan specific initialization parameters.
A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
@@ -146,7 +169,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
\note Ownership of the Vulkan objects is never transferred.
@@ -154,7 +178,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanTextureNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the Vulkan image object that is backing a QRhiTexture.
Importing and exporting Vulkan image objects that back a QRhiTexture when
@@ -168,7 +193,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanCommandBufferNativeHandles
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
\note The Vulkan command buffer object is only guaranteed to be valid, and
@@ -178,6 +204,13 @@ QT_BEGIN_NAMESPACE
\l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
*/
+/*!
+ \class QRhiVulkanRenderPassNativeHandles
+ \internal
+ \inmodule QtGui
+ \brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
+ */
+
static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
{
return (v + byteAlign - 1) & ~(byteAlign - 1);
@@ -1465,7 +1498,6 @@ static inline bool checkDeviceLost(VkResult err)
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
- Q_UNUSED(flags);
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
@@ -1514,7 +1546,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
if (frame.timestampQueryIndex >= 0) {
quint64 timestamp[2] = { 0, 0 };
VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
- 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT);
+ 2 * sizeof(quint64), timestamp, sizeof(quint64),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
frame.timestampQueryIndex = -1;
if (err == VK_SUCCESS) {
@@ -1536,7 +1569,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
}
// build new draw command buffer
- QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf);
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
if (cbres != QRhi::FrameOpSuccess)
return cbres;
@@ -1560,6 +1593,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
}
swapChainD->cbWrapper.cb = frame.cmdBuf;
+ swapChainD->cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
+
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
swapChainD->rtWrapper.d.fb = image.fb;
@@ -1580,7 +1615,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
- recordCommandBuffer(&swapChainD->cbWrapper);
+ recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
@@ -1624,10 +1659,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
// stop recording and submit to the queue
Q_ASSERT(!frame.cmdFenceWaitable);
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf,
- frame.cmdFence,
- frame.imageSemWaitable ? &frame.imageSem : nullptr,
- needsPresent ? &frame.drawSem : nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(frame.cmdBuf,
+ frame.cmdFence,
+ frame.imageSemWaitable ? &frame.imageSem : nullptr,
+ needsPresent ? &frame.drawSem : nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
@@ -1698,7 +1733,7 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
}
-QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
+QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
{
if (*cb) {
df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
@@ -1737,8 +1772,8 @@ QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
- VkSemaphore *waitSem, VkSemaphore *signalSem)
+QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem)
{
VkResult err = df->vkEndCommandBuffer(cb);
if (err != VK_SUCCESS) {
@@ -1789,9 +1824,9 @@ void QRhiVulkan::waitCommandCompletion(int frameSlot)
}
}
-QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
+QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{
- QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb);
+ QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
if (cbres != QRhi::FrameOpSuccess)
return cbres;
@@ -1808,6 +1843,8 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
if (swapchains.count() > 1)
waitCommandCompletion(currentFrameSlot);
+ ofr.cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
+
prepareNewFrame(&ofr.cbWrapper);
ofr.active = true;
@@ -1815,12 +1852,13 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess;
}
-QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
+QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
{
+ Q_UNUSED(flags);
Q_ASSERT(ofr.active);
ofr.active = false;
- recordCommandBuffer(&ofr.cbWrapper);
+ recordPrimaryCommandBuffer(&ofr.cbWrapper);
if (!ofr.cmdFence) {
VkFenceCreateInfo fenceInfo;
@@ -1833,7 +1871,7 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
}
}
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
@@ -1857,15 +1895,19 @@ QRhi::FrameOpResult QRhiVulkan::finish()
VkCommandBuffer cb;
if (ofr.active) {
Q_ASSERT(!currentSwapChain);
- recordCommandBuffer(&ofr.cbWrapper);
+ Q_ASSERT(ofr.cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
+ recordPrimaryCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
cb = ofr.cbWrapper.cb;
} else {
Q_ASSERT(currentSwapChain);
+ Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
swapChainD = currentSwapChain;
- recordCommandBuffer(&swapChainD->cbWrapper);
+ recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
+ swapChainD->cbWrapper.resetCommands();
cb = swapChainD->cbWrapper.cb;
}
- QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
+ QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess)
return submitres;
}
@@ -1875,9 +1917,9 @@ QRhi::FrameOpResult QRhiVulkan::finish()
if (inFrame) {
// Allocate and begin recording on a new command buffer.
if (ofr.active)
- startCommandBuffer(&ofr.cbWrapper.cb);
+ startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
else
- startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
+ startPrimaryCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
}
executeDeferredReleases(true);
@@ -1951,6 +1993,69 @@ void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
enqueueResourceUpdates(cbD, resourceUpdates);
}
+VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD)
+{
+ VkCommandBuffer secondaryCb;
+
+ VkCommandBufferAllocateInfo cmdBufInfo;
+ memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool;
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+ cmdBufInfo.commandBufferCount = 1;
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create secondary command buffer: %d", err);
+ return VK_NULL_HANDLE;
+ }
+
+ VkCommandBufferBeginInfo cmdBufBeginInfo;
+ memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
+ VkCommandBufferInheritanceInfo cmdBufInheritInfo;
+ memset(&cmdBufInheritInfo, 0, sizeof(cmdBufInheritInfo));
+ cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+ cmdBufInheritInfo.subpass = 0;
+ if (rtD) {
+ cmdBufInheritInfo.renderPass = rtD->rp->rp;
+ cmdBufInheritInfo.framebuffer = rtD->fb;
+ }
+ cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
+
+ err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to begin secondary command buffer: %d", err);
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &secondaryCb);
+ return VK_NULL_HANDLE;
+ }
+
+ return secondaryCb;
+}
+
+void QRhiVulkan::endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD)
+{
+ VkResult err = df->vkEndCommandBuffer(cb);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to end secondary command buffer: %d", err);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ExecuteSecondary;
+ cmd.args.executeSecondary.cb = cb;
+ cbD->commands.append(cmd);
+
+ deferredReleaseSecondaryCommandBuffer(cb);
+}
+
+void QRhiVulkan::deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb)
+{
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::CommandBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.commandBuffer.cb = cb;
+ releaseQueue.append(e);
+}
+
void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
@@ -2030,6 +2135,9 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
cbD->pools.clearValue.append(cvs.constData(), cvs.count());
cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb)
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer(rtD));
}
void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2037,6 +2145,13 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+ if (cbD->useSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+ cbD->resetCachedState();
+ }
+
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
cbD->commands.append(cmd);
@@ -2059,6 +2174,9 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
enqueueTransitionPassResources(cbD);
cbD->recordingPass = QVkCommandBuffer::ComputePass;
+
+ if (cbD->useSecondaryCb)
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer());
}
void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2066,6 +2184,13 @@ void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+ if (cbD->useSecondaryCb) {
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+ cbD->resetCachedState();
+ }
+
cbD->recordingPass = QVkCommandBuffer::NoPass;
if (resourceUpdates)
@@ -2080,11 +2205,15 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
- cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
- cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+ }
cbD->currentGraphicsPipeline = nullptr;
cbD->currentComputePipeline = ps;
@@ -2099,12 +2228,16 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::Dispatch;
- cmd.args.dispatch.x = x;
- cmd.args.dispatch.y = y;
- cmd.args.dispatch.z = z;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDispatch(cbD->secondaryCbs.last(), x, y, z);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Dispatch;
+ cmd.args.dispatch.x = x;
+ cmd.args.dispatch.y = y;
+ cmd.args.dispatch.z = z;
+ cbD->commands.append(cmd);
+ }
}
VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
@@ -3009,6 +3142,9 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
break;
+ case QRhiVulkan::DeferredReleaseEntry::CommandBuffer:
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &e.commandBuffer.cb);
+ break;
default:
Q_UNREACHABLE();
break;
@@ -3109,14 +3245,15 @@ VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
cbD->commands.append(cmd);
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
}
-void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
+void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
{
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
@@ -3161,7 +3298,8 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break;
case QVkCommandBuffer::Command::BeginRenderPass:
cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
- df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE);
+ df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
+ cbD->useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
break;
case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb);
@@ -3214,13 +3352,15 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break;
case QVkCommandBuffer::Command::DebugMarkerBegin:
cmd.args.debugMarkerBegin.marker.pMarkerName =
- cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData();
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData();
vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
break;
case QVkCommandBuffer::Command::DebugMarkerEnd:
vkCmdDebugMarkerEnd(cbD->cb);
break;
case QVkCommandBuffer::Command::DebugMarkerInsert:
+ cmd.args.debugMarkerInsert.marker.pMarkerName =
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData();
vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
break;
case QVkCommandBuffer::Command::TransitionPassResources:
@@ -3229,12 +3369,13 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::Dispatch:
df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
break;
+ case QVkCommandBuffer::Command::ExecuteSecondary:
+ df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
+ break;
default:
break;
}
}
-
- cbD->resetCommands();
}
static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
@@ -3651,11 +3792,15 @@ void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
- cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- cmd.args.bindPipeline.pipeline = psD->pipeline;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+ }
cbD->currentGraphicsPipeline = ps;
cbD->currentComputePipeline = nullptr;
@@ -3853,16 +3998,25 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
}
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
- cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
- : VK_PIPELINE_BIND_POINT_COMPUTE;
- cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
- cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
- cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
- cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
- cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindDescriptorSets(cbD->secondaryCbs.last(),
+ gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
+ gfxPsD ? gfxPsD->layout : compPsD->layout,
+ 0, 1, &srbD->descSets[descSetIdx],
+ dynOfs.count(),
+ dynOfs.count() ? dynOfs.constData() : nullptr);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
+ cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
+ : VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
+ cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
+ cbD->commands.append(cmd);
+ }
if (gfxPsD) {
cbD->currentGraphicsSrb = srb;
@@ -3918,15 +4072,20 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
QRhiPassResourceTracker::BufVertexInputStage);
}
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
- cmd.args.bindVertexBuffer.startBinding = startBinding;
- cmd.args.bindVertexBuffer.count = bufs.count();
- cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
- cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
- cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
- cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindVertexBuffers(cbD->secondaryCbs.last(), startBinding,
+ bufs.count(), bufs.constData(), ofs.constData());
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.startBinding = startBinding;
+ cmd.args.bindVertexBuffer.count = bufs.count();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
+ cbD->commands.append(cmd);
+ }
}
if (indexBuf) {
@@ -3949,12 +4108,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
cbD->currentIndexOffset = indexOffset;
cbD->currentIndexFormat = type;
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
- cmd.args.bindIndexBuffer.buf = vkindexbuf;
- cmd.args.bindIndexBuffer.ofs = indexOffset;
- cmd.args.bindIndexBuffer.type = type;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdBindIndexBuffer(cbD->secondaryCbs.last(), vkindexbuf, indexOffset, type);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buf = vkindexbuf;
+ cmd.args.bindIndexBuffer.ofs = indexOffset;
+ cmd.args.bindIndexBuffer.type = type;
+ cbD->commands.append(cmd);
+ }
trackedRegisterBuffer(&passResTracker, ibufD, slot,
QRhiPassResourceTracker::BufIndexRead,
@@ -3975,7 +4138,6 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
return;
QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetViewport;
VkViewport *vp = &cmd.args.setViewport.viewport;
vp->x = x;
vp->y = y;
@@ -3983,16 +4145,26 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
vp->height = h;
vp->minDepth = viewport.minDepth();
vp->maxDepth = viewport.maxDepth();
- cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetViewport(cbD->secondaryCbs.last(), 0, 1, vp);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetViewport;
+ cbD->commands.append(cmd);
+ }
if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
- cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x;
s->offset.y = y;
s->extent.width = w;
s->extent.height = h;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ cbD->commands.append(cmd);
+ }
}
}
@@ -4009,13 +4181,18 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
return;
QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x;
s->offset.y = y;
s->extent.width = w;
s->extent.height = h;
- cbD->commands.append(cmd);
+
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
+ } else {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
@@ -4023,13 +4200,18 @@ void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
- cmd.args.setBlendConstants.c[0] = c.redF();
- cmd.args.setBlendConstants.c[1] = c.greenF();
- cmd.args.setBlendConstants.c[2] = c.blueF();
- cmd.args.setBlendConstants.c[3] = c.alphaF();
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
+ df->vkCmdSetBlendConstants(cbD->secondaryCbs.last(), constants);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
+ cmd.args.setBlendConstants.c[0] = c.redF();
+ cmd.args.setBlendConstants.c[1] = c.greenF();
+ cmd.args.setBlendConstants.c[2] = c.blueF();
+ cmd.args.setBlendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
@@ -4037,10 +4219,14 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
- cmd.args.setStencilRef.ref = refValue;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdSetStencilReference(cbD->secondaryCbs.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
+ cmd.args.setStencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
@@ -4049,13 +4235,17 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::Draw;
- cmd.args.draw.vertexCount = vertexCount;
- cmd.args.draw.instanceCount = instanceCount;
- cmd.args.draw.firstVertex = firstVertex;
- cmd.args.draw.firstInstance = firstInstance;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDraw(cbD->secondaryCbs.last(), vertexCount, instanceCount, firstVertex, firstInstance);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Draw;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
@@ -4064,14 +4254,19 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
- cmd.args.drawIndexed.indexCount = indexCount;
- cmd.args.drawIndexed.instanceCount = instanceCount;
- cmd.args.drawIndexed.firstIndex = firstIndex;
- cmd.args.drawIndexed.vertexOffset = vertexOffset;
- cmd.args.drawIndexed.firstInstance = firstInstance;
- cbD->commands.append(cmd);
+ if (cbD->useSecondaryCb) {
+ df->vkCmdDrawIndexed(cbD->secondaryCbs.last(), indexCount, instanceCount,
+ firstIndex, vertexOffset, firstInstance);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
@@ -4084,12 +4279,17 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
- cmd.args.debugMarkerBegin.marker = marker;
- cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count();
- cbD->pools.debugMarkerName.append(name);
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ marker.pMarkerName = name.constData();
+ vkCmdDebugMarkerBegin(cbD->secondaryCbs.last(), &marker);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
+ cmd.args.debugMarkerBegin.marker = marker;
+ cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cbD->pools.debugMarkerData.append(name);
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
@@ -4098,9 +4298,13 @@ void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
return;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ vkCmdDebugMarkerEnd(cbD->secondaryCbs.last());
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
+ cbD->commands.append(cmd);
+ }
}
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
@@ -4111,13 +4315,19 @@ void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
VkDebugMarkerMarkerInfoEXT marker;
memset(&marker, 0, sizeof(marker));
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
- marker.pMarkerName = msg.constData();
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
- QVkCommandBuffer::Command cmd;
- cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
- cmd.args.debugMarkerInsert.marker = marker;
- cbD->commands.append(cmd);
+ if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
+ marker.pMarkerName = msg.constData();
+ vkCmdDebugMarkerInsert(cbD->secondaryCbs.last(), &marker);
+ } else {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
+ cmd.args.debugMarkerInsert.marker = marker;
+ cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cbD->pools.debugMarkerData.append(msg);
+ cbD->commands.append(cmd);
+ }
}
const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
@@ -4125,14 +4335,76 @@ const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
}
+static inline QVkRenderTargetData *maybeRenderTargetData(QVkCommandBuffer *cbD)
+{
+ Q_ASSERT(cbD->currentTarget);
+ QVkRenderTargetData *rtD = nullptr;
+ if (cbD->recordingPass == QVkCommandBuffer::RenderPass) {
+ switch (cbD->currentTarget->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QVkReferenceRenderTarget, cbD->currentTarget)->d;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ return rtD;
+}
+
void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
{
- Q_UNUSED(cb);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ // When not in a pass, it is simple: record what we have (but do not
+ // submit), the cb can then be used to record more external commands.
+ if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
+ recordPrimaryCommandBuffer(cbD);
+ cbD->resetCommands();
+ return;
+ }
+
+ // Otherwise, inside a pass, have a secondary command buffer (with
+ // RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
+ // cannot just record at this stage, that would mess up the resource
+ // tracking and commands like TransitionPassResources.
+
+ if (cbD->inExternal)
+ return;
+
+ if (!cbD->useSecondaryCb) {
+ qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
+ "This can be enabled by passing QRhi::ExternalContentsInPass to beginFrame().");
+ return;
+ }
+
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
+
+ VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
+ if (extCb) {
+ cbD->secondaryCbs.append(extCb);
+ cbD->inExternal = true;
+ }
}
void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
+ Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
+ } else if (cbD->inExternal) {
+ VkCommandBuffer extCb = cbD->secondaryCbs.last();
+ cbD->secondaryCbs.removeLast();
+ endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
+ cbD->secondaryCbs.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
+ }
+
cbD->resetCachedState();
}
@@ -5094,6 +5366,12 @@ void QVkRenderPassDescriptor::release()
rhiD->unregisterResource(this);
}
+const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
+{
+ nativeHandlesStruct.renderPass = rp;
+ return &nativeHandlesStruct;
+}
+
QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi)
{
@@ -5782,6 +6060,22 @@ void QVkCommandBuffer::release()
// nothing to do here, cb is not owned by us
}
+const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
+{
+ // Ok this is messy but no other way has been devised yet. Outside
+ // begin(Compute)Pass - end(Compute)Pass it is simple - just return the
+ // primary VkCommandBuffer. Inside, however, we need to provide the current
+ // secondary command buffer (typically the one started by beginExternal(),
+ // in case we are between beginExternal - endExternal inside a pass).
+
+ if (useSecondaryCb && !secondaryCbs.isEmpty())
+ nativeHandlesStruct.commandBuffer = secondaryCbs.last();
+ else
+ nativeHandlesStruct.commandBuffer = cb;
+
+ return &nativeHandlesStruct;
+}
+
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rtWrapper(rhi),
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index 545ef5ad72..ff19c7a54e 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -80,6 +80,11 @@ struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHand
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
};
+struct Q_GUI_EXPORT QRhiVulkanRenderPassNativeHandles : public QRhiNativeHandles
+{
+ VkRenderPass renderPass = VK_NULL_HANDLE;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index 31e0eaa585..962a1b8eb7 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -171,9 +171,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVkRenderPassDescriptor(QRhiImplementation *rhi);
~QVkRenderPassDescriptor();
void release() override;
+ const QRhiNativeHandles *nativeHandles() override;
VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false;
+ QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1;
};
@@ -307,13 +309,11 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
~QVkCommandBuffer();
void release() override;
- VkCommandBuffer cb = VK_NULL_HANDLE;
- QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
+ const QRhiNativeHandles *nativeHandles();
- const QRhiNativeHandles *nativeHandles() {
- nativeHandlesStruct.commandBuffer = cb;
- return &nativeHandlesStruct;
- }
+ VkCommandBuffer cb = VK_NULL_HANDLE; // primary
+ bool useSecondaryCb = false;
+ QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
enum PassType {
NoPass,
@@ -322,9 +322,12 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
};
void resetState() {
- resetCommands();
recordingPass = NoPass;
currentTarget = nullptr;
+
+ secondaryCbs.clear();
+
+ resetCommands();
resetCachedState();
}
@@ -341,6 +344,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
currentIndexFormat = VK_INDEX_TYPE_UINT16;
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ inExternal = false;
}
PassType recordingPass;
@@ -358,6 +362,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ QVarLengthArray<VkCommandBuffer, 4> secondaryCbs;
+ bool inExternal;
struct Command {
enum Cmd {
@@ -384,7 +390,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
DebugMarkerEnd,
DebugMarkerInsert,
TransitionPassResources,
- Dispatch
+ Dispatch,
+ ExecuteSecondary
};
Cmd cmd;
@@ -493,6 +500,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
} debugMarkerEnd;
struct {
VkDebugMarkerMarkerInfoEXT marker;
+ int markerNameIndex;
} debugMarkerInsert;
struct {
int trackerIndex;
@@ -500,6 +508,9 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct {
int x, y, z;
} dispatch;
+ struct {
+ VkCommandBuffer cb;
+ } executeSecondary;
} args;
};
QVector<Command> commands;
@@ -508,9 +519,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
void resetCommands() {
commands.clear();
+ resetPools();
+
passResTrackers.clear();
currentPassResTrackerIndex = -1;
- resetPools();
}
void resetPools() {
@@ -519,7 +531,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
pools.dynamicOffset.clear();
pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear();
- pools.debugMarkerName.clear();
+ pools.debugMarkerData.clear();
}
struct {
@@ -528,7 +540,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<uint32_t, 4> dynamicOffset;
QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
- QVarLengthArray<QByteArray, 4> debugMarkerName;
+ QVarLengthArray<QByteArray, 4> debugMarkerData;
} pools;
friend class QRhiVulkan;
@@ -592,7 +604,7 @@ struct QVkSwapChain : public QRhiSwapChain
bool imageAcquired = false;
bool imageSemWaitable = false;
quint32 imageIndex = 0;
- VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
+ VkCommandBuffer cmdBuf = VK_NULL_HANDLE; // primary
VkFence cmdFence = VK_NULL_HANDLE;
bool cmdFenceWaitable = false;
int timestampQueryIndex = -1;
@@ -637,8 +649,8 @@ public:
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
- QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
- QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@@ -727,9 +739,12 @@ public:
VkShaderModule createShader(const QByteArray &spirv);
void prepareNewFrame(QRhiCommandBuffer *cb);
- QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb);
- QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
- VkSemaphore *waitSem, VkSemaphore *signalSem);
+ VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD = nullptr);
+ void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD);
+ void deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb);
+ QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb);
+ QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem);
void waitCommandCompletion(int frameSlot);
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
@@ -740,7 +755,7 @@ public:
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
- void recordCommandBuffer(QVkCommandBuffer *cbD);
+ void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD);
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
QVkBuffer *bufD,
int slot,
@@ -856,7 +871,8 @@ public:
Sampler,
TextureRenderTarget,
RenderPass,
- StagingBuffer
+ StagingBuffer,
+ CommandBuffer
};
Type type;
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
@@ -903,6 +919,9 @@ public:
VkBuffer stagingBuffer;
QVkAlloc stagingAllocation;
} stagingBuffer;
+ struct {
+ VkCommandBuffer cb;
+ } commandBuffer;
};
};
QVector<DeferredReleaseEntry> releaseQueue;
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index 9098180f69..72ce53c87a 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -42,7 +42,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShader
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains multiple versions of a shader translated to multiple shading languages,
together with reflection metadata.
@@ -134,7 +135,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderVersion
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the shading language version.
@@ -171,7 +173,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderKey
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Specifies the shading language, the version with flags, and the variant.
@@ -202,7 +205,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderCode
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Contains source or binary code for a shader and additional metadata.
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index 77aceaddba..c38a83c497 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -43,7 +43,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes the interface of a shader.
@@ -231,21 +232,24 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription::InOutVariable
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes an input or output variable in the shader.
*/
/*!
\class QShaderDescription::BlockVariable
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a member of a uniform or push constant block.
*/
/*!
\class QShaderDescription::UniformBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a uniform block.
@@ -257,14 +261,16 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription::PushConstantBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a push constant block.
*/
/*!
\class QShaderDescription::StorageBlock
- \inmodule QtRhi
+ \internal
+ \inmodule QtGui
\brief Describes a shader storage block.
*/