[Fix] (MG_Backend/DirectGLES, MG_Impl/Texture, MG_Test/Texture): fix Voxy base-instance and bound texStorage2D

This commit is contained in:
BZLZHH
2026-06-10 00:07:20 +08:00
parent df90753d85
commit 720919455f
5 changed files with 166 additions and 29 deletions
+126 -28
View File
@@ -65,6 +65,34 @@ namespace MobileGL::MG_Backend::DirectGLES {
Uint32 baseInstance = 0;
};
struct DrawArraysIndirectCommand {
Uint32 count = 0;
Uint32 instanceCount = 0;
Uint32 first = 0;
Uint32 baseInstance = 0;
};
const Uint8* ResolveIndirectCommandBytes(const void* indirect, SizeT requiredBytes, const char* label) {
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
if (drawBuffer) {
drawBuffer->MarkPersistentMappedRangeDirty();
const auto drawData = drawBuffer->GetDataReadOnly();
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
if (!drawData || commandOffset + requiredBytes > drawData->size()) {
MGLOG_E("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
return nullptr;
}
return drawData->data() + commandOffset;
}
if (!indirect) {
MGLOG_E("%s skipped: indirect pointer is null", label);
return nullptr;
}
return reinterpret_cast<const Uint8*>(indirect);
}
namespace DebugImpl {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
void ErrorLopper::Loop(const std::function<void(GLenum)>& func) {
@@ -1086,24 +1114,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
return;
}
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
if (!drawBuffer) {
MGLOG_E("MultiDrawElementsIndirect skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
return;
}
drawBuffer->MarkPersistentMappedRangeDirty();
const auto drawData = drawBuffer->GetDataReadOnly();
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(drawcount - 1) +
sizeof(DrawElementsIndirectCommand);
if (!drawData || commandBytes > drawData->size()) {
MGLOG_E("MultiDrawElementsIndirect skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
const auto* commandBytes = ResolveIndirectCommandBytes(
indirect,
static_cast<SizeT>(stride) * static_cast<SizeT>(drawcount - 1) + sizeof(DrawElementsIndirectCommand),
"MultiDrawElementsIndirect");
if (!commandBytes) {
return;
}
for (GLsizei i = 0; i < drawcount; ++i) {
DrawElementsIndirectCommand cmd{};
std::memcpy(&cmd, drawData->data() + commandOffset + static_cast<SizeT>(i) * stride, sizeof(cmd));
std::memcpy(&cmd, commandBytes + static_cast<SizeT>(i) * stride, sizeof(cmd));
if (cmd.count == 0 || cmd.instanceCount == 0) {
continue;
}
@@ -1192,14 +1213,41 @@ namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
DebugImpl::OpenGLScopeMarker marker(__func__);
#endif
DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer;
if (drawcount <= 0) {
return;
}
if (stride == 0) {
stride = sizeof(DrawArraysIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
MGLOG_E("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawArraysIndirectCommand));
return;
}
DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
for (GLsizei i = 0; i < drawcount; ++i) {
const GLvoid* cmd = reinterpret_cast<const GLvoid*>(reinterpret_cast<const uint8_t*>(indirect) +
i * (stride ? stride : sizeof(GLsizei) * 4));
g_GLESFuncs.glDrawArraysIndirect(mode, cmd);
const auto* commandBytes = ResolveIndirectCommandBytes(
indirect,
static_cast<SizeT>(stride) * static_cast<SizeT>(drawcount - 1) + sizeof(DrawArraysIndirectCommand),
"MultiDrawArraysIndirect");
if (!commandBytes) {
return;
}
for (GLsizei i = 0; i < drawcount; ++i) {
DrawArraysIndirectCommand cmd{};
std::memcpy(&cmd, commandBytes + static_cast<SizeT>(i) * stride, sizeof(cmd));
if (cmd.count == 0 || cmd.instanceCount == 0) {
continue;
}
SetCurrentBaseInstance(cmd.baseInstance);
g_GLESFuncs.glDrawArraysInstanced(
mode, static_cast<GLint>(cmd.first), static_cast<GLsizei>(cmd.count),
static_cast<GLsizei>(cmd.instanceCount));
}
SetCurrentBaseInstance(0);
}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
@@ -1217,8 +1265,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance) {
// Not supported in OpenGL ES
MGLOG_W("DrawElementsInstancedBaseVertexBaseInstance is not supported in OpenGL ES.");
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
SetCurrentBaseInstance(baseinstance);
g_GLESFuncs.glDrawElementsInstancedBaseVertex(mode, count, type, indices, instancecount, basevertex);
SetCurrentBaseInstance(0);
}
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
@@ -1230,8 +1281,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance) {
// Not supported in OpenGL ES
MGLOG_W("DrawElementsInstancedBaseInstance is not supported in OpenGL ES.");
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
SetCurrentBaseInstance(baseinstance);
g_GLESFuncs.glDrawElementsInstanced(mode, count, type, indices, instancecount);
SetCurrentBaseInstance(0);
}
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) {
@@ -1241,15 +1295,42 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer;
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
g_GLESFuncs.glDrawElementsIndirect(mode, type, indirect);
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
return;
}
const auto* commandBytes =
ResolveIndirectCommandBytes(indirect, sizeof(DrawElementsIndirectCommand), "DrawElementsIndirect");
if (!commandBytes) {
return;
}
DrawElementsIndirectCommand cmd{};
std::memcpy(&cmd, commandBytes, sizeof(cmd));
if (cmd.count == 0 || cmd.instanceCount == 0) {
return;
}
SetCurrentBaseInstance(cmd.baseInstance);
const auto indexByteOffset = static_cast<SizeT>(cmd.firstIndex) * indexSize;
g_GLESFuncs.glDrawElementsInstancedBaseVertex(
mode, static_cast<GLsizei>(cmd.count), type, reinterpret_cast<const GLvoid*>(indexByteOffset),
static_cast<GLsizei>(cmd.instanceCount), cmd.baseVertex);
SetCurrentBaseInstance(0);
}
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance) {
// Not supported in OpenGL ES
MGLOG_W("DrawArraysInstancedBaseInstance is not supported in OpenGL ES.");
DrawSyncBit syncBit = DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
SetCurrentBaseInstance(baseinstance);
g_GLESFuncs.glDrawArraysInstanced(mode, first, count, instancecount);
SetCurrentBaseInstance(0);
}
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {
@@ -1259,9 +1340,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
void DrawArraysIndirect(GLenum mode, const void* indirect) {
DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer;
DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing;
PrepareForDraw(syncBit);
g_GLESFuncs.glDrawArraysIndirect(mode, indirect);
const auto* commandBytes =
ResolveIndirectCommandBytes(indirect, sizeof(DrawArraysIndirectCommand), "DrawArraysIndirect");
if (!commandBytes) {
return;
}
DrawArraysIndirectCommand cmd{};
std::memcpy(&cmd, commandBytes, sizeof(cmd));
if (cmd.count == 0 || cmd.instanceCount == 0) {
return;
}
SetCurrentBaseInstance(cmd.baseInstance);
g_GLESFuncs.glDrawArraysInstanced(
mode, static_cast<GLint>(cmd.first), static_cast<GLsizei>(cmd.count),
static_cast<GLsizei>(cmd.instanceCount));
SetCurrentBaseInstance(0);
}
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
@@ -301,7 +301,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramBinary, GLuint program, GLenum binary
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramParameteri, program, pname, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateFramebuffer, target, numAttachments, attachments)
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params)
DECLARE_GL_FUNCTION_HEAD(void, DispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchCompute, num_groups_x, num_groups_y, num_groups_z)
@@ -1870,6 +1870,20 @@ namespace MobileGL::MG_Impl::GLImpl {
}
}
void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
auto& textureObject = bindingSlot.GetBoundObject();
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
TextureStorage2D(textureObject->GetExternalIndex(), levels, internalformat, width, height);
}
void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
GLenum format, GLenum type, const void* pixels) {
auto textureObject = GetTextureObjectByName(texture, __func__);
@@ -28,6 +28,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params);
void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params);
void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+24
View File
@@ -99,6 +99,30 @@ TEST_F(TextureTest, BoundTexSubImage2DUsesCompactRowsAfterUnpackProcessing) {
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
TEST_F(TextureTest, BoundTexStorage2DAllocatesRedTextureForSubImageUpdates) {
GLuint texture = 0;
MG_Impl::GLImpl::GenTextures(1, &texture);
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
MG_Impl::GLImpl::TexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 32, 32);
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
ASSERT_NE(mipmapObject, nullptr);
EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 0), IntVec3(32, 32, 1));
EXPECT_TRUE(textureObject->IsComplete());
const Uint8 pixels[4 * 4] = {
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16,
};
MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 20, 28, 4, 4, GL_RED, GL_UNSIGNED_BYTE, pixels);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) {
GLuint texture = 0;
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);