From 058c52fc2a6476f688d4b07d6f24516e26d0a8f5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jun 2019 15:06:06 +0200 Subject: [PATCH] rhi: Improve base vertex/instance support Have feature flags as appropriate. OpenGL is causing a mess here but let's support what we can since some of this will become relevant in more sophisticated mesh drawing cases with 3D in particular. Change-Id: Idfa7b4642ec87147978e03d78d6233efbd151491 Reviewed-by: Lars Knoll --- src/gui/rhi/qrhi.cpp | 42 ++++++++++++++---- src/gui/rhi/qrhi_p.h | 4 +- src/gui/rhi/qrhid3d11.cpp | 4 ++ src/gui/rhi/qrhigles2.cpp | 46 +++++++++++++++----- src/gui/rhi/qrhigles2_p_p.h | 7 ++- src/gui/rhi/qrhimetal.mm | 4 ++ src/gui/rhi/qrhivulkan.cpp | 8 +++- tests/manual/rhi/triquadcube/triquadcube.cpp | 2 + 8 files changed, 92 insertions(+), 25 deletions(-) diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 681a6ddfb8..646f0aef13 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -511,6 +511,14 @@ QT_BEGIN_NAMESPACE dropped from the generated code) Note that some APIs (Metal, Vulkan) require the point size to be set in the shader explicitly whenever drawing points, even when the size is 1, as they do not automatically default to 1. + + \value BaseVertex Indicates that \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} + supports the \c vertexOffset argument. When reported as not supported, the + vertexOffset value in an indexed draw is ignored. + + \value BaseInstance Indicates that instanced draw commands support the \c + firstInstance argument. When reported as not supported, the firstInstance + value is ignored and the instance ID starts from 0. */ /*! @@ -4483,15 +4491,21 @@ void QRhiCommandBuffer::setStencilRef(quint32 refValue) Records a non-indexed draw. The number of vertices is specified in \a vertexCount. For instanced - drawing set \a instanceCount to a value other than 1. \a firstVertex is - the index of the first vertex to draw. \a firstInstance is the instance ID - of the first instance to draw. + drawing set \a instanceCount to a value other than 1. \a firstVertex is the + index of the first vertex to draw. When drawing multiple instances, the + first instance ID is specified by \a firstInstance. + + \note \a firstInstance may not be supported, and is ignored when the + QRhi::BaseInstance feature is reported as not supported. The first ID is + always 0 in that case. \note This function can only be called inside a render pass, meaning between a beginPass() and endPass() call. */ void QRhiCommandBuffer::draw(quint32 vertexCount, - quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) + quint32 instanceCount, + quint32 firstVertex, + quint32 firstInstance) { m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance); } @@ -4509,17 +4523,27 @@ void QRhiCommandBuffer::draw(quint32 vertexCount, \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset} feature will be reported as not-supported. - For instanced drawing set \a instanceCount to a value other than 1. \a - firstInstance is the instance ID of the first instance to draw. + For instanced drawing set \a instanceCount to a value other than 1. When + drawing multiple instances, the first instance ID is specified by \a + firstInstance. - \a vertexOffset is added to the vertex index. + \note \a firstInstance may not be supported, and is ignored when the + QRhi::BaseInstance feature is reported as not supported. The first ID is + always 0 in that case. + + \a vertexOffset (also called \c{base vertex}) is a signed value that is + added to the element index before indexing into the vertex buffer. Support + for this is not always available, and the value is ignored when the feature + QRhi::BaseVertex is reported as unsupported. \note This function can only be called inside a render pass, meaning between a beginPass() and endPass() call. */ void QRhiCommandBuffer::drawIndexed(quint32 indexCount, - quint32 instanceCount, quint32 firstIndex, - qint32 vertexOffset, quint32 firstInstance) + quint32 instanceCount, + quint32 firstIndex, + qint32 vertexOffset, + quint32 firstInstance) { m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index fb8727b265..df30817ef4 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1318,7 +1318,9 @@ public: ElementIndexUint, Compute, WideLines, - VertexShaderPointSize + VertexShaderPointSize, + BaseVertex, + BaseInstance }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index dc69f50cc8..49e90693be 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -382,6 +382,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::VertexShaderPointSize: return false; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 60bbda2d60..22cb030c27 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -426,6 +426,7 @@ bool QRhiGles2::create(QRhi::Flags flags) caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24); caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats); caps.instancing = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 3); + caps.baseVertex = caps.ctxMajor >= 3 && caps.ctxMinor >= 2; nativeHandlesStruct.context = ctx; @@ -687,6 +688,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return caps.baseVertex; + case QRhi::BaseInstance: + return false; // not in ES 3.2, so won't bother default: Q_UNREACHABLE(); return false; @@ -934,7 +939,6 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { - Q_UNUSED(firstInstance); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); @@ -944,14 +948,13 @@ void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, cmd.args.draw.vertexCount = vertexCount; cmd.args.draw.firstVertex = firstVertex; cmd.args.draw.instanceCount = instanceCount; + cmd.args.draw.baseInstance = firstInstance; cbD->commands.append(cmd); } void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { - Q_UNUSED(firstInstance); - Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); @@ -961,6 +964,8 @@ void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, cmd.args.drawIndexed.indexCount = indexCount; cmd.args.drawIndexed.firstIndex = firstIndex; cmd.args.drawIndexed.instanceCount = instanceCount; + cmd.args.drawIndexed.baseInstance = firstInstance; + cmd.args.drawIndexed.baseVertex = vertexOffset; cbD->commands.append(cmd); } @@ -1705,16 +1710,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) const GLvoid *ofs = reinterpret_cast( quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset)); if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) { - f->glDrawElements(psD->drawMode, - cmd.args.drawIndexed.indexCount, - indexType, - ofs); + if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) { + f->glDrawElementsBaseVertex(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.baseVertex); + } else { + f->glDrawElements(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs); + } } else { - f->glDrawElementsInstanced(psD->drawMode, - cmd.args.drawIndexed.indexCount, - indexType, - ofs, - cmd.args.drawIndexed.instanceCount); + if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) { + f->glDrawElementsInstancedBaseVertex(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.instanceCount, + cmd.args.drawIndexed.baseVertex); + } else { + f->glDrawElementsInstanced(psD->drawMode, + cmd.args.drawIndexed.indexCount, + indexType, + ofs, + cmd.args.drawIndexed.instanceCount); + } } } else { qWarning("No graphics pipeline active for drawIndexed; ignored"); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index b758ec40b8..d6682977ff 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -325,12 +325,15 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer quint32 vertexCount; quint32 firstVertex; quint32 instanceCount; + quint32 baseInstance; } draw; struct { QRhiGraphicsPipeline *ps; quint32 indexCount; quint32 firstIndex; quint32 instanceCount; + quint32 baseInstance; + qint32 baseVertex; } drawIndexed; struct { QRhiGraphicsPipeline *ps; @@ -648,7 +651,8 @@ public: elementIndexUint(false), depth24(false), rgba8Format(false), - instancing(false) + instancing(false), + baseVertex(false) { } int ctxMajor; int ctxMinor; @@ -677,6 +681,7 @@ public: uint depth24 : 1; uint rgba8Format : 1; uint instancing : 1; + uint baseVertex : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 09b80c831d..fa537a504b 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -527,6 +527,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 8598e5869a..7c4eeaf226 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3549,6 +3549,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return hasWideLines; case QRhi::VertexShaderPointSize: return true; + case QRhi::BaseVertex: + return true; + case QRhi::BaseInstance: + return true; default: Q_UNREACHABLE(); return false; @@ -4063,7 +4067,7 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) } void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, - quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) + quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) { QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); @@ -4078,7 +4082,7 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, } void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, - quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) + quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) { QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); diff --git a/tests/manual/rhi/triquadcube/triquadcube.cpp b/tests/manual/rhi/triquadcube/triquadcube.cpp index 1b390c34ed..4165e96127 100644 --- a/tests/manual/rhi/triquadcube/triquadcube.cpp +++ b/tests/manual/rhi/triquadcube/triquadcube.cpp @@ -174,6 +174,8 @@ void Window::customInit() qDebug("isFeatureSupported(Compute): %d", m_r->isFeatureSupported(QRhi::Compute)); qDebug("isFeatureSupported(WideLines): %d", m_r->isFeatureSupported(QRhi::WideLines)); qDebug("isFeatureSupported(VertexShaderPointSize): %d", m_r->isFeatureSupported(QRhi::VertexShaderPointSize)); + qDebug("isFeatureSupported(BaseVertex): %d", m_r->isFeatureSupported(QRhi::BaseVertex)); + qDebug("isFeatureSupported(BaseInstance): %d", m_r->isFeatureSupported(QRhi::BaseInstance)); qDebug("Min 2D texture width/height: %d", m_r->resourceLimit(QRhi::TextureSizeMin)); qDebug("Max 2D texture width/height: %d", m_r->resourceLimit(QRhi::TextureSizeMax)); qDebug("Max color attachment count: %d", m_r->resourceLimit(QRhi::MaxColorAttachments));