mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-14 07:08:32 +09:00
[Fix, Test] (MG_Backend/DirectGLES, MG_Util): baseInstance reached the shader but never the vertex fetch
This commit is contained in:
@@ -3591,13 +3591,32 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glDrawRangeElements(mode, start, end, count, type, indices);
|
||||
}
|
||||
|
||||
// True when the driver will apply baseInstance to the vertex fetch itself, in which case the
|
||||
// attribute-offset emulation must stay out of the way. SetCurrentBaseInstance is orthogonal
|
||||
// and runs either way - it feeds the shader's gl_BaseInstance, not the fetch.
|
||||
inline Bool UseNativeBaseInstance() {
|
||||
return g_GLESCapabilities.SupportsBaseInstance;
|
||||
}
|
||||
|
||||
// The emulated shift has to be in place before PrepareForDraw, because that is what syncs the
|
||||
// VAO; a zero here is what un-shifts the arrays for the next ordinary draw.
|
||||
inline Uint32 EmulatedFetchBaseInstance(GLuint baseinstance) {
|
||||
return UseNativeBaseInstance() ? 0u : static_cast<Uint32>(baseinstance);
|
||||
}
|
||||
|
||||
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||
GLsizei instancecount, GLint basevertex, GLuint baseinstance) {
|
||||
DrawSyncFlags syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing;
|
||||
const VertexArrayImpl::ScopedFetchBaseInstance fetchScope(EmulatedFetchBaseInstance(baseinstance));
|
||||
PrepareForDraw(syncBit);
|
||||
SetCurrentBaseInstance(baseinstance);
|
||||
SetCurrentBaseVertex(basevertex);
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseVertex(mode, count, type, indices, instancecount, basevertex);
|
||||
if (UseNativeBaseInstance()) {
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseVertexBaseInstanceEXT(mode, count, type, indices, instancecount,
|
||||
basevertex, baseinstance);
|
||||
} else {
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseVertex(mode, count, type, indices, instancecount, basevertex);
|
||||
}
|
||||
SetCurrentBaseVertex(0);
|
||||
SetCurrentBaseInstance(0);
|
||||
}
|
||||
@@ -3614,9 +3633,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||
GLsizei instancecount, GLuint baseinstance) {
|
||||
DrawSyncFlags syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing;
|
||||
const VertexArrayImpl::ScopedFetchBaseInstance fetchScope(EmulatedFetchBaseInstance(baseinstance));
|
||||
PrepareForDraw(syncBit);
|
||||
SetCurrentBaseInstance(baseinstance);
|
||||
g_GLESFuncs.glDrawElementsInstanced(mode, count, type, indices, instancecount);
|
||||
if (UseNativeBaseInstance()) {
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseInstanceEXT(mode, count, type, indices, instancecount,
|
||||
baseinstance);
|
||||
} else {
|
||||
g_GLESFuncs.glDrawElementsInstanced(mode, count, type, indices, instancecount);
|
||||
}
|
||||
SetCurrentBaseInstance(0);
|
||||
}
|
||||
|
||||
@@ -3652,9 +3677,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||
GLuint baseinstance) {
|
||||
DrawSyncFlags syncBit = DrawSyncBit::Instancing;
|
||||
const VertexArrayImpl::ScopedFetchBaseInstance fetchScope(EmulatedFetchBaseInstance(baseinstance));
|
||||
PrepareForDraw(syncBit);
|
||||
SetCurrentBaseInstance(baseinstance);
|
||||
g_GLESFuncs.glDrawArraysInstanced(mode, first, count, instancecount);
|
||||
if (UseNativeBaseInstance()) {
|
||||
g_GLESFuncs.glDrawArraysInstancedBaseInstanceEXT(mode, first, count, instancecount, baseinstance);
|
||||
} else {
|
||||
g_GLESFuncs.glDrawArraysInstanced(mode, first, count, instancecount);
|
||||
}
|
||||
SetCurrentBaseInstance(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1490,6 +1490,34 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glVertexBindingDivisor != nullptr;
|
||||
}
|
||||
|
||||
// Draw state, not VAO state: set by the baseInstance draw entry points around
|
||||
// PrepareForDraw and back to zero as soon as the draw is issued.
|
||||
Uint32 g_pendingFetchBaseInstance = 0;
|
||||
|
||||
void SetPendingFetchBaseInstance(Uint32 baseInstance) {
|
||||
g_pendingFetchBaseInstance = baseInstance;
|
||||
}
|
||||
|
||||
Uint32 GetPendingFetchBaseInstance() {
|
||||
return g_pendingFetchBaseInstance;
|
||||
}
|
||||
|
||||
// The "+ baseInstance" of GL's instanced-array element index, expressed as a byte shift
|
||||
// of the array's own offset. Only divisor'd arrays step per instance, so only they move.
|
||||
//
|
||||
// baseInstance is added to the ELEMENT index, not to instance/divisor - the divisor
|
||||
// therefore does not appear here, and the shift is a whole number of strides.
|
||||
//
|
||||
// A resolved stride of zero is the binding model's "never advance" (see
|
||||
// VertexAttribute::Stride), so such an array reads the same element for every instance
|
||||
// and a baseInstance cannot move it. The arithmetic already yields zero for that case.
|
||||
inline SizeT BaseInstanceByteShift(const MG_State::GLState::VertexAttribute& attrib, Uint32 baseInstance) {
|
||||
if (baseInstance == 0 || attrib.Divisor == 0) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<SizeT>(baseInstance) * static_cast<SizeT>(attrib.Stride);
|
||||
}
|
||||
|
||||
// Declares one attribute through the ES binding-point API, the only spelling that can
|
||||
// carry a stride of zero. Returns false when the attribute has no usable buffer, in
|
||||
// which case nothing was emitted.
|
||||
@@ -1547,7 +1575,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const Uint16 currentIndexBufferVersion = stateVAOObject->GetIndexBufferBindingSlot().GetVersion();
|
||||
const Bool attributesDirty = !m_hasSyncedConfigVersion || m_syncedConfigVersion != currentConfigVersion;
|
||||
const Bool indexBufferDirty = currentIndexBufferVersion != m_syncedIndexBufferVersion;
|
||||
if (!attributesDirty && !indexBufferDirty) {
|
||||
|
||||
// The baseInstance shift lives in the attribute offsets the driver already holds, so
|
||||
// a change of baseInstance has to re-emit the divisor'd arrays even when the frontend
|
||||
// config version says nothing moved - and equally has to un-shift them for the next
|
||||
// draw that carries no baseInstance. Resting state is 0 on both sides, so a program
|
||||
// that never calls a *BaseInstance entry point never pays for this compare.
|
||||
const Uint32 fetchBaseInstance = g_pendingFetchBaseInstance;
|
||||
const Bool baseInstanceDirty = m_syncedFetchBaseInstance != fetchBaseInstance;
|
||||
const Bool emitAttributes = attributesDirty || baseInstanceDirty;
|
||||
if (!emitAttributes && !indexBufferDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1555,8 +1592,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
const auto& allAttributeVersions = stateVAOObject->GetAllAttributeVersions();
|
||||
const auto& allAttributes = stateVAOObject->GetAllAttributes();
|
||||
for (Uint attribIndex = 0; attribIndex < allAttributes.size() && attributesDirty; ++attribIndex) {
|
||||
for (Uint attribIndex = 0; attribIndex < allAttributes.size() && emitAttributes; ++attribIndex) {
|
||||
const auto& attrib = allAttributes[attribIndex];
|
||||
// Only the divisor'd arrays carry the shift, and only an enabled one is worth
|
||||
// re-emitting - a disabled array has no pointer the draw could fetch through,
|
||||
// and may well have no buffer to bind either.
|
||||
const Bool needsSyncBaseInstance = baseInstanceDirty && attrib.Enabled && attrib.Divisor != 0;
|
||||
Bool needsSyncSwitch = allAttributeVersions[attribIndex].SwitchVersion !=
|
||||
m_syncedAttributeVersions[attribIndex].SwitchVersion;
|
||||
if (needsSyncSwitch) {
|
||||
@@ -1571,7 +1612,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
m_syncedAttributeVersions[attribIndex].FormatVersion;
|
||||
Bool needsSyncBuffer = allAttributeVersions[attribIndex].BufferVersion !=
|
||||
m_syncedAttributeVersions[attribIndex].BufferVersion;
|
||||
if (!needsSyncFormat && !needsSyncBuffer) continue;
|
||||
if (!needsSyncFormat && !needsSyncBuffer && !needsSyncBaseInstance) continue;
|
||||
|
||||
// Defence in depth. The frontend already declines glVertexAttribLFormat on this
|
||||
// backend (SupportsFloat64VertexAttributes is false - ES has no GL_DOUBLE vertex
|
||||
@@ -1611,6 +1652,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (!SyncZeroStrideAttribute(attribIndex, attrib)) {
|
||||
continue;
|
||||
}
|
||||
// No BaseInstanceByteShift here on purpose: a zero stride never advances, so
|
||||
// the shift is zero by construction and adding it would only obscure that.
|
||||
if (needsSyncFormat) {
|
||||
g_GLESFuncs.glVertexBindingDivisor(attribIndex, attrib.Divisor);
|
||||
}
|
||||
@@ -1641,16 +1684,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
} // start from a clean slate so the check below is about THIS call
|
||||
}
|
||||
|
||||
const SizeT fetchOffset = attrib.Offset + BaseInstanceByteShift(attrib, fetchBaseInstance);
|
||||
|
||||
if (!attrib.IsInteger) {
|
||||
// GL_BGRA is passed to the driver as the size argument (the driver reorders BGRA).
|
||||
const GLint glSize = attrib.IsBgra ? static_cast<GLint>(GL_BGRA) : attrib.Size;
|
||||
g_GLESFuncs.glVertexAttribPointer(
|
||||
attribIndex, glSize, MG_Util::ConvertDataTypeToGLEnum(attrib.Type),
|
||||
attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)attrib.Offset);
|
||||
attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)fetchOffset);
|
||||
} else {
|
||||
g_GLESFuncs.glVertexAttribIPointer(attribIndex, attrib.Size,
|
||||
MG_Util::ConvertDataTypeToGLEnum(attrib.Type), attrib.Stride,
|
||||
(const void*)attrib.Offset);
|
||||
(const void*)fetchOffset);
|
||||
}
|
||||
|
||||
if (formatMayBeRefused && g_GLESFuncs.glGetError() != GL_NO_ERROR) {
|
||||
@@ -1694,6 +1739,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
m_syncedConfigVersion = currentConfigVersion;
|
||||
m_hasSyncedConfigVersion = true;
|
||||
}
|
||||
if (emitAttributes) {
|
||||
m_syncedFetchBaseInstance = fetchBaseInstance;
|
||||
}
|
||||
}
|
||||
|
||||
void BackendVertexArrayObject::SyncClientSideAttributesForDrawArrays(
|
||||
|
||||
@@ -466,6 +466,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Uint32 m_syncedConfigVersion = 0;
|
||||
Array<MG_State::GLState::VertexAttributeVersion, MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS>
|
||||
m_syncedAttributeVersions;
|
||||
// Byte shift currently baked into the instanced arrays' offsets by the baseInstance
|
||||
// emulation (see SetPendingFetchBaseInstance). It is draw state, not VAO state, so it
|
||||
// is deliberately NOT covered by the config version: the frontend never bumps for it.
|
||||
// Kept here because it describes what was last EMITTED, which is what the next sync
|
||||
// has to correct.
|
||||
Uint32 m_syncedFetchBaseInstance = 0;
|
||||
};
|
||||
|
||||
extern StateBackendObjectRegistry<MG_State::GLState::VertexArrayObject, BackendVertexArrayObject>
|
||||
@@ -479,6 +485,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void InvalidateVAOBindingCache();
|
||||
// ES resets the binding to 0 when the currently bound VAO is deleted.
|
||||
void NoteVAOIdDeleted(Uint id);
|
||||
|
||||
// baseInstance emulation for drivers without GL_EXT_base_instance. GL fetches an
|
||||
// instanced array at element "floor(instance / divisor) + baseInstance", and ES has no
|
||||
// way to say the "+ baseInstance" part - so it is folded into the attribute's own byte
|
||||
// offset (baseInstance * stride) for every divisor'd array, which is exactly equivalent.
|
||||
// Must be set BEFORE PrepareForDraw so the VAO sync sees it, and cleared after the draw
|
||||
// so the next one refetches from element 0; ScopedFetchBaseInstance does both.
|
||||
void SetPendingFetchBaseInstance(Uint32 baseInstance);
|
||||
Uint32 GetPendingFetchBaseInstance();
|
||||
|
||||
class ScopedFetchBaseInstance {
|
||||
public:
|
||||
explicit ScopedFetchBaseInstance(Uint32 baseInstance) { SetPendingFetchBaseInstance(baseInstance); }
|
||||
~ScopedFetchBaseInstance() { SetPendingFetchBaseInstance(0); }
|
||||
ScopedFetchBaseInstance(const ScopedFetchBaseInstance&) = delete;
|
||||
ScopedFetchBaseInstance& operator=(const ScopedFetchBaseInstance&) = delete;
|
||||
};
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
|
||||
Reference in New Issue
Block a user