From 720919455fb98f12fbd6420af4cc4269357a9217 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Wed, 10 Jun 2026 00:07:20 +0800 Subject: [PATCH] [Fix] (MG_Backend/DirectGLES, MG_Impl/Texture, MG_Test/Texture): fix Voxy base-instance and bound texStorage2D --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 154 ++++++++++++++---- .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 2 +- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 14 ++ MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h | 1 + MobileGL/MG_Test/Texture/TextureTest.cpp | 24 +++ 5 files changed, 166 insertions(+), 29 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index c1a7019c..541dd4fb 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -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(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(indirect); + } + namespace DebugImpl { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG void ErrorLopper::Loop(const std::function& 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(indirect); - const SizeT commandBytes = commandOffset + static_cast(stride) * static_cast(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(stride) * static_cast(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(i) * stride, sizeof(cmd)); + std::memcpy(&cmd, commandBytes + static_cast(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(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(reinterpret_cast(indirect) + - i * (stride ? stride : sizeof(GLsizei) * 4)); - g_GLESFuncs.glDrawArraysIndirect(mode, cmd); + const auto* commandBytes = ResolveIndirectCommandBytes( + indirect, + static_cast(stride) * static_cast(drawcount - 1) + sizeof(DrawArraysIndirectCommand), + "MultiDrawArraysIndirect"); + if (!commandBytes) { + return; } + + for (GLsizei i = 0; i < drawcount; ++i) { + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes + static_cast(i) * stride, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + continue; + } + SetCurrentBaseInstance(cmd.baseInstance); + g_GLESFuncs.glDrawArraysInstanced( + mode, static_cast(cmd.first), static_cast(cmd.count), + static_cast(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(cmd.firstIndex) * indexSize; + g_GLESFuncs.glDrawElementsInstancedBaseVertex( + mode, static_cast(cmd.count), type, reinterpret_cast(indexByteOffset), + static_cast(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(cmd.first), static_cast(cmd.count), + static_cast(cmd.instanceCount)); + SetCurrentBaseInstance(0); } void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 962511d8..a69f0cbb 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -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) diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 6b549d02..3d67ae43 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -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__); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h index fbfead3b..1ea881e8 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h @@ -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, diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index 61c16cce..68a41f07 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -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(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);