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 <lars.knoll@qt.io>
This commit is contained in:
Laszlo Agocs 2019-06-28 15:06:06 +02:00
parent 201a22a4d2
commit 058c52fc2a
8 changed files with 92 additions and 25 deletions

View File

@ -511,6 +511,14 @@ QT_BEGIN_NAMESPACE
dropped from the generated code) Note that some APIs (Metal, Vulkan) dropped from the generated code) Note that some APIs (Metal, Vulkan)
require the point size to be set in the shader explicitly whenever drawing 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. 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. Records a non-indexed draw.
The number of vertices is specified in \a vertexCount. For instanced The number of vertices is specified in \a vertexCount. For instanced
drawing set \a instanceCount to a value other than 1. \a firstVertex is drawing set \a instanceCount to a value other than 1. \a firstVertex is the
the index of the first vertex to draw. \a firstInstance is the instance ID index of the first vertex to draw. When drawing multiple instances, the
of the first instance to draw. 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 \note This function can only be called inside a render pass, meaning
between a beginPass() and endPass() call. between a beginPass() and endPass() call.
*/ */
void QRhiCommandBuffer::draw(quint32 vertexCount, 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); m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance);
} }
@ -4509,17 +4523,27 @@ void QRhiCommandBuffer::draw(quint32 vertexCount,
\l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset} \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset}
feature will be reported as not-supported. feature will be reported as not-supported.
For instanced drawing set \a instanceCount to a value other than 1. \a For instanced drawing set \a instanceCount to a value other than 1. When
firstInstance is the instance ID of the first instance to draw. 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 \note This function can only be called inside a render pass, meaning
between a beginPass() and endPass() call. between a beginPass() and endPass() call.
*/ */
void QRhiCommandBuffer::drawIndexed(quint32 indexCount, void QRhiCommandBuffer::drawIndexed(quint32 indexCount,
quint32 instanceCount, quint32 firstIndex, quint32 instanceCount,
qint32 vertexOffset, quint32 firstInstance) quint32 firstIndex,
qint32 vertexOffset,
quint32 firstInstance)
{ {
m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
} }

View File

@ -1318,7 +1318,9 @@ public:
ElementIndexUint, ElementIndexUint,
Compute, Compute,
WideLines, WideLines,
VertexShaderPointSize VertexShaderPointSize,
BaseVertex,
BaseInstance
}; };
enum BeginFrameFlag { enum BeginFrameFlag {

View File

@ -382,6 +382,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return false; return false;
case QRhi::VertexShaderPointSize: case QRhi::VertexShaderPointSize:
return false; return false;
case QRhi::BaseVertex:
return true;
case QRhi::BaseInstance:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;

View File

@ -426,6 +426,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24); caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats); caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
caps.instancing = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 3); caps.instancing = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 3);
caps.baseVertex = caps.ctxMajor >= 3 && caps.ctxMinor >= 2;
nativeHandlesStruct.context = ctx; nativeHandlesStruct.context = ctx;
@ -687,6 +688,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::VertexShaderPointSize: case QRhi::VertexShaderPointSize:
return true; return true;
case QRhi::BaseVertex:
return caps.baseVertex;
case QRhi::BaseInstance:
return false; // not in ES 3.2, so won't bother
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -934,7 +939,6 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount, void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
{ {
Q_UNUSED(firstInstance);
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); 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.vertexCount = vertexCount;
cmd.args.draw.firstVertex = firstVertex; cmd.args.draw.firstVertex = firstVertex;
cmd.args.draw.instanceCount = instanceCount; cmd.args.draw.instanceCount = instanceCount;
cmd.args.draw.baseInstance = firstInstance;
cbD->commands.append(cmd); cbD->commands.append(cmd);
} }
void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
{ {
Q_UNUSED(firstInstance);
Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass); 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.indexCount = indexCount;
cmd.args.drawIndexed.firstIndex = firstIndex; cmd.args.drawIndexed.firstIndex = firstIndex;
cmd.args.drawIndexed.instanceCount = instanceCount; cmd.args.drawIndexed.instanceCount = instanceCount;
cmd.args.drawIndexed.baseInstance = firstInstance;
cmd.args.drawIndexed.baseVertex = vertexOffset;
cbD->commands.append(cmd); cbD->commands.append(cmd);
} }
@ -1705,10 +1710,26 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
const GLvoid *ofs = reinterpret_cast<const GLvoid *>( const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset)); quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset));
if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) { if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) {
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, f->glDrawElements(psD->drawMode,
cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.indexCount,
indexType, indexType,
ofs); ofs);
}
} else {
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 { } else {
f->glDrawElementsInstanced(psD->drawMode, f->glDrawElementsInstanced(psD->drawMode,
cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.indexCount,
@ -1716,6 +1737,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
ofs, ofs,
cmd.args.drawIndexed.instanceCount); cmd.args.drawIndexed.instanceCount);
} }
}
} else { } else {
qWarning("No graphics pipeline active for drawIndexed; ignored"); qWarning("No graphics pipeline active for drawIndexed; ignored");
} }

View File

@ -325,12 +325,15 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
quint32 vertexCount; quint32 vertexCount;
quint32 firstVertex; quint32 firstVertex;
quint32 instanceCount; quint32 instanceCount;
quint32 baseInstance;
} draw; } draw;
struct { struct {
QRhiGraphicsPipeline *ps; QRhiGraphicsPipeline *ps;
quint32 indexCount; quint32 indexCount;
quint32 firstIndex; quint32 firstIndex;
quint32 instanceCount; quint32 instanceCount;
quint32 baseInstance;
qint32 baseVertex;
} drawIndexed; } drawIndexed;
struct { struct {
QRhiGraphicsPipeline *ps; QRhiGraphicsPipeline *ps;
@ -648,7 +651,8 @@ public:
elementIndexUint(false), elementIndexUint(false),
depth24(false), depth24(false),
rgba8Format(false), rgba8Format(false),
instancing(false) instancing(false),
baseVertex(false)
{ } { }
int ctxMajor; int ctxMajor;
int ctxMinor; int ctxMinor;
@ -677,6 +681,7 @@ public:
uint depth24 : 1; uint depth24 : 1;
uint rgba8Format : 1; uint rgba8Format : 1;
uint instancing : 1; uint instancing : 1;
uint baseVertex : 1;
} caps; } caps;
QGles2SwapChain *currentSwapChain = nullptr; QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats; QVector<GLint> supportedCompressedFormats;

View File

@ -527,6 +527,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return false; return false;
case QRhi::VertexShaderPointSize: case QRhi::VertexShaderPointSize:
return true; return true;
case QRhi::BaseVertex:
return true;
case QRhi::BaseInstance:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;

View File

@ -3549,6 +3549,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return hasWideLines; return hasWideLines;
case QRhi::VertexShaderPointSize: case QRhi::VertexShaderPointSize:
return true; return true;
case QRhi::BaseVertex:
return true;
case QRhi::BaseInstance:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;

View File

@ -174,6 +174,8 @@ void Window::customInit()
qDebug("isFeatureSupported(Compute): %d", m_r->isFeatureSupported(QRhi::Compute)); qDebug("isFeatureSupported(Compute): %d", m_r->isFeatureSupported(QRhi::Compute));
qDebug("isFeatureSupported(WideLines): %d", m_r->isFeatureSupported(QRhi::WideLines)); qDebug("isFeatureSupported(WideLines): %d", m_r->isFeatureSupported(QRhi::WideLines));
qDebug("isFeatureSupported(VertexShaderPointSize): %d", m_r->isFeatureSupported(QRhi::VertexShaderPointSize)); 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("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 2D texture width/height: %d", m_r->resourceLimit(QRhi::TextureSizeMax));
qDebug("Max color attachment count: %d", m_r->resourceLimit(QRhi::MaxColorAttachments)); qDebug("Max color attachment count: %d", m_r->resourceLimit(QRhi::MaxColorAttachments));