From a09d6fa8ffcc751db12e8bd9a18c433d45b62cb5 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 31 Jan 2026 12:50:21 +0800 Subject: [PATCH 01/74] [Fix]: fix typo --- CMakeLists.txt | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dffb06c1..ab6013f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project("MobileGL") option(MOBILEGL_BUILD_TEST "Build MobileGL tests" ON ) option(MOBILEGL_BUILD_BENCHMARK "Build MobileGL benchmarks" ON ) option(MOBILEGL_FORCE_RELEASE_OPT "Enable Release optimization flags in Debug build" ON ) -option(MOBILEGL_ENABLE_TRANCY "Enable trancy for profiling" OFF) +option(MOBILEGL_ENABLE_TRACY "Enable tracy for profiling" OFF) if (ANDROID) set(MOBILEGL_BUILD_TEST OFF CACHE BOOL "Build MobileGL tests" FORCE) diff --git a/README.md b/README.md index fdd0c25f..ea8050e8 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,11 @@ If you want to try the project right now, you’ll need to build it yourself: ## Build Options | Option | Description | Default | -| ---------------------------- | ----------------------------------------------------- | ------- | +|------------------------------| ----------------------------------------------------- | ------- | | `MOBILEGL_BUILD_TEST` | Build MobileGL tests (requires Clang) | ON | | `MOBILEGL_BUILD_BENCHMARK` | Build MobileGL benchmarks (requires Clang) | ON | | `MOBILEGL_FORCE_RELEASE_OPT` | Enable O3 and LTO in Debug build | ON | -| `MOBILEGL_ENABLE_TRANCY` | Enable Tracy profiler for performance analysis | OFF | +| `MOBILEGL_ENABLE_TRACY` | Enable Tracy profiler for performance analysis | OFF | **Notes:** From acda4c24aa81ed7cec5debc8bf27bbb5e4fafc77 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 31 Jan 2026 13:10:45 +0800 Subject: [PATCH 02/74] [Fix]: fix typo --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4b1148d4..8780ca18 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ android { // externalNativeBuild { // cmake { -// arguments "-DTRACY_ENABLE=ON" +// arguments "-DMOBILEGL_ENABLE_TRACY=ON" // } // } } From 0cf9d9223dcf01513b0afc832172b3bb1596c751 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 2 Feb 2026 02:25:41 +0800 Subject: [PATCH 03/74] [Feat|Perf] (*/BufferState): Improve dirty mark for Buffer. --- CMakeLists.txt | 2 + MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 32 +- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 148 +++-- MobileGL/MG_Backend/DirectGLES/Managers.h | 11 +- MobileGL/MG_Backend/DirectGLES/Utils.cpp | 11 +- .../MG_Impl/EGLImpl/EGLWrapper/EGLWrapper.cpp | 28 +- .../EGLImpl/Temporary/TemporaryEGLImpl.cpp | 4 +- .../EGLImpl/Temporary/TemporaryEGLImpl.h | 19 +- MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp | 4 +- MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h | 18 +- .../MG_Impl/GLImpl/Program/GL_Program.cpp | 8 +- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 49 +- MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h | 8 +- .../MG_Impl/GLImpl/Texture/Validators.cpp | 12 +- MobileGL/MG_Impl/GLImpl/Texture/Validators.h | 3 +- MobileGL/MG_Impl/GetProcAddress.cpp | 6 +- .../GLState/BufferState/BufferObject.cpp | 52 +- .../GLState/BufferState/BufferObject.h | 23 +- .../GLState/ProgramState/ProgramObject.cpp | 2 +- .../GLState/ProgramState/ShaderObject.cpp | 3 +- .../GLState/TextureState/MipmapStorage.h | 1 + .../TextureState/MipmapUploadTargetArray.h | 4 +- .../GLState/TextureState/TextureObject.h | 3 +- .../TextureState/TextureObjectBuffer.h | 3 +- MobileGL/MG_Test/Program/ProgramTest.cpp | 2 +- .../MG_Util/BackendLoaders/OpenGL/Loader.cpp | 11 +- .../MG_Util/BackendLoaders/OpenGL/Loader.h | 12 +- .../GLToMG/TextureEnumConverter.cpp | 3 +- .../MGToMG/TextureEnumConverter.cpp | 265 ++++---- .../Converters/MGToMG/TextureEnumConverter.h | 3 +- MobileGL/MG_Util/Debug/Log.cpp | 4 +- MobileGL/MG_Util/Math/VectorTypes.cpp | 134 ++++ MobileGL/MG_Util/Math/VectorTypes.h | 12 +- .../ShaderTranspiler/ShaderCompiler.cpp | 7 +- .../MG_Util/ShaderTranspiler/ShaderCompiler.h | 3 +- .../EliminateFloatEqualsZeroPass.cpp | 51 +- .../EliminateFloatEqualsZeroPass.h | 6 +- .../MG_Util/Texture/PixelStoreProcessor.cpp | 12 +- .../MG_Util/Texture/PixelStoreProcessor.h | 3 +- .../Texture/TextureFormatProcessor.cpp | 601 +++++++++--------- .../MG_Util/Texture/TextureFormatProcessor.h | 3 +- 41 files changed, 908 insertions(+), 678 deletions(-) create mode 100644 MobileGL/MG_Util/Math/VectorTypes.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a6d3a95..ae5bf6c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,9 @@ set(SOURCE_FILES MobileGL/MG_Util/Debug/Log.cpp + MobileGL/MG_Util/Math/VectorTypes.cpp MobileGL/MG_Util/Metrics/TextureMetrics.cpp + MobileGL/MG_Util/Metrics/BufferMetrics.cpp MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 5f9aab62..261be744 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -151,6 +151,8 @@ namespace MobileGL::MG_Backend::DirectGLES { // Do real sync for (auto& bufferObject : buffersToSync) { + if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) continue; + const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); SharedPtr backendBufferObject; if (backendBufferIt == g_backendBufferObjects.end()) { @@ -778,8 +780,8 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, - MG_Util::ConvertGLEnumToString(filter).c_str()); + MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, + dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str()); MG_External::GLES::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); @@ -825,7 +827,7 @@ namespace MobileGL::MG_Backend::DirectGLES { void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG - DebugImpl::OpenGLScopeMarker marker(__func__); + DebugImpl::OpenGLScopeMarker marker(__func__); #endif DebugImpl::ErrorLopper errorLopper; MGLOG_D("%s: Backend", __func__); @@ -842,25 +844,24 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - if (!UpdateTextureBindingAtTarget(target)) - return; + if (!UpdateTextureBindingAtTarget(target)) return; -// GLint realInternalFormat; -// MG_External::GLES::glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &realInternalFormat); -// errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { -// MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); -// }); -// internalformat = (GLenum)realInternalFormat; + // GLint realInternalFormat; + // MG_External::GLES::glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, + // &realInternalFormat); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + // MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + // }); + // internalformat = (GLenum)realInternalFormat; auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); GLenum format = GL_DEPTH_COMPONENT; GLenum type = GL_UNSIGNED_INT; TextureImpl::GenerateTextureFormatInfo(mglInternalFormat, &internalformat, &format, &type); - MOBILEGL_ASSERT(format != GL_NONE && type != GL_NONE, "%s: cannot GenerateTextureFormatInfo(%s): out internalformat=%s, format=%s, type=%s", + MOBILEGL_ASSERT(format != GL_NONE && type != GL_NONE, + "%s: cannot GenerateTextureFormatInfo(%s): out internalformat=%s, format=%s, type=%s", MG_Util::ConvertTextureInternalFormatToString(mglInternalFormat).c_str(), MG_Util::ConvertGLEnumToString(internalformat).c_str(), - MG_Util::ConvertGLEnumToString(format).c_str(), - MG_Util::ConvertGLEnumToString(type).c_str()); + MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str()); TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); bool isDepthFormat = @@ -938,8 +939,7 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - if (!UpdateTextureBindingAtTarget(target)) - return; + if (!UpdateTextureBindingAtTarget(target)) return; BindCurrentFBO(FramebufferTarget::Read); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index ab6c263d..8acecd7e 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -7,24 +7,24 @@ // End of Source File Header #include "Managers.h" -#include "MG_Backend/Backends.h" -#include "MG_Util/Debug/Log.h" #include "Utils.h" #include "DirectGLES.h" -#include "MG_State/GLState/TextureState/TextureObjectBuffer.h" #include #include -#include #include #include -#include #include +#include +#include +#include #include #include #include namespace MobileGL::MG_Backend::DirectGLES { + constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true; + namespace BufferImpl { BackendBufferObject::BackendBufferObject() { #ifdef TRACY_ENABLE @@ -58,8 +58,10 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("Syncing buffer object with backend ID %u to backend for state ID %u", m_backendBufferId, stateBufferObject->GetExternalIndex()); + // Decide sync method + // glBufferData Bool needsRegeneration = - !m_isInitialized || bufferSize > m_prevBufferSize || bufferSize < m_prevBufferSize / 2; + !m_isInitialized || (stateBufferObject->GetChangeBits() & BufferChangeBits::PreferReallocationBit); if (needsRegeneration) { MGLOG_D("Buffer size changed significantly or not initialized, regenerating buffer with ID: %u", @@ -67,22 +69,33 @@ namespace MobileGL::MG_Backend::DirectGLES { SyncToBackend_glBufferData(stateBufferObject); m_isInitialized = true; m_prevBufferSize = bufferSize; + stateBufferObject->ClearDirty(); return; } - switch (stateBufferObject->GetUsage()) { - case BufferUsage::StaticDraw: - SyncToBackend_glBufferSubData(stateBufferObject); - break; - case BufferUsage::DynamicDraw: - case BufferUsage::StreamDraw: - SyncToBackend_glMapBufferRange(stateBufferObject); - break; - default: - SyncToBackend_glBufferSubData(stateBufferObject); - break; + // glMapBufferRange or glBufferSubData + Bool useInvalidationMap = !(stateBufferObject->GetChangeBits() & BufferChangeBits::ForbidInvalidationBit); + Bool useUnsynchronizedMap = + !(stateBufferObject->GetChangeBits() & BufferChangeBits::ForbidUnsynchronizationBit); + Bool useMapBufferRange = useInvalidationMap || useUnsynchronizedMap; + + if (!useMapBufferRange && PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC) { + auto usage = stateBufferObject->GetUsage(); + if (usage == BufferUsage::DynamicDraw || usage == BufferUsage::StreamDraw || + usage == BufferUsage::StreamCopy || usage == BufferUsage::DynamicCopy) { + useMapBufferRange = true; + } } + if (useMapBufferRange) { + MGLOG_D("Using glMapBufferRange to sync buffer with ID: %u", m_backendBufferId); + SyncToBackend_glMapBufferRange(stateBufferObject, useInvalidationMap, useUnsynchronizedMap); + } else { + MGLOG_D("Using glBufferSubData to sync buffer with ID: %u", m_backendBufferId); + SyncToBackend_glBufferSubData(stateBufferObject); + } + + // Clear dirty state stateBufferObject->ClearDirty(); m_prevBufferSize = bufferSize; } @@ -102,8 +115,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); MG_External::GLES::glBufferData(TempBufferTarget, size, data, usage); - - stateBufferObject->ClearDirty(); } void BackendBufferObject::SyncToBackend_glBufferSubData( @@ -117,19 +128,21 @@ namespace MobileGL::MG_Backend::DirectGLES { const void* data = stateBufferObject->GetDataReadOnly()->data(); // dirty range: [range.start, range.end) - const auto& range = stateBufferObject->GetDirtyRange(); - if (range.end == 0) { + auto ranges = stateBufferObject->GetDirtyRanges(); + if (ranges.empty()) { MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId); return; } - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); - MG_External::GLES::glBufferSubData(TempBufferTarget, range.start, range.end - range.start, - reinterpret_cast(data) + range.start); + for (const auto& range : ranges) { + MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); + MG_External::GLES::glBufferSubData(TempBufferTarget, range.start, range.end - range.start, + reinterpret_cast(data) + range.start); + } } void BackendBufferObject::SyncToBackend_glMapBufferRange( - SharedPtr& stateBufferObject, Bool invalidate) { + SharedPtr& stateBufferObject, Bool invalidate, Bool unsynchronized) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -137,19 +150,27 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("Syncing buffer map (glMapBuffer) for object with ID : %u", m_backendBufferId); MGLOG_D("Mapping buffer with ID: %u", m_backendBufferId); - const auto& range = stateBufferObject->GetDirtyRange(); - if (range.end == 0) { + auto ranges = stateBufferObject->GetDirtyRanges(); + if (ranges.empty()) { MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId); return; } + SizeT minStart = ranges.GetOverallMinStart(); + SizeT maxEnd = ranges.GetOverallMaxEnd(); MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); - void* mappedData = - MG_External::GLES::glMapBufferRange(TempBufferTarget, range.start, range.end - range.start, - (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : 0) | GL_MAP_WRITE_BIT); + void* mappedData = MG_External::GLES::glMapBufferRange( + TempBufferTarget, minStart, maxEnd - minStart, + (invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) | (unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) | + GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); const void* data = stateBufferObject->GetDataReadOnly()->data(); if (mappedData) { - Memcpy(mappedData, ((const char*)(data) + range.start), range.end - range.start); MGLOG_D("Mapped buffer data successfully for object with ID: %u", m_backendBufferId); + Memcpy(mappedData, reinterpret_cast(data) + minStart, maxEnd - minStart); + // Explicitly flush the dirty ranges + for (const auto& range : ranges) { + MG_External::GLES::glFlushMappedBufferRange(TempBufferTarget, range.start - minStart, + range.end - range.start); + } MG_External::GLES::glUnmapBuffer(TempBufferTarget); } else { MGLOG_E("Failed to map buffer with ID: %u", m_backendBufferId); @@ -328,7 +349,8 @@ namespace MobileGL::MG_Backend::DirectGLES { // 4. Mipmap levels changed if (!stateTextureObject->IsComplete()) { - MGLOG_D("Texture object with ID: %u is not complete, skipping sync.", stateTextureObject->GetExternalIndex()); + MGLOG_D("Texture object with ID: %u is not complete, skipping sync.", + stateTextureObject->GetExternalIndex()); return; } @@ -376,39 +398,42 @@ namespace MobileGL::MG_Backend::DirectGLES { auto* pData = (levelDirty && levelByteSize != 0) ? textureMipmapObject->MapMipmapData(uploadTarget, level) : nullptr; - MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", __func__, - MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, - levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, levelDirty ? "true" : "false"); + MGLOG_D( + "%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", + __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, + levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, + levelDirty ? "true" : "false"); errorLopper.Clear(); MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); auto textureTarget = stateTextureObject->GetTarget(); // TODO: handle more texture types switch (textureTarget) { - case TextureTarget::Texture2D: - case TextureTarget::TextureCubeMap: - { - MG_External::GLES::glTexImage2D(glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), - static_cast(levelTexelSize.y()), 0, glFormat, - glType, pData); - break; - } - case TextureTarget::Texture3D: { - MG_External::GLES::glTexImage3D(glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), - static_cast(levelTexelSize.y()), - static_cast(levelTexelSize.z()), - 0, glFormat, - glType, pData); - break; - } - default: { - MGLOG_E("Unhandled texture target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); - } + case TextureTarget::Texture2D: + case TextureTarget::TextureCubeMap: { + MG_External::GLES::glTexImage2D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + 0, glFormat, glType, pData); + break; } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, glInternalFormat, glFormat, glType, pData](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, type=%s, pixels=%p", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), + case TextureTarget::Texture3D: { + MG_External::GLES::glTexImage3D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); + break; + } + default: { + MGLOG_E("Unhandled texture target %s", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + } + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, + glInternalFormat, glFormat, glType, pData](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " + "type=%s, pixels=%p", + func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), MG_Util::ConvertGLEnumToString(glFormat).c_str(), @@ -493,8 +518,8 @@ namespace MobileGL::MG_Backend::DirectGLES { auto backendId = backendBufferObject->GetBackendBufferId(); GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, - &glFormat, &glType); + TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, + &glType); MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); break; @@ -1129,8 +1154,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Int width = static_cast(stateRBOObject->GetWidth()); Int height = static_cast(stateRBOObject->GetHeight()); GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(internalFormat, &glInternalFormat, - &glFormat, &glType); + TextureImpl::GenerateTextureFormatInfo(internalFormat, &glInternalFormat, &glFormat, &glType); MG_External::GLES::glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, static_cast(width), static_cast(height)); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index c3ac25eb..506e0cb8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -28,7 +28,7 @@ namespace MobileGL::MG_Backend::DirectGLES { void SyncToBackend_glBufferData(SharedPtr& stateBufferObject); void SyncToBackend_glBufferSubData(SharedPtr& stateBufferObject); void SyncToBackend_glMapBufferRange(SharedPtr& stateBufferObject, - Bool invalidate = true); + Bool invalidate = true, Bool unsynchronized = true); Uint m_backendBufferId = 0; SizeT m_prevBufferSize = 0; @@ -58,12 +58,9 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace TextureImpl { inline Bool IsSupportedTextureTarget(TextureTarget target) { - if (target == TextureTarget::Texture1D || - target == TextureTarget::TextureRectangle || - target == TextureTarget::Texture2DMultisampleArray || - target == TextureTarget::Texture1DArray || - target == TextureTarget::Texture2DMultisample || - target == TextureTarget::Texture2DArray) + if (target == TextureTarget::Texture1D || target == TextureTarget::TextureRectangle || + target == TextureTarget::Texture2DMultisampleArray || target == TextureTarget::Texture1DArray || + target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DArray) return false; return true; } diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index f8cb2fee..65435cb8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -74,13 +74,10 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif using namespace MobileGL::MG_Util::TextureFormatProcessor; - auto options = - (MG_External::GLES::g_glesCaps.hasNorm16Texture) ? PixelFormatNormalizeOptionBit::None : PixelFormatNormalizeOptionBit::NoNorm16; - NormalizePixelFormat( - MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat), - options, - outInternalFormat, - outFormat, outType); + auto options = (MG_External::GLES::g_glesCaps.hasNorm16Texture) ? PixelFormatNormalizeOptionBit::None + : PixelFormatNormalizeOptionBit::NoNorm16; + NormalizePixelFormat(MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat), options, + outInternalFormat, outFormat, outType); } } // namespace TextureImpl diff --git a/MobileGL/MG_Impl/EGLImpl/EGLWrapper/EGLWrapper.cpp b/MobileGL/MG_Impl/EGLImpl/EGLWrapper/EGLWrapper.cpp index 7ea342de..d187c756 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLWrapper/EGLWrapper.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLWrapper/EGLWrapper.cpp @@ -88,7 +88,7 @@ namespace MobileGL { return MG_External::EGL::eglQuerySurface(display, surface, attribute, value); } - char const * QueryString(EGLDisplay display, EGLint name) { + char const* QueryString(EGLDisplay display, EGLint name) { return MG_External::EGL::eglQueryString(display, name); } @@ -122,7 +122,7 @@ namespace MobileGL { } EGLSurface CreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, - const EGLint* attrib_list) { + const EGLint* attrib_list) { return MG_External::EGL::eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); } @@ -170,51 +170,45 @@ namespace MobileGL { return (__eglMustCastToProperFunctionPointerType)proc; } - EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib * attrib_list) { + EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { return MG_External::EGL::eglCreateSync(dpy, type, attrib_list); } - EGLBoolean DestroySync(EGLDisplay dpy, EGLSync sync) { return MG_External::EGL::eglDestroySync(dpy, sync); } - EGLint ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout) { return MG_External::EGL::eglClientWaitSync(dpy, sync, flags, timeout); } - - EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib * value) { + EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { return MG_External::EGL::eglGetSyncAttrib(dpy, sync, attribute, value); } - - EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list) { + EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { return MG_External::EGL::eglCreateImage(dpy, ctx, target, buffer, attrib_list); } - EGLBoolean DestroyImage(EGLDisplay dpy, EGLImage image) { return MG_External::EGL::eglDestroyImage(dpy, image); } - - EGLDisplay GetPlatformDisplay(EGLenum platform, void * native_display, const EGLAttrib * attrib_list) { + EGLDisplay GetPlatformDisplay(EGLenum platform, void* native_display, const EGLAttrib* attrib_list) { return MG_External::EGL::eglGetPlatformDisplay(platform, native_display, attrib_list); } - - EGLSurface CreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void * native_window, const EGLAttrib * attrib_list) { + EGLSurface CreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { return MG_External::EGL::eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list); } - - EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void * native_pixmap, const EGLAttrib * attrib_list) { + EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, + const EGLAttrib* attrib_list) { return MG_External::EGL::eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list); } - EGLBoolean WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) { return MG_External::EGL::eglWaitSync(dpy, sync, flags); } diff --git a/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.cpp index bcd93d6a..d3cc5508 100644 --- a/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.cpp @@ -98,7 +98,7 @@ namespace MobileGL { return EGL_TRUE; } - char const * QueryString(EGLDisplay display, EGLint name) { + char const* QueryString(EGLDisplay display, EGLint name) { return ""; } @@ -132,7 +132,7 @@ namespace MobileGL { } EGLSurface CreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, - const EGLint* attrib_list) { + const EGLint* attrib_list) { return (EGLSurface)1; } diff --git a/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h b/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h index a72edbf4..c1f89733 100644 --- a/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h +++ b/MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h @@ -29,7 +29,7 @@ namespace MobileGL { EGLBoolean BindAPI(EGLenum api); EGLSurface GetCurrentSurface(EGLint readdraw); EGLBoolean QuerySurface(EGLDisplay display, EGLSurface surface, EGLint attribute, EGLint* value); - char const * QueryString(EGLDisplay display, EGLint name); + char const* QueryString(EGLDisplay display, EGLint name); EGLBoolean SwapInterval(EGLDisplay dpy, EGLint interval); EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw); EGLSurface CreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list); @@ -39,7 +39,7 @@ namespace MobileGL { EGLSurface CreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint* attrib_list); EGLSurface CreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, - const EGLint* attrib_list); + const EGLint* attrib_list); EGLBoolean GetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config); EGLDisplay GetCurrentDisplay(void); EGLenum QueryAPI(void); @@ -49,15 +49,18 @@ namespace MobileGL { EGLBoolean WaitGL(void); EGLBoolean WaitNative(EGLint engine); __eglMustCastToProperFunctionPointerType GetProcAddress(const char* name); - EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib * attrib_list); + EGLSync CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list); EGLBoolean DestroySync(EGLDisplay dpy, EGLSync sync); EGLint ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); - EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib * value); - EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list); + EGLBoolean GetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value); + EGLImage CreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list); EGLBoolean DestroyImage(EGLDisplay dpy, EGLImage image); - EGLDisplay GetPlatformDisplay(EGLenum platform, void * native_display, const EGLAttrib * attrib_list); - EGLSurface CreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void * native_window, const EGLAttrib * attrib_list); - EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void * native_pixmap, const EGLAttrib * attrib_list); + EGLDisplay GetPlatformDisplay(EGLenum platform, void* native_display, const EGLAttrib* attrib_list); + EGLSurface CreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list); + EGLSurface CreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, + const EGLAttrib* attrib_list); EGLBoolean WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags); } // namespace MG_Impl::EGLImpl } // namespace MobileGL diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index 3e06d5a6..a6c92001 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -525,8 +525,7 @@ namespace MobileGL { auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); point.Bind(bufferObject); point.SetRange(Range1D(0, bufferObject->GetSize())); - MGLOG_D("%s: set range (0, %d)", __func__, - bufferObject->GetSize()); + MGLOG_D("%s: set range (0, %d)", __func__, bufferObject->GetSize()); } void BindBufferRange_State(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { @@ -575,6 +574,7 @@ namespace MobileGL { return MapBuffer_State(target, access); } + // FIXME: this should be a "backend" function void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { CopyBufferSubData_State(readTarget, writeTarget, readOffset, writeOffset, size); diff --git a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h index 981de9b9..fe1f0339 100644 --- a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h +++ b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h @@ -12,16 +12,22 @@ namespace MobileGL { namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ - void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); + void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, + GLsizei stride); void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); - void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); + void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, + const void* indices, GLint basevertex); void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); - void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); - void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex); - void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLuint baseinstance); + void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, + GLsizei instancecount, GLint basevertex, GLuint baseinstance); + void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, + GLsizei instancecount, GLint basevertex); + void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, + GLsizei instancecount, GLuint baseinstance); void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount); void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect); - void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); + void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, + GLuint baseinstance); void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); void DrawArraysIndirect(GLenum mode, const void* indirect); void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex); diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index b907e57d..e538ab9e 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -516,15 +516,15 @@ namespace MobileGL { void Uniform_State(MG_State::GLState::ProgramObject& programObject, GLuint location, T* value, SizeT byteOffsetInsideUniform = 0) { if (!programObject.IsUniformOpaqueAtLocation(location)) { - MGLOG_D("%s: program = %d, location = %d, maxLocation = %d", __func__, - programObject.GetExternalIndex(), location, programObject.GetMaxUniformLocation()); + MGLOG_D("%s: program = %d, location = %d, maxLocation = %d", __func__, programObject.GetExternalIndex(), + location, programObject.GetMaxUniformLocation()); auto size = programObject.GetUniformSizesInBytes(location); auto offset = programObject.GetUniformOffset(location); MOBILEGL_ASSERT(size >= ItemCount * sizeof(T), "Uniform size mismatch, expected at least %zu bytes, got %zu bytes.", ItemCount * sizeof(T), size); - MGLOG_D("%s: program = %d, location = %d, byteOffset = %d", __func__, - programObject.GetExternalIndex(), location, offset + byteOffsetInsideUniform); + MGLOG_D("%s: program = %d, location = %d, byteOffset = %d", __func__, programObject.GetExternalIndex(), + location, offset + byteOffsetInsideUniform); Memcpy((char*)programObject.MapUBO() + offset + byteOffsetInsideUniform, value, ItemCount * sizeof(T)); } else { auto* ttype = programObject.GetUniformTType(location); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 137a2c66..911fca93 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -63,8 +63,7 @@ namespace MobileGL { // TextureInternalFormat textureInternalFormat = // MG_Util::ConvertGLEnumToTextureInternalFormat(format); MGLOG_D("TexSubImage2D_State: target = %s, level = %d, (%d, %d), format = %s, pixels = %p", - MG_Util::ConvertGLEnumToString(target).c_str(), level, - width, height, + MG_Util::ConvertGLEnumToString(target).c_str(), level, width, height, MG_Util::ConvertTextureInputFormatToString(textureInputFormat).c_str(), pixels); // ===================== Error Checking ============================== if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; @@ -502,14 +501,18 @@ namespace MobileGL { GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) { MGLOG_D( "%s called with target: %s, level: %d, internalformat: %s, width: %d, height: %d, depth: %d, " - "border: %d, format: %s, type: %s (%u), pixels: %p", __func__, - MG_Util::ConvertTextureUploadTargetToString(MG_Util::ConvertGLEnumToTextureUploadTarget(target)).c_str(), + "border: %d, format: %s, type: %s (%u), pixels: %p", + __func__, + MG_Util::ConvertTextureUploadTargetToString(MG_Util::ConvertGLEnumToTextureUploadTarget(target)) + .c_str(), level, MG_Util::ConvertTextureInternalFormatToString( - MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)).c_str(), + MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)) + .c_str(), width, height, depth, border, MG_Util::ConvertTextureInputFormatToString(MG_Util::ConvertGLEnumToTextureInputFormat(format)).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(MG_Util::ConvertGLEnumToTexturePixelDataType(type)).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(MG_Util::ConvertGLEnumToTexturePixelDataType(type)) + .c_str(), type, pixels); // ======================= Converting ================================ TextureUploadTarget textureUploadingTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); @@ -580,13 +583,13 @@ namespace MobileGL { reinterpret_cast(pixels); } - MOBILEGL_ASSERT(nullptr != dynamic_cast(textureObject.get()), "Texture object here should always be an object with mipmap"); auto textureMipmapObject = static_cast(textureObject.get()); // Allocate in TextureObject - textureMipmapObject->AllocateStorage(textureUploadingTarget, level, {{width, height, depth}, internalBytes}); + textureMipmapObject->AllocateStorage(textureUploadingTarget, level, + {{width, height, depth}, internalBytes}); if (!originalPixels) { MGLOG_D("%s: No input pixel and no PBO bound, no pixel transfer", __func__); @@ -601,8 +604,8 @@ namespace MobileGL { if (processedPixels && imageSize > 0) { if (imageSize != internalBytes) { MGLOG_W("%s: Processed pixel data size (%zu) does not match expected size (%zu). " - "This may indicate an alignment or processing issue.", __func__, - imageSize, internalBytes); + "This may indicate an alignment or processing issue.", + __func__, imageSize, internalBytes); } const SizeT copySize = std::min(imageSize, internalBytes); @@ -623,20 +626,16 @@ namespace MobileGL { TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); - MGLOG_D( - "%s called with target: %s (%s), level: %d, internalformat: %s (%s), width: %d, height: %d, " - "border: %d, format: %s (%s), type: %s (%s), pixels: %p", __func__, - MG_Util::ConvertTextureUploadTargetToString(textureUploadingTarget).c_str(), - MG_Util::ConvertGLEnumToString(target).c_str(), - level, + MGLOG_D("%s called with target: %s (%s), level: %d, internalformat: %s (%s), width: %d, height: %d, " + "border: %d, format: %s (%s), type: %s (%s), pixels: %p", + __func__, MG_Util::ConvertTextureUploadTargetToString(textureUploadingTarget).c_str(), + MG_Util::ConvertGLEnumToString(target).c_str(), level, MG_Util::ConvertTextureInternalFormatToString(textureInternalFormat).c_str(), - MG_Util::ConvertGLEnumToString(internalformat).c_str(), - width, height, border, + MG_Util::ConvertGLEnumToString(internalformat).c_str(), width, height, border, MG_Util::ConvertTextureInputFormatToString(textureInputFormat).c_str(), MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertTexturePixelDataTypeToString(texturePixelDataType).c_str(), - MG_Util::ConvertGLEnumToString(type).c_str(), - pixels); + MG_Util::ConvertGLEnumToString(type).c_str(), pixels); // ===================== Error Checking ============================== if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; @@ -661,7 +660,8 @@ namespace MobileGL { // indicated by type. // ======================= Processing ================================ - textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType); + textureInternalFormat = + MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType); SharedPtr textureObject = nullptr; Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadingTarget); if (isProxy) { @@ -1227,11 +1227,12 @@ namespace MobileGL { } void CopyTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, - GLsizei height, GLint border) { + GLsizei height, GLint border) { GLenum outInternalFormat, format, type; - MG_Util::TextureFormatProcessor::NormalizePixelFormat(internalformat, 0, &outInternalFormat, &format, &type); + MG_Util::TextureFormatProcessor::NormalizePixelFormat(internalformat, 0, &outInternalFormat, &format, + &type); const auto pixelUnpackBufferObject = - MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); TexImage2D_State(target, level, outInternalFormat, width, height, border, format, type, nullptr); MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).Bind(pixelUnpackBufferObject); } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h index a2f88891..97b4047d 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h @@ -20,10 +20,10 @@ namespace MobileGL { const GLvoid* pixels); void TexParameterf(GLenum target, GLenum pname, GLfloat param); void TexParameteri(GLenum target, GLenum pname, GLint param); - void TexParameterfv(GLenum target, GLenum pname, const GLfloat * params); - void TexParameteriv(GLenum target, GLenum pname, const GLint * params); - void TexParameterIiv(GLenum target, GLenum pname, const GLint * params); - void TexParameterIuiv(GLenum target, GLenum pname, const GLuint * params); + void TexParameterfv(GLenum target, GLenum pname, const GLfloat* params); + void TexParameteriv(GLenum target, GLenum pname, const GLint* params); + void TexParameterIiv(GLenum target, GLenum pname, const GLint* params); + void TexParameterIuiv(GLenum target, GLenum pname, const GLuint* params); void TexImage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp index 0e49813f..e2e86f49 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp @@ -170,7 +170,8 @@ namespace MobileGL::MG_Impl::GLImpl { } return true; } - Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, TextureInternalFormat internalFormat, + Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, + TextureInternalFormat internalFormat, TexturePixelDataType type) { if (type == TexturePixelDataType::UnsignedByte332 || type == TexturePixelDataType::UnsignedByte233Rev || type == TexturePixelDataType::UnsignedShort565 || type == TexturePixelDataType::UnsignedShort565Rev || @@ -178,7 +179,8 @@ namespace MobileGL::MG_Impl::GLImpl { if (format != TextureInputFormat::RGB) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeShared("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", + MakeShared("MG_Impl/GLImpl", + "ValidateTextureInternalFormatCompatibleWithInput", "Invalid format for the given type")); return false; } @@ -193,7 +195,8 @@ namespace MobileGL::MG_Impl::GLImpl { if (format != TextureInputFormat::RGBA && format != TextureInputFormat::BGRA) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeShared("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", + MakeShared("MG_Impl/GLImpl", + "ValidateTextureInternalFormatCompatibleWithInput", "Invalid format for the given type")); return false; } @@ -206,7 +209,8 @@ namespace MobileGL::MG_Impl::GLImpl { if (format != TextureInputFormat::DepthComponent) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeShared("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", + MakeShared("MG_Impl/GLImpl", + "ValidateTextureInternalFormatCompatibleWithInput", "Invalid format for depth component internal format")); return false; } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/Validators.h b/MobileGL/MG_Impl/GLImpl/Texture/Validators.h index f1caac2e..2ee4f0a4 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.h @@ -23,7 +23,8 @@ namespace MobileGL::MG_Impl::GLImpl { Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth); Bool ValidateTextureInternalFormat(TextureInternalFormat format); Bool ValidateTextureBorderNumber(Int border); - Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, TextureInternalFormat internalFormat, + Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, + TextureInternalFormat internalFormat, TexturePixelDataType type); Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level); Bool ValidateTextureObject(SharedPtr textureObject); diff --git a/MobileGL/MG_Impl/GetProcAddress.cpp b/MobileGL/MG_Impl/GetProcAddress.cpp index 559856ab..a9f6f699 100644 --- a/MobileGL/MG_Impl/GetProcAddress.cpp +++ b/MobileGL/MG_Impl/GetProcAddress.cpp @@ -7,9 +7,9 @@ // End of Source File Header #include "GetProcAddress.h" -#define GETPROC(name, var) \ - if (strcmp(#name, var) == 0) { \ - return (void*)name; \ +#define GETPROC(name, var) \ + if (strcmp(#name, var) == 0) { \ + return (void*)name; \ } namespace MobileGL { diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp index 5e70ce51..6eb77b61 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -14,14 +14,18 @@ namespace MobileGL { namespace GLState { BufferObject::BufferObject(Uint externalIndex) : m_externalIndex(externalIndex), m_size(0), m_usage(BufferUsage::StaticDraw), m_isMapped(false), - m_mappingAccess(BufferMappingAccessBit::Null), m_dirtyRange({0, 0}), m_mappedRange({0, 0}), - m_dataPtr(MakeShared()) {} + m_mappingAccess(BufferMappingAccessBit::Null), + m_change(BufferChangeBits::DirtyBit | BufferChangeBits::PreferReallocationBit), m_mappedRange({0, 0}), + m_dataPtr(MakeShared()) { + m_change.DirtyRanges.reserve(BufferChange::DEFAULT_RESERVED_DIRTY_RANGES_COUNT); + } void BufferObject::Resize(SizeT size) { m_size = size; m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve m_dataPtr->resize(size); - m_dirtyRange = {0, 0}; + m_change.Bits |= BufferChangeBits::DirtyBit; + m_change.Bits |= BufferChangeBits::PreferReallocationBit; } void BufferObject::UploadData(DataPtr data, SizeT atOffset) { @@ -30,7 +34,14 @@ namespace MobileGL { data.size, m_size); MOBILEGL_ASSERT(!m_isMapped, "Cannot upload data while buffer is mapped."); Memcpy(m_dataPtr->data() + atOffset, data.data, data.size); - m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size); + m_change.DirtyRanges.Add({atOffset, atOffset + data.size}); + m_change.Bits |= BufferChangeBits::DirtyBit; + m_change.Bits |= BufferChangeBits::ForbidInvalidationBit; + m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit; + // This function may be called by `glBufferData`, but we still set the forbid bits above, + // because when `PreferReallocationBit` is set, those bits are ignored anyway. + // The bits can fit the `glBufferSubData` semantics + // (though `glBufferSubData` calls `UploadSubData` instead). } void BufferObject::SetUsage(BufferUsage usage) { @@ -44,7 +55,8 @@ namespace MobileGL { if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(), m_mappedRange.end - m_mappedRange.start); - m_dirtyRange.UnionUpdate(m_mappedRange.start, m_mappedRange.end); + m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end}); + m_change.Bits |= BufferChangeBits::DirtyBit; } m_stagingData.clear(); @@ -69,7 +81,8 @@ namespace MobileGL { "Flush range out of bounds: mappedRange.end (%zu) < end (%zu)", m_mappedRange.end, end); Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length); - m_dirtyRange.UnionUpdate(start, end); + m_change.DirtyRanges.Add({start, end}); + m_change.Bits |= BufferChangeBits::DirtyBit; } void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) { @@ -79,7 +92,10 @@ namespace MobileGL { atOffset, data.size, m_size); Memcpy(m_dataPtr->data() + atOffset, data.data, data.size); - m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size); + m_change.DirtyRanges.Add({atOffset, atOffset + data.size}); + m_change.Bits |= BufferChangeBits::DirtyBit; + m_change.Bits |= BufferChangeBits::ForbidInvalidationBit; + m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit; } void BufferObject::CopyDataFrom(const SharedPtr& src, SizeT srcOffset, SizeT dstOffset, @@ -95,7 +111,8 @@ namespace MobileGL { const Uint8* srcData = src->m_dataPtr->data() + srcOffset; Memcpy(m_dataPtr->data() + dstOffset, srcData, size); - m_dirtyRange.UnionUpdate(dstOffset, dstOffset + size); + m_change.DirtyRanges.Add({dstOffset, dstOffset + size}); + m_change.Bits |= BufferChangeBits::DirtyBit; } void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) { @@ -144,6 +161,14 @@ namespace MobileGL { m_ownsStagingData = false; return m_dataPtr->data() + range.start; } + + m_change.Bits |= !(access & BufferMappingAccessBit::InvalidateBuffer || + access & BufferMappingAccessBit::InvalidateRange) + ? BufferChangeBits::ForbidInvalidationBit + : BufferChangeBits::None; + m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized) + ? BufferChangeBits::ForbidUnsynchronizationBit + : BufferChangeBits::None; } const SharedPtr BufferObject::GetDataReadOnly() const { @@ -151,7 +176,8 @@ namespace MobileGL { } void BufferObject::ClearDirty() { - m_dirtyRange = {0, 0}; + m_change.DirtyRanges.clear(); + m_change.Bits = BufferChangeBits::None; } SizeT BufferObject::GetSize() const { @@ -162,8 +188,12 @@ namespace MobileGL { return m_usage; } - Range1D BufferObject::GetDirtyRange() const { - return m_dirtyRange; + const VecRange1D& BufferObject::GetDirtyRanges() const { + return m_change.DirtyRanges; + } + + Flags BufferObject::GetChangeBits() const { + return m_change.Bits; } Bool BufferObject::IsMapped() const { diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index 69bf74c9..99caf195 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -9,6 +9,7 @@ #pragma once #include "MG_Util/Types.h" #include +#include namespace MobileGL { enum class BufferTarget { @@ -55,6 +56,23 @@ namespace MobileGL { Coherent = 0x80 }; + enum class BufferChangeBits : Uint8 { + None = 0, + DirtyBit = 1 << 0, // When not set, bits below are ignored and nothing should be synced to backend + PreferReallocationBit = + 1 << 1, // <=> `glBufferData`; When set, ForbidInvalidationBit and ForbidUnsynchronizationBit are ignored + ForbidInvalidationBit = 1 << 2, // Indidate that invalidation flags were not used during mapping, else we're + // allowed to act as `GL_MAP_INVALIDATE_*` in backend + ForbidUnsynchronizationBit = 1 << 3, // (the same description as above, but for unsynchronization) + }; + + struct BufferChange { + static constexpr int DEFAULT_RESERVED_DIRTY_RANGES_COUNT = 50; + + Flags Bits = BufferChangeBits::None; + VecRange1D DirtyRanges; + }; + namespace MG_State { namespace GLState { class BufferObject { @@ -77,11 +95,12 @@ namespace MobileGL { Bool IsMapped() const; SizeT GetSize() const; BufferUsage GetUsage() const; - Range1D GetDirtyRange() const; Range1D GetMappedRange() const; const SharedPtr GetDataReadOnly() const; Flags GetMappingAccess() const; Uint GetExternalIndex() const; + const VecRange1D& GetDirtyRanges() const; + Flags GetChangeBits() const; private: const Uint m_externalIndex = 0; @@ -90,7 +109,7 @@ namespace MobileGL { SharedPtr m_dataPtr; Bool m_isMapped; Flags m_mappingAccess; - Range1D m_dirtyRange; + BufferChange m_change; Range1D m_mappedRange; Vector m_stagingData; Bool m_ownsStagingData; diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 09240926..53112caf 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -453,7 +453,7 @@ namespace MobileGL { MGLOG_D("ProgramObject %u: GenerateBinary - generated %zu SPIR-V modules", m_externalIndex, m_generatedSpirv.size()); - for (auto& spv: m_generatedSpirv) { + for (auto& spv : m_generatedSpirv) { auto success = ShaderCompiler::SanitizeAndOptimizeBinary(spv, spv); MOBILEGL_ASSERT(success, "SanitizeBinary failed"); } diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp index bcd0ed7b..62875667 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp @@ -42,7 +42,8 @@ namespace MobileGL { } else { m_compileStatus = false; m_infoLog = result.error().log; - MGLOG_D("ShaderObject::Compile: Shader %d compilation failed.\nSource:\n%s\nInfoLog:\n%s\nSetting m_compileStatus = false as a result.", + MGLOG_D("ShaderObject::Compile: Shader %d compilation failed.\nSource:\n%s\nInfoLog:\n%s\nSetting " + "m_compileStatus = false as a result.", m_externalIndex, m_source.c_str(), m_infoLog.c_str()); } } diff --git a/MobileGL/MG_State/GLState/TextureState/MipmapStorage.h b/MobileGL/MG_State/GLState/TextureState/MipmapStorage.h index 683f3dcf..5976986d 100644 --- a/MobileGL/MG_State/GLState/TextureState/MipmapStorage.h +++ b/MobileGL/MG_State/GLState/TextureState/MipmapStorage.h @@ -25,6 +25,7 @@ namespace MobileGL { SizeT GetByteSize(Uint level) const; void MarkDirty(Uint level, bool dirty); bool IsDirty(Uint level) const; + protected: Vector m_texelSizes; Vector> m_data; diff --git a/MobileGL/MG_State/GLState/TextureState/MipmapUploadTargetArray.h b/MobileGL/MG_State/GLState/TextureState/MipmapUploadTargetArray.h index 2d5a0627..c0fc19dd 100644 --- a/MobileGL/MG_State/GLState/TextureState/MipmapUploadTargetArray.h +++ b/MobileGL/MG_State/GLState/TextureState/MipmapUploadTargetArray.h @@ -19,7 +19,9 @@ namespace MobileGL { template class MipmapUploadTargetArray { public: - MipmapUploadTargetArray() { static_assert(TargetCount > 0, "Upload target count must be greater than zero"); } + MipmapUploadTargetArray() { + static_assert(TargetCount > 0, "Upload target count must be greater than zero"); + } void AllocateLevel(Uint targetIndex, Uint level, MipmapInput input) { MOBILEGL_ASSERT(targetIndex < TargetCount, "AllocateLevel: target invalid"); diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.h b/MobileGL/MG_State/GLState/TextureState/TextureObject.h index 960835d7..dab1b41c 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -81,7 +81,8 @@ namespace MobileGL { class TextureObjectMipmap : public TextureObjectBase { public: - TextureObjectMipmap(TextureTarget target, Uint externalIndex): TextureObjectBase(target, externalIndex) {} + TextureObjectMipmap(TextureTarget target, Uint externalIndex) + : TextureObjectBase(target, externalIndex) {} TextureStorageType GetStorageType() const override { return TextureStorageType::Mipmap; } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h index cf8c3e00..f5d7928e 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h @@ -18,7 +18,8 @@ namespace MobileGL { TextureStorageType GetStorageType() const override { return TextureStorageType::Buffer; } explicit TextureObjectBuffer(Uint externalIndex); const Vector& GetUploadTargets() const override { return m_uploadTargets; } - BindingSlot& GetBufferBindingSlot(TextureUploadTarget target = TextureUploadTarget::TextureBuffer); + BindingSlot& GetBufferBindingSlot( + TextureUploadTarget target = TextureUploadTarget::TextureBuffer); protected: Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override; diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 20b92a1d..cd183e26 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -925,7 +925,7 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitVertexIn) { // auto& vertexSpirv = spirvs[1]; // 0 - fragment, 1 - vertex char* pSrcVertIn = nullptr; const char* needle = "layout(location = 2) in vec2 UV0;"; - for (auto spirv: spirvs) { + for (auto spirv : spirvs) { MG_Util::ShaderTranspiler::SpvcSession spvcSession(spirv); spvc_compiler_options options; spvcSession.CreateOptions(&options); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 2a3be359..7e954f9d 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -457,7 +457,10 @@ namespace MobileGL { void *libGLES = nullptr, *libEGL = nullptr; static const char* LibPathPrefixes[] = { - "/opt/vc/lib/", "/usr/local/lib/", "/usr/lib/", "/usr/lib/x86_64-linux-gnu/", + "/opt/vc/lib/", + "/usr/local/lib/", + "/usr/lib/", + "/usr/lib/x86_64-linux-gnu/", "", // We should put this to the end of the list to avoid breaking `LD_LIBRARY_PATH` usage nullptr}; static const char* LibExts[] = {"so", "so.1", "so.2", "dylib", "dll", nullptr}; @@ -531,8 +534,10 @@ namespace MobileGL { } MGLOG_I("OpenGL ES capabilities:"); - MG_External::GLES::glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &MG_External::GLES::g_glesCaps.uniformBufferOffsetAlignment); - MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", MG_External::GLES::g_glesCaps.uniformBufferOffsetAlignment); + MG_External::GLES::glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, + &MG_External::GLES::g_glesCaps.uniformBufferOffsetAlignment); + MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", + MG_External::GLES::g_glesCaps.uniformBufferOffsetAlignment); } void InitGLES() { diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index 2f7b49f0..7b548b7e 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -511,16 +511,18 @@ namespace MobileGL { typedef EGLBoolean (*eglWaitClient_PTR)(); typedef EGLBoolean (*eglWaitGL_PTR)(); typedef EGLBoolean (*eglWaitNative_PTR)(EGLint engine); - typedef EGLSync (*eglCreateSync_PTR)(EGLDisplay dpy, EGLenum type, const EGLAttrib * attrib_list); + typedef EGLSync (*eglCreateSync_PTR)(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list); typedef EGLBoolean (*eglDestroySync_PTR)(EGLDisplay dpy, EGLSync sync); typedef EGLint (*eglClientWaitSync_PTR)(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); - typedef EGLBoolean (*eglGetSyncAttrib_PTR)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib * value); - typedef EGLImage (*eglCreateImage_PTR)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib * attrib_list); + typedef EGLBoolean (*eglGetSyncAttrib_PTR)(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLAttrib* value); + typedef EGLImage (*eglCreateImage_PTR)(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLAttrib* attrib_list); typedef EGLBoolean (*eglDestroyImage_PTR)(EGLDisplay dpy, EGLImage image); - typedef EGLSurface (*eglCreatePlatformPixmapSurface_PTR)(EGLDisplay dpy, EGLConfig config, void * native_pixmap, const EGLAttrib * attrib_list); + typedef EGLSurface (*eglCreatePlatformPixmapSurface_PTR)(EGLDisplay dpy, EGLConfig config, + void* native_pixmap, const EGLAttrib* attrib_list); typedef EGLBoolean (*eglWaitSync_PTR)(EGLDisplay dpy, EGLSync sync, EGLint flags); - EGL_FUNC_DECL(eglBindAPI) EGL_FUNC_DECL(eglBindTexImage) EGL_FUNC_DECL(eglChooseConfig) diff --git a/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp index 3a899ccf..e3ce180c 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp @@ -234,7 +234,8 @@ namespace MobileGL { case GL_RGBA: return TextureInternalFormat::RGBA; default: - MGLOG_D("%s: unknown internal format %s", __func__, MG_Util::ConvertGLEnumToString(internalformat).c_str()); + MGLOG_D("%s: unknown internal format %s", __func__, + MG_Util::ConvertGLEnumToString(internalformat).c_str()); return TextureInternalFormat::Unknown; } } diff --git a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp index ae103088..6bb12ac4 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp @@ -53,144 +53,145 @@ namespace MobileGL { } } - TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat, TextureInputFormat format, TexturePixelDataType type) { + TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat, + TextureInputFormat format, TexturePixelDataType type) { switch (internalformat) { - case TextureInternalFormat::R8: - case TextureInternalFormat::R8Snorm: - case TextureInternalFormat::R16: - case TextureInternalFormat::R16Snorm: - case TextureInternalFormat::RG8: - case TextureInternalFormat::RG8Snorm: - case TextureInternalFormat::RG16: - case TextureInternalFormat::RG16Snorm: - case TextureInternalFormat::R3G3B2: - case TextureInternalFormat::RGB4: - case TextureInternalFormat::RGB5: - case TextureInternalFormat::RGB8: - case TextureInternalFormat::RGB8Snorm: - case TextureInternalFormat::RGB10: - case TextureInternalFormat::RGB12: - case TextureInternalFormat::RGB16Snorm: - case TextureInternalFormat::RGBA2: - case TextureInternalFormat::RGBA4: - case TextureInternalFormat::RGB5A1: - case TextureInternalFormat::RGBA8: - case TextureInternalFormat::RGBA8Snorm: - case TextureInternalFormat::RGB10A2: - case TextureInternalFormat::RGB10A2UI: - case TextureInternalFormat::RGBA12: - case TextureInternalFormat::RGBA16: - case TextureInternalFormat::SRGB8: - case TextureInternalFormat::SRGB8Alpha8: - case TextureInternalFormat::R16F: - case TextureInternalFormat::RG16F: - case TextureInternalFormat::RGB16F: - case TextureInternalFormat::RGBA16F: - case TextureInternalFormat::R32F: - case TextureInternalFormat::RG32F: - case TextureInternalFormat::RGB32F: - case TextureInternalFormat::RGBA32F: - case TextureInternalFormat::R11FG11FB10F: - case TextureInternalFormat::RGB9E5: - case TextureInternalFormat::R8I: - case TextureInternalFormat::R8UI: - case TextureInternalFormat::R16I: - case TextureInternalFormat::R16UI: - case TextureInternalFormat::R32I: - case TextureInternalFormat::R32UI: - case TextureInternalFormat::RG8I: - case TextureInternalFormat::RG8UI: - case TextureInternalFormat::RG16I: - case TextureInternalFormat::RG16UI: - case TextureInternalFormat::RG32I: - case TextureInternalFormat::RG32UI: - case TextureInternalFormat::RGB8I: - case TextureInternalFormat::RGB8UI: - case TextureInternalFormat::RGB16I: - case TextureInternalFormat::RGB16UI: - case TextureInternalFormat::RGB32I: - case TextureInternalFormat::RGB32UI: - case TextureInternalFormat::RGBA8I: - case TextureInternalFormat::RGBA8UI: - case TextureInternalFormat::RGBA16I: - case TextureInternalFormat::RGBA16UI: - case TextureInternalFormat::RGBA32I: - case TextureInternalFormat::RGBA32UI: - case TextureInternalFormat::DepthComponent16: - case TextureInternalFormat::DepthComponent24: - case TextureInternalFormat::DepthComponent32: // not a standard format in OpenGL core profile - case TextureInternalFormat::DepthComponent32F: - case TextureInternalFormat::Depth24Stencil8: - case TextureInternalFormat::Depth32FStencil8: - return internalformat; - // probably we should assume unorm here? - case TextureInternalFormat::RGBA: { - switch (type) { - case TexturePixelDataType::UnsignedByte: - return TextureInternalFormat::RGBA8; - case TexturePixelDataType::UnsignedShort: - return TextureInternalFormat::RGBA16; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning original.", - __func__, - MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; - } - } - case TextureInternalFormat::RGB: { - switch (type) { - case TexturePixelDataType::UnsignedByte: - return TextureInternalFormat::RGB8; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning original.", - __func__, - MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; - } - } - case TextureInternalFormat::RG: { - switch (type) { - case TexturePixelDataType::UnsignedByte: - return TextureInternalFormat::RG8; - case TexturePixelDataType::UnsignedShort: - return TextureInternalFormat::RG16; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning original.", - __func__, - MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; - } - } - case TextureInternalFormat::Red: { - switch (type) { - case TexturePixelDataType::UnsignedByte: - return TextureInternalFormat::R8; - case TexturePixelDataType::UnsignedShort: - return TextureInternalFormat::R16; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning original.", - __func__, - MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; - } - } - default: { - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning original.", - __func__, - MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + case TextureInternalFormat::R8: + case TextureInternalFormat::R8Snorm: + case TextureInternalFormat::R16: + case TextureInternalFormat::R16Snorm: + case TextureInternalFormat::RG8: + case TextureInternalFormat::RG8Snorm: + case TextureInternalFormat::RG16: + case TextureInternalFormat::RG16Snorm: + case TextureInternalFormat::R3G3B2: + case TextureInternalFormat::RGB4: + case TextureInternalFormat::RGB5: + case TextureInternalFormat::RGB8: + case TextureInternalFormat::RGB8Snorm: + case TextureInternalFormat::RGB10: + case TextureInternalFormat::RGB12: + case TextureInternalFormat::RGB16Snorm: + case TextureInternalFormat::RGBA2: + case TextureInternalFormat::RGBA4: + case TextureInternalFormat::RGB5A1: + case TextureInternalFormat::RGBA8: + case TextureInternalFormat::RGBA8Snorm: + case TextureInternalFormat::RGB10A2: + case TextureInternalFormat::RGB10A2UI: + case TextureInternalFormat::RGBA12: + case TextureInternalFormat::RGBA16: + case TextureInternalFormat::SRGB8: + case TextureInternalFormat::SRGB8Alpha8: + case TextureInternalFormat::R16F: + case TextureInternalFormat::RG16F: + case TextureInternalFormat::RGB16F: + case TextureInternalFormat::RGBA16F: + case TextureInternalFormat::R32F: + case TextureInternalFormat::RG32F: + case TextureInternalFormat::RGB32F: + case TextureInternalFormat::RGBA32F: + case TextureInternalFormat::R11FG11FB10F: + case TextureInternalFormat::RGB9E5: + case TextureInternalFormat::R8I: + case TextureInternalFormat::R8UI: + case TextureInternalFormat::R16I: + case TextureInternalFormat::R16UI: + case TextureInternalFormat::R32I: + case TextureInternalFormat::R32UI: + case TextureInternalFormat::RG8I: + case TextureInternalFormat::RG8UI: + case TextureInternalFormat::RG16I: + case TextureInternalFormat::RG16UI: + case TextureInternalFormat::RG32I: + case TextureInternalFormat::RG32UI: + case TextureInternalFormat::RGB8I: + case TextureInternalFormat::RGB8UI: + case TextureInternalFormat::RGB16I: + case TextureInternalFormat::RGB16UI: + case TextureInternalFormat::RGB32I: + case TextureInternalFormat::RGB32UI: + case TextureInternalFormat::RGBA8I: + case TextureInternalFormat::RGBA8UI: + case TextureInternalFormat::RGBA16I: + case TextureInternalFormat::RGBA16UI: + case TextureInternalFormat::RGBA32I: + case TextureInternalFormat::RGBA32UI: + case TextureInternalFormat::DepthComponent16: + case TextureInternalFormat::DepthComponent24: + case TextureInternalFormat::DepthComponent32: // not a standard format in OpenGL core profile + case TextureInternalFormat::DepthComponent32F: + case TextureInternalFormat::Depth24Stencil8: + case TextureInternalFormat::Depth32FStencil8: + return internalformat; + // probably we should assume unorm here? + case TextureInternalFormat::RGBA: { + switch (type) { + case TexturePixelDataType::UnsignedByte: + return TextureInternalFormat::RGBA8; + case TexturePixelDataType::UnsignedShort: + return TextureInternalFormat::RGBA16; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), MG_Util::ConvertTextureInputFormatToString(format).c_str(), MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); return internalformat; } } + case TextureInternalFormat::RGB: { + switch (type) { + case TexturePixelDataType::UnsignedByte: + return TextureInternalFormat::RGB8; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } + case TextureInternalFormat::RG: { + switch (type) { + case TexturePixelDataType::UnsignedByte: + return TextureInternalFormat::RG8; + case TexturePixelDataType::UnsignedShort: + return TextureInternalFormat::RG16; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } + case TextureInternalFormat::Red: { + switch (type) { + case TexturePixelDataType::UnsignedByte: + return TextureInternalFormat::R8; + case TexturePixelDataType::UnsignedShort: + return TextureInternalFormat::R16; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } + default: { + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning " + "original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } } } // namespace MG_Util } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h index ca10139c..e04bdf66 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h @@ -13,6 +13,7 @@ namespace MobileGL { namespace MG_Util { TextureTarget ConvertTextureUploadTargetToTextureTarget(TextureUploadTarget target); - TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat, TextureInputFormat format, TexturePixelDataType type); + TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat, + TextureInputFormat format, TexturePixelDataType type); } // namespace MG_Util } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/Debug/Log.cpp b/MobileGL/MG_Util/Debug/Log.cpp index a0a6a88a..32ccc68a 100644 --- a/MobileGL/MG_Util/Debug/Log.cpp +++ b/MobileGL/MG_Util/Debug/Log.cpp @@ -91,9 +91,9 @@ namespace MobileGL { int n = std::vsnprintf(buffer, sizeof(buffer), fmt, args); std::string out = header + #if MOBILEGL_LOG_ENABLE_STACKTRACE - padding + + padding + #endif - std::string(buffer, n) + "\n"; + std::string(buffer, n) + "\n"; #if MOBILEGL_LOG_ENABLE_CONSOLE std::fwrite(out.c_str(), 1, out.size(), stdout); diff --git a/MobileGL/MG_Util/Math/VectorTypes.cpp b/MobileGL/MG_Util/Math/VectorTypes.cpp new file mode 100644 index 00000000..b0d43a65 --- /dev/null +++ b/MobileGL/MG_Util/Math/VectorTypes.cpp @@ -0,0 +1,134 @@ +// MobileGL - MobileGL/MG_Util/Math/VectorTypes.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include "VectorTypes.h" + +namespace MobileGL { + void VecRange1D::Add(const Range1D& newRange, Double ratio, SizeT* outMinStart, SizeT* outMaxEnd) { + if (this->empty()) { + this->push_back(newRange); + m_overallMaxEnd = newRange.end; + if (outMinStart) *outMinStart = this->front().start; + if (outMaxEnd) *outMaxEnd = m_overallMaxEnd; + return; + } + + auto it = std::lower_bound(this->begin(), this->end(), newRange.start, + [](const Range1D& a, SizeT valueStart) { return a.start < valueStart; }); + size_t pos = static_cast(std::distance(this->begin(), it)); + + auto calc_gap = [](const Range1D& a, const Range1D& b) -> SizeT { + return (b.start > a.end) ? (b.start - a.end) : 0; + }; + + auto calc_threshold = [ratio](const Range1D& a, const Range1D& b) -> SizeT { + SizeT minStart = (a.start < b.start) ? a.start : b.start; + SizeT maxEnd = (a.end > b.end) ? a.end : b.end; + SizeT span = (maxEnd > minStart) ? (maxEnd - minStart) : 0; + return static_cast(static_cast(span) * ratio); + }; + + if (pos >= this->size()) { + Range1D& last = this->back(); + SizeT gap = calc_gap(last, newRange); + SizeT threshold = calc_threshold(last, newRange); + + if (gap <= threshold) { + last.end = std::max(last.end, newRange.end); + m_overallMaxEnd = std::max(m_overallMaxEnd, last.end); + } else { + this->push_back(newRange); + m_overallMaxEnd = std::max(m_overallMaxEnd, newRange.end); + } + + if (outMinStart) *outMinStart = this->front().start; + if (outMaxEnd) *outMaxEnd = m_overallMaxEnd; + return; + } + + bool merged = false; + if (pos > 0) { + Range1D& prev = (*this)[pos - 1]; + SizeT gapPrev = calc_gap(prev, newRange); + SizeT thresholdPrev = calc_threshold(prev, newRange); + + if (gapPrev <= thresholdPrev) { + prev.end = std::max(prev.end, newRange.end); + m_overallMaxEnd = std::max(m_overallMaxEnd, prev.end); + + size_t writeIdx = pos - 1; + while (writeIdx + 1 < this->size()) { + Range1D& cur = (*this)[writeIdx]; + Range1D& nxt = (*this)[writeIdx + 1]; + SizeT gap = calc_gap(cur, nxt); + SizeT threshold = calc_threshold(cur, nxt); + if (gap <= threshold) { + // merge nxt into cur + cur.end = std::max(cur.end, nxt.end); + this->erase(this->begin() + (writeIdx + 1)); + m_overallMaxEnd = std::max(m_overallMaxEnd, cur.end); + } else { + break; + } + } + + merged = true; + } + } + + if (!merged) { + // Try to merge with the current pos interval or insert + Range1D& cur = (*this)[pos]; + SizeT gapCur = calc_gap(newRange, cur); // gap between new and cur + SizeT thresholdCur = calc_threshold(newRange, cur); + + if (gapCur <= thresholdCur) { + cur.start = std::min(cur.start, newRange.start); + cur.end = std::max(cur.end, newRange.end); + m_overallMaxEnd = std::max(m_overallMaxEnd, cur.end); + + size_t writeIdx = pos; + while (writeIdx + 1 < this->size()) { + Range1D& cur2 = (*this)[writeIdx]; + Range1D& nxt = (*this)[writeIdx + 1]; + SizeT gap = calc_gap(cur2, nxt); + SizeT threshold = calc_threshold(cur2, nxt); + if (gap <= threshold) { + cur2.end = std::max(cur2.end, nxt.end); + this->erase(this->begin() + (writeIdx + 1)); + m_overallMaxEnd = std::max(m_overallMaxEnd, cur2.end); + } else { + break; + } + } + + merged = true; + } else { + this->insert(this->begin() + pos, newRange); + m_overallMaxEnd = std::max(m_overallMaxEnd, newRange.end); + merged = true; + } + } + + if (outMinStart) { + *outMinStart = this->front().start; + } + if (outMaxEnd) { + *outMaxEnd = m_overallMaxEnd; + } + } + + SizeT VecRange1D::GetOverallMaxEnd() const { + return m_overallMaxEnd; + } + + SizeT VecRange1D::GetOverallMinStart() const { + if (this->empty()) return 0; + return this->front().start; + } +} // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/Math/VectorTypes.h b/MobileGL/MG_Util/Math/VectorTypes.h index 6ec456a5..5ea2f3c3 100644 --- a/MobileGL/MG_Util/Math/VectorTypes.h +++ b/MobileGL/MG_Util/Math/VectorTypes.h @@ -11,7 +11,6 @@ #include namespace MobileGL { - template struct VecBase { Array data; @@ -247,4 +246,15 @@ namespace MobileGL { return incident - normal * (2.0f * incident.Dot(normal)); } } // namespace MG_Util + + class VecRange1D : public Vector { + public: + void Add(const Range1D& newRange, Double ratio = 0.07, SizeT* outMinStart = nullptr, + SizeT* outMaxEnd = nullptr); + SizeT GetOverallMaxEnd() const; + SizeT GetOverallMinStart() const; + + private: + SizeT m_overallMaxEnd; + }; } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 534160a4..09246fd9 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -227,13 +227,12 @@ namespace MobileGL { return allSpirv; } - bool ShaderCompiler::SanitizeAndOptimizeBinary(const Vector& inputBinary, Vector& outputBinary) { + bool ShaderCompiler::SanitizeAndOptimizeBinary(const Vector& inputBinary, + Vector& outputBinary) { using namespace spvtools; Optimizer optimizer(SPV_ENV_UNIVERSAL_1_5); - optimizer - .RegisterPass(EliminateFloatEqualsZeroPass::CreateEliminateFloatEqualsZeroPass()) - ; + optimizer.RegisterPass(EliminateFloatEqualsZeroPass::CreateEliminateFloatEqualsZeroPass()); return optimizer.Run(inputBinary.data(), inputBinary.size(), &outputBinary); } diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h index 497832ee..2908a283 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h @@ -20,7 +20,8 @@ namespace MobileGL { static Result> CompileShader(const ShaderAttrib& attrib); static Result> LinkProgram(const ProgramAttrib& attrib); static Result>> GetSpirvBinaryFromProgram(const ProgramBinaryAttrib& attrib); - static bool SanitizeAndOptimizeBinary(const Vector& inputBinary, Vector& outputBinary); + static bool SanitizeAndOptimizeBinary(const Vector& inputBinary, + Vector& outputBinary); static Result DecompileShader(SpvcSession& session); }; } // namespace ShaderTranspiler diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp index 89169c94..11ef6188 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp @@ -1,4 +1,4 @@ -// MobileGL - MobileGL/MG_Util/ShaderTranspiler/FloatEqualsZeroEliminationPass.cpp +// MobileGL - MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp // Copyright (c) 2025-2026 MobileGL-Dev // Licensed under the GNU Lesser General Public License v3.0: // https://www.gnu.org/licenses/gpl-3.0.txt @@ -39,7 +39,7 @@ namespace MobileGL { // 3. iterate all function -> basic block -> insn for (auto& func : *get_module()) { for (auto& bb : func) { - for (auto itInst = bb.begin(); itInst != bb.end(); ) { + for (auto itInst = bb.begin(); itInst != bb.end();) { auto& inst = *itInst; bool shouldSkip = true; @@ -48,14 +48,14 @@ namespace MobileGL { // `OpFOrdNotEqual` or `OpFUnordNotEqual`, // simply skip if irrelevant switch (inst.opcode()) { - case spv::Op::OpFOrdEqual: - case spv::Op::OpFUnordEqual: - case spv::Op::OpFOrdNotEqual: - case spv::Op::OpFUnordNotEqual: - shouldSkip = false; - break; - default: - break; + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + shouldSkip = false; + break; + default: + break; } if (shouldSkip) { @@ -96,18 +96,13 @@ namespace MobileGL { // 2. Create constant ID for `Epsilon` const analysis::Constant* eps_const = const_mgr->GetConstant( - type_mgr->GetType(float_type_id), - {*(reinterpret_cast(&K_EPSILON))} - ); + type_mgr->GetType(float_type_id), {*(reinterpret_cast(&K_EPSILON))}); uint32_t eps_id = const_mgr->GetDefiningInstruction(eps_const)->result_id(); // 3. Build Abs(x) inst // OpExtInst %float_type %glsl_import Abs %x InstructionBuilder builder( - context(), - &inst, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping - ); + context(), &inst, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); std::vector abs_operands; abs_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl_std_450_id}}); @@ -117,12 +112,7 @@ namespace MobileGL { // In GLSL.std.450, `FAbs`'s OpCode == 4 // Ref: https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html Instruction* abs_inst = builder.AddInstruction(MakeUnique( - context(), - spv::Op::OpExtInst, - float_type_id, - context()->TakeNextId(), - abs_operands - )); + context(), spv::Op::OpExtInst, float_type_id, context()->TakeNextId(), abs_operands)); // 4. build Abs(x) < Epsilon // OpFOrdLessThan %bool_type %abs_val %eps @@ -130,14 +120,11 @@ namespace MobileGL { less_operands.push_back({SPV_OPERAND_TYPE_ID, {abs_inst->result_id()}}); less_operands.push_back({SPV_OPERAND_TYPE_ID, {eps_id}}); - bool isEqualOp = (inst.opcode() == spv::Op::OpFOrdEqual || inst.opcode() == spv::Op::OpFUnordEqual); + bool isEqualOp = + (inst.opcode() == spv::Op::OpFOrdEqual || inst.opcode() == spv::Op::OpFUnordEqual); Instruction* less_than_inst = builder.AddInstruction(MakeUnique( - context(), - isEqualOp ? spv::Op::OpFOrdLessThan : spv::Op::OpFOrdGreaterThanEqual, - bool_type_id, - context()->TakeNextId(), - less_operands - )); + context(), isEqualOp ? spv::Op::OpFOrdLessThan : spv::Op::OpFOrdGreaterThanEqual, + bool_type_id, context()->TakeNextId(), less_operands)); // 5. Replaces all uses of old insn with new one context()->ReplaceAllUsesWith(inst.result_id(), less_than_inst->result_id()); @@ -161,5 +148,5 @@ namespace MobileGL { return spvtools::Optimizer::PassToken(MakeUnique()); } } // namespace ShaderTranspiler - } -} \ No newline at end of file + } // namespace MG_Util +} // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h index 57d8c56a..8e6ab37d 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h +++ b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h @@ -1,4 +1,4 @@ -// MobileGL - MobileGL/MG_Util/ShaderTranspiler/FloatEqualsZeroEliminationPass.h +// MobileGL - MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h // Copyright (c) 2025-2026 MobileGL-Dev // Licensed under the GNU Lesser General Public License v3.0: // https://www.gnu.org/licenses/gpl-3.0.txt @@ -15,16 +15,16 @@ namespace MobileGL { namespace MG_Util { namespace ShaderTranspiler { - class EliminateFloatEqualsZeroPass: public spvtools::opt::Pass { + class EliminateFloatEqualsZeroPass : public spvtools::opt::Pass { public: const char* name() const override { return "float-equals-zero-elimination"; } Status Process() override; static spvtools::Optimizer::PassToken CreateEliminateFloatEqualsZeroPass(); + private: const float K_EPSILON = 0.0001f; }; } // namespace ShaderTranspiler } // namespace MG_Util } // namespace MobileGL - diff --git a/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp index 7439e9ed..6092914f 100644 --- a/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp +++ b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp @@ -108,8 +108,8 @@ namespace MobileGL::MG_Util::PixelStoreProcessor { const Int copyHeight = height; const Int copyDepth = depth; - MGLOG_D("%s: start at: (%d, %d, %d), copy size: (%d, %d, %d), i/o row stride: (%d, %dx%d)", __func__, - startX, startY, startZ, copyWidth, copyHeight, copyDepth, inputRowStride, width, pixelSize); + MGLOG_D("%s: start at: (%d, %d, %d), copy size: (%d, %d, %d), i/o row stride: (%d, %dx%d)", __func__, startX, + startY, startZ, copyWidth, copyHeight, copyDepth, inputRowStride, width, pixelSize); if (copyWidth <= 0 || copyHeight <= 0 || copyDepth <= 0) { outSize = 0; @@ -152,14 +152,14 @@ namespace MobileGL::MG_Util::PixelStoreProcessor { if (textureInputFormat == TextureInputFormat::BGRA && targetInternalFormat == TextureInternalFormat::RGBA8) { MGLOG_D("%s: Swizzle (BGRA)", __func__); -// MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst)); + // MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst)); ProcessColorSwizzle(layerDst, static_cast(copyWidth), {TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha, TextureSwizzleParam::Red}); -// MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst)); + // MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst)); } -// else -// MGLOG_D("%s: pixel0 = %x", __func__, *((Uint32*)layerDst)); + // else + // MGLOG_D("%s: pixel0 = %x", __func__, *((Uint32*)layerDst)); layerSrc += inputRowStride; layerDst += outputRowStride; diff --git a/MobileGL/MG_Util/Texture/PixelStoreProcessor.h b/MobileGL/MG_Util/Texture/PixelStoreProcessor.h index 8821d40c..d5fcbca2 100644 --- a/MobileGL/MG_Util/Texture/PixelStoreProcessor.h +++ b/MobileGL/MG_Util/Texture/PixelStoreProcessor.h @@ -14,7 +14,8 @@ namespace MobileGL::MG_Util::PixelStoreProcessor { void* ProcessTexturePixelsDataUnpack(const void* inputPixels, const PixelStoreParameters& params, - TextureInternalFormat targetInternalFormat, TextureInputFormat textureInputFormat, TexturePixelDataType inputDataType, + TextureInternalFormat targetInternalFormat, + TextureInputFormat textureInputFormat, TexturePixelDataType inputDataType, IntVec3 dimension, Bool isBitmap, SizeT& outSize); void* ProcessTexturePixelsDataPack(const void* inputPixels, const PixelStoreParameters& params, SizeT pixelSize, IntVec3 dimension, Bool isBitmap, SizeT& outSize); diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp index 94edc4d5..4d8eea60 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp @@ -10,335 +10,338 @@ #include "MG_Util/Converters/GLToStr/GLEnumConverter.h" namespace MobileGL::MG_Util::TextureFormatProcessor { - void NormalizePixelFormat(GLenum internalFormat, Flags options, GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType) { + void NormalizePixelFormat(GLenum internalFormat, Flags options, + GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif // internal format if (outInternalFormat) { switch (internalFormat) { - case GL_DEPTH_COMPONENT32: - *outInternalFormat = GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32: + *outInternalFormat = GL_DEPTH_COMPONENT; + break; + case GL_RGBA16: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RGBA32F; break; - case GL_RGBA16: - if (options & PixelFormatNormalizeOptionBit::NoNorm16) { - *outInternalFormat = GL_RGBA32F; - break; - } - case GL_RGB16: - if (options & PixelFormatNormalizeOptionBit::NoNorm16) { - *outInternalFormat = GL_RGB32F; - break; - } - case GL_RG16: - if (options & PixelFormatNormalizeOptionBit::NoNorm16) { - *outInternalFormat = GL_RG32F; - break; - } - case GL_R16: - if (options & PixelFormatNormalizeOptionBit::NoNorm16) { - *outInternalFormat = GL_R32F; - break; - } - default: - *outInternalFormat = internalFormat; + } + case GL_RGB16: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RGB32F; break; + } + case GL_RG16: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RG32F; + break; + } + case GL_R16: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_R32F; + break; + } + default: + *outInternalFormat = internalFormat; + break; } } // format if (outFormat) { switch (internalFormat) { - // Color Unsigned Normalized - case GL_RGBA: - case GL_RGBA16: - case GL_RGBA8: + // Color Unsigned Normalized + case GL_RGBA: + case GL_RGBA16: + case GL_RGBA8: + *outFormat = GL_RGBA; + break; + + case GL_RGB: + case GL_RGB16: + case GL_RGB8: + *outFormat = GL_RGB; + break; + + case GL_RG: + case GL_RG16: + case GL_RG8: + *outFormat = GL_RG; + break; + + case GL_RED: + case GL_R16: + case GL_R8: + *outFormat = GL_RED; + break; + + // Color Signed Normalized + case GL_RGBA_SNORM: + case GL_RGBA16_SNORM: + case GL_RGBA8_SNORM: + *outFormat = GL_RGBA; + break; + + case GL_RGB_SNORM: + case GL_RGB16_SNORM: + case GL_RGB8_SNORM: + *outFormat = GL_RGB; + break; + + case GL_RG_SNORM: + case GL_RG16_SNORM: + case GL_RG8_SNORM: + *outFormat = GL_RG; + break; + + case GL_RED_SNORM: + case GL_R16_SNORM: + case GL_R8_SNORM: + *outFormat = GL_RED; + break; + + // Color Integer + case GL_RGBA32UI: + case GL_RGBA16UI: + case GL_RGBA8UI: + case GL_RGBA32I: + case GL_RGBA16I: + case GL_RGBA8I: + *outFormat = GL_RGBA_INTEGER; + break; + case GL_RGB32UI: + case GL_RGB16UI: + case GL_RGB8UI: + case GL_RGB32I: + case GL_RGB16I: + case GL_RGB8I: + *outFormat = GL_RGB_INTEGER; + break; + case GL_RG32UI: + case GL_RG16UI: + case GL_RG8UI: + case GL_RG32I: + case GL_RG16I: + case GL_RG8I: + *outFormat = GL_RG_INTEGER; + break; + case GL_R32UI: + case GL_R16UI: + case GL_R8UI: + case GL_R32I: + case GL_R16I: + case GL_R8I: + *outFormat = GL_RED_INTEGER; + break; + + // Color Float + case GL_RGBA32F: + case GL_RGBA16F: + *outFormat = GL_RGBA; + break; + case GL_RGB32F: + case GL_RGB16F: + *outFormat = GL_RGB; + break; + case GL_RG32F: + case GL_RG16F: + *outFormat = GL_RG; + break; + case GL_R32F: + case GL_R16F: + *outFormat = GL_RED; + break; + + // Color sRGB + case GL_SRGB: + case GL_SRGB8: + *outFormat = GL_RGB; + break; + + // Color sized other + case GL_RGB9_E5: + case GL_R11F_G11F_B10F: + *outFormat = GL_RGB; + break; + case GL_RGB10_A2: + case GL_RGB5_A1: + *outFormat = GL_RGBA; + break; + + // Depth + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + case GL_DEPTH_COMPONENT32F: + case GL_DEPTH_COMPONENT: + *outFormat = GL_DEPTH_COMPONENT; + break; + + // Depth Stencil + case GL_DEPTH32F_STENCIL8: + case GL_DEPTH_STENCIL: + *outFormat = GL_DEPTH_STENCIL; + break; + + default: + MGLOG_E("NormalizePixelFormat: outFormat: unhandled internalFormat: %s", + MG_Util::ConvertGLEnumToString(internalFormat).c_str()); + // Fallback handling for other formats + // Try to infer format from internal format name + if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RGBA") != nullptr) { *outFormat = GL_RGBA; - break; - - case GL_RGB: - case GL_RGB16: - case GL_RGB8: + } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RGB") != nullptr) { *outFormat = GL_RGB; - break; - - case GL_RG: - case GL_RG16: - case GL_RG8: + } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RG") != nullptr) { *outFormat = GL_RG; - break; - - case GL_RED: - case GL_R16: - case GL_R8: + } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RED") != nullptr) { *outFormat = GL_RED; - break; - - // Color Signed Normalized - case GL_RGBA_SNORM: - case GL_RGBA16_SNORM: - case GL_RGBA8_SNORM: - *outFormat = GL_RGBA; - break; - - case GL_RGB_SNORM: - case GL_RGB16_SNORM: - case GL_RGB8_SNORM: - *outFormat = GL_RGB; - break; - - case GL_RG_SNORM: - case GL_RG16_SNORM: - case GL_RG8_SNORM: - *outFormat = GL_RG; - break; - - case GL_RED_SNORM: - case GL_R16_SNORM: - case GL_R8_SNORM: - *outFormat = GL_RED; - break; - - // Color Integer - case GL_RGBA32UI: - case GL_RGBA16UI: - case GL_RGBA8UI: - case GL_RGBA32I: - case GL_RGBA16I: - case GL_RGBA8I: - *outFormat = GL_RGBA_INTEGER; - break; - case GL_RGB32UI: - case GL_RGB16UI: - case GL_RGB8UI: - case GL_RGB32I: - case GL_RGB16I: - case GL_RGB8I: - *outFormat = GL_RGB_INTEGER; - break; - case GL_RG32UI: - case GL_RG16UI: - case GL_RG8UI: - case GL_RG32I: - case GL_RG16I: - case GL_RG8I: - *outFormat = GL_RG_INTEGER; - break; - case GL_R32UI: - case GL_R16UI: - case GL_R8UI: - case GL_R32I: - case GL_R16I: - case GL_R8I: - *outFormat = GL_RED_INTEGER; - break; - - // Color Float - case GL_RGBA32F: - case GL_RGBA16F: - *outFormat = GL_RGBA; - break; - case GL_RGB32F: - case GL_RGB16F: - *outFormat = GL_RGB; - break; - case GL_RG32F: - case GL_RG16F: - *outFormat = GL_RG; - break; - case GL_R32F: - case GL_R16F: - *outFormat = GL_RED; - break; - - // Color sRGB - case GL_SRGB: - case GL_SRGB8: - *outFormat = GL_RGB; - break; - - // Color sized other - case GL_RGB9_E5: - case GL_R11F_G11F_B10F: - *outFormat = GL_RGB; - break; - case GL_RGB10_A2: - case GL_RGB5_A1: - *outFormat = GL_RGBA; - break; - - // Depth - case GL_DEPTH_COMPONENT16: - case GL_DEPTH_COMPONENT24: - case GL_DEPTH_COMPONENT32: - case GL_DEPTH_COMPONENT32F: - case GL_DEPTH_COMPONENT: - *outFormat = GL_DEPTH_COMPONENT; - break; - - // Depth Stencil - case GL_DEPTH32F_STENCIL8: - case GL_DEPTH_STENCIL: - *outFormat = GL_DEPTH_STENCIL; - break; - - default: - MGLOG_E("NormalizePixelFormat: outFormat: unhandled internalFormat: %s", MG_Util::ConvertGLEnumToString(internalFormat).c_str()); - // Fallback handling for other formats - // Try to infer format from internal format name - if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RGBA") != nullptr) { - *outFormat = GL_RGBA; - } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RGB") != nullptr) { - *outFormat = GL_RGB; - } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RG") != nullptr) { - *outFormat = GL_RG; - } else if (strstr(MG_Util::ConvertGLEnumToString(internalFormat).c_str(), "RED") != nullptr) { - *outFormat = GL_RED; - } else { - *outFormat = GL_RGBA; // Ultimate fallback - } - break; + } else { + *outFormat = GL_RGBA; // Ultimate fallback + } + break; } } // type if (outType) { switch (internalFormat) { - // Color Unsigned Normalized - case GL_RGBA16: - case GL_RGB16: - case GL_RG16: - case GL_R16: - if (options & PixelFormatNormalizeOptionBit::NoNorm16) { - // converted to GL_RGBA32F - *outType = GL_FLOAT; - break; - } else { - *outType = GL_UNSIGNED_SHORT; - break; - } - case GL_RGBA8: - case GL_RGB8: - case GL_RG8: - case GL_R8: - *outType = GL_UNSIGNED_BYTE; - break; - - // Color Signed Normalized - case GL_RGBA16_SNORM: - case GL_RGB16_SNORM: - case GL_RG16_SNORM: - case GL_R16_SNORM: - *outType = GL_SHORT; - break; - case GL_RGBA8_SNORM: - case GL_RGB8_SNORM: - case GL_RG8_SNORM: - case GL_R8_SNORM: - *outType = GL_BYTE; - break; - - // Color Unsigned Integer - case GL_RGBA32UI: - case GL_RGB32UI: - case GL_RG32UI: - case GL_R32UI: - *outType = GL_UNSIGNED_INT; - break; - case GL_RGBA16UI: - case GL_RGB16UI: - case GL_RG16UI: - case GL_R16UI: - *outType = GL_UNSIGNED_SHORT; - break; - case GL_RGBA8UI: - case GL_RGB8UI: - case GL_RG8UI: - case GL_R8UI: - *outType = GL_UNSIGNED_BYTE; - break; - - // Color Integer - case GL_RGBA32I: - case GL_RGB32I: - case GL_RG32I: - case GL_R32I: - *outType = GL_INT; - break; - case GL_RGBA16I: - case GL_RGB16I: - case GL_RG16I: - case GL_R16I: - *outType = GL_SHORT; - break; - case GL_RGBA8I: - case GL_RGB8I: - case GL_RG8I: - case GL_R8I: - *outType = GL_BYTE; - break; - - // Color Float - case GL_RGBA32F: - case GL_RGB32F: - case GL_RG32F: - case GL_R32F: + // Color Unsigned Normalized + case GL_RGBA16: + case GL_RGB16: + case GL_RG16: + case GL_R16: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + // converted to GL_RGBA32F *outType = GL_FLOAT; break; - case GL_RGBA16F: - case GL_RGB16F: - case GL_RG16F: - case GL_R16F: - *outType = GL_HALF_FLOAT; - break; - - // Color sRGB - case GL_SRGB8: - *outType = GL_UNSIGNED_BYTE; - break; - - // Color sized other - case GL_RGB9_E5: - *outType = GL_UNSIGNED_INT_5_9_9_9_REV; - break; - case GL_R11F_G11F_B10F: - *outType = GL_UNSIGNED_INT_10F_11F_11F_REV; - break; - case GL_RGB10_A2: - *outType = GL_UNSIGNED_INT_2_10_10_10_REV; - break; - case GL_RGB5_A1: - *outType = GL_UNSIGNED_SHORT_5_5_5_1; - break; - - // Depth - case GL_DEPTH_COMPONENT16: + } else { *outType = GL_UNSIGNED_SHORT; break; - case GL_DEPTH_COMPONENT24: - *outType = GL_UNSIGNED_INT; - break; - case GL_DEPTH_COMPONENT32: - *outType = GL_UNSIGNED_INT; - break; - case GL_DEPTH_COMPONENT32F: - *outType = GL_FLOAT; - break; - case GL_DEPTH_COMPONENT: - *outType = GL_UNSIGNED_INT; - break; + } + case GL_RGBA8: + case GL_RGB8: + case GL_RG8: + case GL_R8: + *outType = GL_UNSIGNED_BYTE; + break; - // Depth Stencil - case GL_DEPTH32F_STENCIL8: - case GL_DEPTH_STENCIL: - *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; - break; + // Color Signed Normalized + case GL_RGBA16_SNORM: + case GL_RGB16_SNORM: + case GL_RG16_SNORM: + case GL_R16_SNORM: + *outType = GL_SHORT; + break; + case GL_RGBA8_SNORM: + case GL_RGB8_SNORM: + case GL_RG8_SNORM: + case GL_R8_SNORM: + *outType = GL_BYTE; + break; - default: - MGLOG_E("NormalizePixelFormat: outType: unhandled internalFormat: %s", MG_Util::ConvertGLEnumToString(internalFormat).c_str()); - // Fallback handling for other formats - *outType = GL_UNSIGNED_BYTE; - break; + // Color Unsigned Integer + case GL_RGBA32UI: + case GL_RGB32UI: + case GL_RG32UI: + case GL_R32UI: + *outType = GL_UNSIGNED_INT; + break; + case GL_RGBA16UI: + case GL_RGB16UI: + case GL_RG16UI: + case GL_R16UI: + *outType = GL_UNSIGNED_SHORT; + break; + case GL_RGBA8UI: + case GL_RGB8UI: + case GL_RG8UI: + case GL_R8UI: + *outType = GL_UNSIGNED_BYTE; + break; + + // Color Integer + case GL_RGBA32I: + case GL_RGB32I: + case GL_RG32I: + case GL_R32I: + *outType = GL_INT; + break; + case GL_RGBA16I: + case GL_RGB16I: + case GL_RG16I: + case GL_R16I: + *outType = GL_SHORT; + break; + case GL_RGBA8I: + case GL_RGB8I: + case GL_RG8I: + case GL_R8I: + *outType = GL_BYTE; + break; + + // Color Float + case GL_RGBA32F: + case GL_RGB32F: + case GL_RG32F: + case GL_R32F: + *outType = GL_FLOAT; + break; + case GL_RGBA16F: + case GL_RGB16F: + case GL_RG16F: + case GL_R16F: + *outType = GL_HALF_FLOAT; + break; + + // Color sRGB + case GL_SRGB8: + *outType = GL_UNSIGNED_BYTE; + break; + + // Color sized other + case GL_RGB9_E5: + *outType = GL_UNSIGNED_INT_5_9_9_9_REV; + break; + case GL_R11F_G11F_B10F: + *outType = GL_UNSIGNED_INT_10F_11F_11F_REV; + break; + case GL_RGB10_A2: + *outType = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + case GL_RGB5_A1: + *outType = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + // Depth + case GL_DEPTH_COMPONENT16: + *outType = GL_UNSIGNED_SHORT; + break; + case GL_DEPTH_COMPONENT24: + *outType = GL_UNSIGNED_INT; + break; + case GL_DEPTH_COMPONENT32: + *outType = GL_UNSIGNED_INT; + break; + case GL_DEPTH_COMPONENT32F: + *outType = GL_FLOAT; + break; + case GL_DEPTH_COMPONENT: + *outType = GL_UNSIGNED_INT; + break; + + // Depth Stencil + case GL_DEPTH32F_STENCIL8: + case GL_DEPTH_STENCIL: + *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + break; + + default: + MGLOG_E("NormalizePixelFormat: outType: unhandled internalFormat: %s", + MG_Util::ConvertGLEnumToString(internalFormat).c_str()); + // Fallback handling for other formats + *outType = GL_UNSIGNED_BYTE; + break; } } } diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h index fbc70c88..6027fd46 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h @@ -14,5 +14,6 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { NoNorm16 = 1 << 0, None = 0, }; - void NormalizePixelFormat(GLenum internalFormat, Flags options, GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType); + void NormalizePixelFormat(GLenum internalFormat, Flags options, + GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType); } // namespace MobileGL::MG_Util::TextureFormatProcessor \ No newline at end of file From 33a4afba5e03cf53f2d41e863ffd361b1a94035c Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 2 Feb 2026 11:17:16 +0800 Subject: [PATCH 04/74] [Optimize] (MG_Backend/DirectGLES): get rid of dedup logic in buffer sync --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 261be744..1280cb56 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -94,7 +94,10 @@ namespace MobileGL::MG_Backend::DirectGLES { // 1.VBO 2.IBO (if needed) 3.UBO 4.IndirectBuffer (if needed) 5.SSBO (TODO) // PBO is not needed since it should be handled in frontend - Vector> buffersToSync; + static Vector> buffersToSync; + + buffersToSync.clear(); + const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); if (!currentVAOObject) { MGLOG_E("No VAO is currently bound, cannot sync necessary buffers."); @@ -106,10 +109,7 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!attrib.Enabled) continue; const auto& bufferObject = attrib.Buffer; if (bufferObject) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, bufferObject) == end) { - buffersToSync.push_back(bufferObject); - } + buffersToSync.push_back(bufferObject); } } @@ -117,10 +117,7 @@ namespace MobileGL::MG_Backend::DirectGLES { if (includeIBO) { const auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); if (possibleIBO) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, possibleIBO) == end) { - buffersToSync.push_back(possibleIBO); - } + buffersToSync.push_back(possibleIBO); } } @@ -129,10 +126,7 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& possibleIndirectBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); if (possibleIndirectBuffer) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, possibleIndirectBuffer) == end) { - buffersToSync.push_back(possibleIndirectBuffer); - } + buffersToSync.push_back(possibleIndirectBuffer); } } @@ -142,10 +136,7 @@ namespace MobileGL::MG_Backend::DirectGLES { auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, i); auto obj = point.GetBoundObject(); if (obj) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, obj) == end) { - buffersToSync.push_back(obj); - } + buffersToSync.push_back(obj); } } From cb2ce8f2a598e402b6b5c2e35f7b611b74aa35f5 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 2 Feb 2026 12:58:50 +0800 Subject: [PATCH 05/74] [Optimize] (MG_Backend/DirectGLES): sync buffer object directly --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 1280cb56..89aeb380 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -86,6 +86,21 @@ namespace MobileGL::MG_Backend::DirectGLES { // TODO: deletion for deleted objects namespace BufferImpl { + void CreateAndSyncBufferObject(SharedPtr& bufferObject) { + if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) + return; + + const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); + SharedPtr backendBufferObject; + if (backendBufferIt == g_backendBufferObjects.end()) { + backendBufferObject = MakeShared(); + g_backendBufferObjects[bufferObject] = backendBufferObject; + } else { + backendBufferObject = backendBufferIt->second; + } + backendBufferObject->SyncToBackend(bufferObject); + } + void SyncNeccessaryBuffers(Bool includeIBO = false, Bool includeIndirectBuffer = false) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -94,9 +109,8 @@ namespace MobileGL::MG_Backend::DirectGLES { // 1.VBO 2.IBO (if needed) 3.UBO 4.IndirectBuffer (if needed) 5.SSBO (TODO) // PBO is not needed since it should be handled in frontend - static Vector> buffersToSync; - - buffersToSync.clear(); + // static Vector> buffersToSync; + // buffersToSync.clear(); const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); if (!currentVAOObject) { @@ -107,26 +121,26 @@ namespace MobileGL::MG_Backend::DirectGLES { // VBO for (const auto& attrib : currentVAOObject->GetAllAttributes()) { if (!attrib.Enabled) continue; - const auto& bufferObject = attrib.Buffer; + auto bufferObject = attrib.Buffer; if (bufferObject) { - buffersToSync.push_back(bufferObject); + CreateAndSyncBufferObject(bufferObject); } } // IBO if (includeIBO) { - const auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); + auto possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); if (possibleIBO) { - buffersToSync.push_back(possibleIBO); + CreateAndSyncBufferObject(possibleIBO); } } // Indirect Buffer Object if (includeIndirectBuffer) { - const auto& possibleIndirectBuffer = + auto possibleIndirectBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); if (possibleIndirectBuffer) { - buffersToSync.push_back(possibleIndirectBuffer); + CreateAndSyncBufferObject(possibleIndirectBuffer); } } @@ -136,24 +150,9 @@ namespace MobileGL::MG_Backend::DirectGLES { auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, i); auto obj = point.GetBoundObject(); if (obj) { - buffersToSync.push_back(obj); + CreateAndSyncBufferObject(obj); } } - - // Do real sync - for (auto& bufferObject : buffersToSync) { - if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) continue; - - const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); - SharedPtr backendBufferObject; - if (backendBufferIt == g_backendBufferObjects.end()) { - backendBufferObject = MakeShared(); - g_backendBufferObjects[bufferObject] = backendBufferObject; - } else { - backendBufferObject = backendBufferIt->second; - } - backendBufferObject->SyncToBackend(bufferObject); - } } } // namespace BufferImpl From 8f5517949360fa1ac21a88c80d67f6493b09c699 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 2 Feb 2026 15:37:55 +0800 Subject: [PATCH 06/74] [Optimize] (MG_Backend/DirectGLES): Texture object mipmap dirty bit --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 28 +- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 301 +++++++++--------- MobileGL/MG_Backend/DirectGLES/Managers.h | 1 + .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 1 + .../GLState/TextureState/TextureObject.cpp | 18 ++ .../GLState/TextureState/TextureObject.h | 16 +- .../TextureState/TextureObject2DCube.cpp | 14 + .../TextureState/TextureObject2DCube.h | 1 + .../TextureState/TextureObjectBuffer.cpp | 8 + .../TextureState/TextureObjectBuffer.h | 2 + MobileGL/MG_Util/Types.h | 10 + 11 files changed, 238 insertions(+), 162 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 89aeb380..c0a4752c 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -205,8 +205,8 @@ namespace MobileGL::MG_Backend::DirectGLES { // 1. textures bound to texture units (TODO: only sync ones that are used in current program) // 2. textures used in current FBO // 3. textures bound to image units (TODO) - constexpr SizeT TextureTargetCount = static_cast(TextureTarget::TextureTargetCount); - std::bitset dirtyTextureTargetBits; +// constexpr SizeT TextureTargetCount = static_cast(TextureTarget::TextureTargetCount); +// std::bitset dirtyTextureTargetBits; Vector> texturesToSync; @@ -218,7 +218,7 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& end = texturesToSync.end(); if (std::find(texturesToSync.begin(), end, textureObject) == end) { texturesToSync.push_back(textureObject); - dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); +// dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); } } } @@ -234,22 +234,22 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& end = texturesToSync.end(); if (std::find(texturesToSync.begin(), end, textureObject) == end) { texturesToSync.push_back(textureObject); - dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); +// dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); } } } } - BufferImpl::BackendBufferBindingProtector pixelUnpackProtector = - BufferImpl::BackendBufferBindingProtector(GL_PIXEL_UNPACK_BUFFER); - - Vector textureBindingProtectors; - for (SizeT target = 0; target < TextureTargetCount; ++target) { - if (dirtyTextureTargetBits[target]) { - textureBindingProtectors.emplace_back( - MG_Util::ConvertTextureTargetToGLEnum(static_cast(target))); - } - } +// BufferImpl::BackendBufferBindingProtector pixelUnpackProtector = +// BufferImpl::BackendBufferBindingProtector(GL_PIXEL_UNPACK_BUFFER); +// +// Vector textureBindingProtectors; +// for (SizeT target = 0; target < TextureTargetCount; ++target) { +// if (dirtyTextureTargetBits[target]) { +// textureBindingProtectors.emplace_back( +// MG_Util::ConvertTextureTargetToGLEnum(static_cast(target))); +// } +// } // Do real sync for (auto& textureObject : texturesToSync) { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 8acecd7e..492ae6f0 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -366,166 +366,173 @@ namespace MobileGL::MG_Backend::DirectGLES { static_cast(baseSize.z()), 0, 0}; - switch (stateTextureObject->GetStorageType()) { - case TextureStorageType::Mipmap: { - auto* textureMipmapObject = - static_cast(stateTextureObject.get()); - const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); - currentTextureInfo.mipmapLevels = mipmapCount; - Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); + if (stateTextureObject->CheckDirtyBit(MG_State::GLState::TextureDirtyBit::StorageDirtyBit)) { + switch (stateTextureObject->GetStorageType()) { + case TextureStorageType::Mipmap: { + auto* textureMipmapObject = + static_cast(stateTextureObject.get()); + const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); + currentTextureInfo.mipmapLevels = mipmapCount; - MGLOG_D("%s: Got texture info: %dx%dx%d, mips %d, format %s", __func__, baseSize.x(), baseSize.y(), - baseSize.z(), mipmapCount, - MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str()); + Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); - if (needsRegeneration) { - MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u", + MGLOG_D("%s: Got texture info: %dx%dx%d, mips %d, format %s", __func__, baseSize.x(), baseSize.y(), + baseSize.z(), mipmapCount, + MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str()); + + if (needsRegeneration) { + MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u", + m_backendTextureId); + + // Regenerate all mipmap levels + GLenum glInternalFormat, glType, glFormat; + TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, + &glFormat, &glType); + + const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + for (auto uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); + auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); + bool levelDirty = textureMipmapObject->IsStorageDirty(uploadTarget, level); + auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); + auto* pData = (levelDirty && levelByteSize != 0) + ? textureMipmapObject->MapMipmapData(uploadTarget, level) + : nullptr; + MGLOG_D( + "%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", + __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, + levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, + levelDirty ? "true" : "false"); + + errorLopper.Clear(); + MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + auto textureTarget = stateTextureObject->GetTarget(); + // TODO: handle more texture types + switch (textureTarget) { + case TextureTarget::Texture2D: + case TextureTarget::TextureCubeMap: { + MG_External::GLES::glTexImage2D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + 0, glFormat, glType, pData); + break; + } + case TextureTarget::Texture3D: { + MG_External::GLES::glTexImage3D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); + break; + } + default: { + MGLOG_E("Unhandled texture target %s", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + } + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, + glInternalFormat, glFormat, glType, pData](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " + "type=%s, pixels=%p", + func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), + MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), + MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), + MG_Util::ConvertGLEnumToString(glFormat).c_str(), + MG_Util::ConvertGLEnumToString(glType).c_str(), pData); + }); + MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, m_backendTextureId); + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); + } + } + + m_isInitialized = true; + } + + { // Update all dirty mipmap levels + const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); + GLenum glInternalFormat, glType, glFormat; + TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, + &glFormat, &glType); + const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + for (auto uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { + continue; + } + + auto byteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); + if (byteSize == 0) { + MGLOG_W("Mipmap level %d has no data, skipping update.", level); + continue; + } + + if (level > 0) + MGLOG_D("%s: Updating dirty mip %d for texture ID %u, size: %dx%d, " + "byteSize: %d", + __func__, level, m_backendTextureId, + textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).x(), + textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).y(), byteSize); + + auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); + MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, + MG_Util::ConvertGLEnumToString(err).c_str()); + }); + auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); + MG_External::GLES::glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, + static_cast(texelSize.x()), + static_cast(texelSize.y()), glFormat, glType, + textureMipmapObject->MapMipmapData(uploadTarget, level)); + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); + } + } + } + + textureMipmapObject->ClearAllStorageDirtyBit(); + break; + } + case TextureStorageType::Buffer: { + auto* textureBufferObject = + static_cast(stateTextureObject.get()); + auto& slot = textureBufferObject->GetBufferBindingSlot(); + auto buffer = slot.GetBoundObject(); + auto bufferIndex = buffer->GetExternalIndex(); + currentTextureInfo.bufferExternalIndex = bufferIndex; + + Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); + MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) " + "with ID: %u", m_backendTextureId); - // Regenerate all mipmap levels - GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, - &glFormat, &glType); - - const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); - for (auto uploadTarget : uploadTargets) { - for (SizeT level = 0; level < mipmapCount; ++level) { - auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); - bool levelDirty = textureMipmapObject->IsStorageDirty(uploadTarget, level); - auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); - auto* pData = (levelDirty && levelByteSize != 0) - ? textureMipmapObject->MapMipmapData(uploadTarget, level) - : nullptr; - MGLOG_D( - "%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", - __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, - levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, - levelDirty ? "true" : "false"); - - errorLopper.Clear(); - MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - auto textureTarget = stateTextureObject->GetTarget(); - // TODO: handle more texture types - switch (textureTarget) { - case TextureTarget::Texture2D: - case TextureTarget::TextureCubeMap: { - MG_External::GLES::glTexImage2D( - glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - 0, glFormat, glType, pData); - break; - } - case TextureTarget::Texture3D: { - MG_External::GLES::glTexImage3D( - glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); - break; - } - default: { - MGLOG_E("Unhandled texture target %s", - MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); - } - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, - glInternalFormat, glFormat, glType, pData](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " - "type=%s, pixels=%p", - func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), - MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), - MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), - MG_Util::ConvertGLEnumToString(glFormat).c_str(), - MG_Util::ConvertGLEnumToString(glType).c_str(), pData); - }); - MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, m_backendTextureId); - textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); - } + // Need to sync texture buffer if not synced yet + auto& backendBuffers = BufferImpl::g_backendBufferObjects; + SharedPtr backendBufferObject; + const auto& backendBufferIt = backendBuffers.find(buffer); + if (backendBufferIt == backendBuffers.end()) { + backendBufferObject = MakeShared(); + backendBuffers[buffer] = backendBufferObject; + } else { + backendBufferObject = backendBufferIt->second; } + backendBufferObject->SyncToBackend(buffer); - m_isInitialized = true; - } + // Bind buffer to texture + auto backendId = backendBufferObject->GetBackendBufferId(); - { // Update all dirty mipmap levels - const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, - &glFormat, &glType); - const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); - for (auto uploadTarget : uploadTargets) { - for (SizeT level = 0; level < mipmapCount; ++level) { - if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { - continue; - } + TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, + &glType); - auto byteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); - if (byteSize == 0) { - MGLOG_W("Mipmap level %d has no data, skipping update.", level); - continue; - } + MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); - if (level > 0) - MGLOG_D("%s: Updating dirty mip %d for texture ID %u, size: %dx%d, " - "byteSize: %d", - __func__, level, m_backendTextureId, - textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).x(), - textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).y(), byteSize); - - auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); - MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, - MG_Util::ConvertGLEnumToString(err).c_str()); - }); - auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - MG_External::GLES::glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, - static_cast(texelSize.x()), - static_cast(texelSize.y()), glFormat, glType, - textureMipmapObject->MapMipmapData(uploadTarget, level)); - textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); - } - } + textureBufferObject->ClearAllStorageDirtyBit(); + break; } - break; - } - case TextureStorageType::Buffer: { - auto* textureBufferObject = - static_cast(stateTextureObject.get()); - auto& slot = textureBufferObject->GetBufferBindingSlot(); - auto buffer = slot.GetBoundObject(); - auto bufferIndex = buffer->GetExternalIndex(); - currentTextureInfo.bufferExternalIndex = bufferIndex; - - Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); - MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) " - "with ID: %u", - m_backendTextureId); - - // Need to sync texture buffer if not synced yet - auto& backendBuffers = BufferImpl::g_backendBufferObjects; - SharedPtr backendBufferObject; - const auto& backendBufferIt = backendBuffers.find(buffer); - if (backendBufferIt == backendBuffers.end()) { - backendBufferObject = MakeShared(); - backendBuffers[buffer] = backendBufferObject; - } else { - backendBufferObject = backendBufferIt->second; + default: + THROW_UNIMPL_EXCEPTION; } - backendBufferObject->SyncToBackend(buffer); - - // Bind buffer to texture - auto backendId = backendBufferObject->GetBackendBufferId(); - - GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, - &glType); - - MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); - break; - } - default: - THROW_UNIMPL_EXCEPTION; } { // Update built-in sampler parameters diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 506e0cb8..3c884958 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -9,6 +9,7 @@ #pragma once #include #include "DirectGLES.h" +#include "Utils.h" #include "MG_State/GLState/SamplerState/SamplerObject.h" #include "MG_State/GLState/TextureState/TextureEnum.h" #include diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 911fca93..61a9d9f3 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -788,6 +788,7 @@ namespace MobileGL { auto* texBufferObject = static_cast(textureObject.get()); auto& bufferSlot = texBufferObject->GetBufferBindingSlot(); bufferSlot.Bind(bufferObject); + texBufferObject->SetStorageDirtyBit(); texBufferObject->SetInternalFormat(textureInternalFormat); } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index 2fbf712f..7f011c1d 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -115,6 +115,13 @@ namespace MobileGL { m_levelRange.y() = maxLevel; } + bool TextureObjectBase::IsDirty() const { + return m_dirtyBit; + } + bool TextureObjectBase::CheckDirtyBit(TextureDirtyBit bit) const { + return m_dirtyBit & bit; + } + Uint TextureObjectWithOneMipmap::GetMipmapLevelCount() const { return m_textureStorage.GetLevelCount(); } @@ -146,6 +153,9 @@ namespace MobileGL { void TextureObjectWithOneMipmap::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); + if (dirty) { + m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); + } } bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { @@ -189,6 +199,14 @@ namespace MobileGL { return true; } + void TextureObjectWithOneMipmap::ClearAllStorageDirtyBit() { + auto levelCount = GetMipmapLevelCount(); + for (Uint i = 0; i < levelCount; ++i) { + m_textureStorage.MarkDirty(0, i, false); + } + m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); + } + // TODO: add other texture types as needed } // namespace GLState diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.h b/MobileGL/MG_State/GLState/TextureState/TextureObject.h index dab1b41c..78d285be 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -17,6 +17,12 @@ namespace MobileGL { namespace MG_State { namespace GLState { + enum class TextureDirtyBit: Uint8 { + None = 0, + StorageDirtyBit = 1 << 0, // refers to mipmap or TexBuffer according to tex type + ParamDirtyBit = 1 << 1 + }; + class ITextureObject { public: using TargetEnum = TextureTarget; @@ -41,6 +47,8 @@ namespace MobileGL { virtual const UintVec2& GetLevelRange() const = 0; virtual void SetBaseLevel(Uint baseLevel) = 0; virtual void SetMaxLevel(Uint maxLevel) = 0; + virtual bool IsDirty() const = 0; + virtual bool CheckDirtyBit(TextureDirtyBit bit) const = 0; protected: virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0; @@ -67,6 +75,9 @@ namespace MobileGL { const UintVec2& GetLevelRange() const override; void SetBaseLevel(Uint baseLevel) override; void SetMaxLevel(Uint maxLevel) override; + bool IsDirty() const override; + bool CheckDirtyBit(TextureDirtyBit bit) const override; + virtual void ClearAllStorageDirtyBit() = 0; protected: const Uint m_externalIndex; @@ -77,6 +88,8 @@ namespace MobileGL { Vec4 m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; UintVec2 m_levelRange = {0, 1000}; + + Flags m_dirtyBit = TextureDirtyBit::None; }; class TextureObjectMipmap : public TextureObjectBase { @@ -92,7 +105,7 @@ namespace MobileGL { virtual void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) = 0; virtual void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) = 0; virtual void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) = 0; - virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) = 0; + virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty = true) = 0; virtual bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const = 0; }; @@ -110,6 +123,7 @@ namespace MobileGL { void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override; void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override; bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; + void ClearAllStorageDirtyBit() override; IntVec3 GetBaseSize() const override; Bool IsComplete() const override; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp index 8ed9113b..e7eea174 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp @@ -42,6 +42,9 @@ namespace MobileGL { void TextureObject2DCube::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); + if (dirty) { + m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); + } } bool TextureObject2DCube::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { @@ -82,6 +85,17 @@ namespace MobileGL { // TODO: add more completeness checks based on texture type and mipmap levels return true; } + + void TextureObject2DCube::ClearAllStorageDirtyBit() { + auto uploadTargetCount = GetUploadTargets().size(); + auto levelCount = GetMipmapLevelCount(); + for (SizeT target = 0; target < uploadTargetCount; ++target) { + for (Uint level = 0; level < levelCount; ++level) { + m_textureStorage.MarkDirty(target, level, false); + } + } + m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); + } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h index 2d97a8b2..a7f8abe9 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h @@ -26,6 +26,7 @@ namespace MobileGL { void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override; void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override; bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; + void ClearAllStorageDirtyBit() override; IntVec3 GetBaseSize() const override; Bool IsComplete() const override; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp index 4cb4f712..481e9760 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp @@ -23,6 +23,14 @@ namespace MobileGL { MOBILEGL_ASSERT(target == TextureUploadTarget::TextureBuffer, "Invalid TextureUploadTarget!"); return m_bufferBindingSlot; } + + void TextureObjectBuffer::ClearAllStorageDirtyBit() { + m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); + } + + void TextureObjectBuffer::SetStorageDirtyBit() { + m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); + } } // namespace GLState } // namespace MG_State } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h index f5d7928e..c2b4badf 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h @@ -20,6 +20,8 @@ namespace MobileGL { const Vector& GetUploadTargets() const override { return m_uploadTargets; } BindingSlot& GetBufferBindingSlot( TextureUploadTarget target = TextureUploadTarget::TextureBuffer); + void ClearAllStorageDirtyBit() override; + void SetStorageDirtyBit(); protected: Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override; diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index c4748b5c..94085dc6 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -278,6 +278,16 @@ namespace MobileGL { Flags(typename Underlying::type b) : flags(b) {} + Flags Set(const Bit b) { + flags |= static_cast(b); + return flags; + } + + Flags Clear(const Bit b) { + flags &= ~static_cast(b); + return flags; + } + // Flags - Bit Flags operator|(const Bit b) const { return Flags(flags | static_cast(b)); } From 39a8e72ee9e6da4949e01a2cd0e1a018ba77e65f Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 2 Feb 2026 18:28:17 +0800 Subject: [PATCH 07/74] [Perf] (MG_Backend/DirectGLES): Directly sync texture objects instead of collecting first. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 39 ++------------ MobileGL/MG_Backend/DirectGLES/Managers.cpp | 52 ++++++++++--------- 2 files changed, 33 insertions(+), 58 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index c0a4752c..96a4a56c 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -87,8 +87,7 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { void CreateAndSyncBufferObject(SharedPtr& bufferObject) { - if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) - return; + if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) return; const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); SharedPtr backendBufferObject; @@ -205,21 +204,13 @@ namespace MobileGL::MG_Backend::DirectGLES { // 1. textures bound to texture units (TODO: only sync ones that are used in current program) // 2. textures used in current FBO // 3. textures bound to image units (TODO) -// constexpr SizeT TextureTargetCount = static_cast(TextureTarget::TextureTargetCount); -// std::bitset dirtyTextureTargetBits; - - Vector> texturesToSync; for (int index = 0; index < MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; ++index) { auto& unit = MG_State::pGLContext->GetTextureUnitObject(index); for (const auto& bindingSlot : unit.GetAllBindingSlots()) { - const auto& textureObject = bindingSlot.GetBoundObject(); + auto textureObject = bindingSlot.GetBoundObject(); if (textureObject) { - const auto& end = texturesToSync.end(); - if (std::find(texturesToSync.begin(), end, textureObject) == end) { - texturesToSync.push_back(textureObject); -// dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); - } + SyncTextureObjectToBackend(textureObject); } } } @@ -229,32 +220,12 @@ namespace MobileGL::MG_Backend::DirectGLES { if (currentFBO) { for (const auto& attachment : currentFBO->GetAllAttachments()) { if (!attachment.IsTexture()) continue; - const auto& textureObject = attachment.GetTexture(); + auto textureObject = attachment.GetTexture(); if (textureObject) { - const auto& end = texturesToSync.end(); - if (std::find(texturesToSync.begin(), end, textureObject) == end) { - texturesToSync.push_back(textureObject); -// dirtyTextureTargetBits.set(static_cast(textureObject->GetTarget())); - } + SyncTextureObjectToBackend(textureObject); } } } - -// BufferImpl::BackendBufferBindingProtector pixelUnpackProtector = -// BufferImpl::BackendBufferBindingProtector(GL_PIXEL_UNPACK_BUFFER); -// -// Vector textureBindingProtectors; -// for (SizeT target = 0; target < TextureTargetCount; ++target) { -// if (dirtyTextureTargetBits[target]) { -// textureBindingProtectors.emplace_back( -// MG_Util::ConvertTextureTargetToGLEnum(static_cast(target))); -// } -// } - - // Do real sync - for (auto& textureObject : texturesToSync) { - SyncTextureObjectToBackend(textureObject); - } } } // namespace TextureImpl diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 492ae6f0..ca00ddf8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -382,8 +382,9 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str()); if (needsRegeneration) { - MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u", - m_backendTextureId); + MGLOG_D( + "Texture state changed significantly or not initialized, regenerating texture with ID: %u", + m_backendTextureId); // Regenerate all mipmap levels GLenum glInternalFormat, glType, glFormat; @@ -400,11 +401,11 @@ namespace MobileGL::MG_Backend::DirectGLES { auto* pData = (levelDirty && levelByteSize != 0) ? textureMipmapObject->MapMipmapData(uploadTarget, level) : nullptr; - MGLOG_D( - "%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", - __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, - levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, - levelDirty ? "true" : "false"); + MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, " + "levelDirty = %s", + __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), + level, levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), + levelByteSize, pData, levelDirty ? "true" : "false"); errorLopper.Clear(); MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); @@ -415,14 +416,15 @@ namespace MobileGL::MG_Backend::DirectGLES { case TextureTarget::TextureCubeMap: { MG_External::GLES::glTexImage2D( glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - 0, glFormat, glType, pData); + static_cast(levelTexelSize.x()), + static_cast(levelTexelSize.y()), 0, glFormat, glType, pData); break; } case TextureTarget::Texture3D: { MG_External::GLES::glTexImage3D( glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + static_cast(levelTexelSize.x()), + static_cast(levelTexelSize.y()), static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); break; } @@ -433,15 +435,17 @@ namespace MobileGL::MG_Backend::DirectGLES { } errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, glInternalFormat, glFormat, glType, pData](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " - "type=%s, pixels=%p", - func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), - MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), - MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), - MG_Util::ConvertGLEnumToString(glFormat).c_str(), - MG_Util::ConvertGLEnumToString(glType).c_str(), pData); + MGLOG_D( + "%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " + "type=%s, pixels=%p", + func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), + MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), + MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), + MG_Util::ConvertGLEnumToString(glFormat).c_str(), + MG_Util::ConvertGLEnumToString(glType).c_str(), pData); }); - MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, m_backendTextureId); + MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, + m_backendTextureId); textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); } } @@ -481,10 +485,10 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(err).c_str()); }); auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - MG_External::GLES::glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, - static_cast(texelSize.x()), - static_cast(texelSize.y()), glFormat, glType, - textureMipmapObject->MapMipmapData(uploadTarget, level)); + MG_External::GLES::glTexSubImage2D( + glUploadTarget, static_cast(level), 0, 0, + static_cast(texelSize.x()), static_cast(texelSize.y()), glFormat, + glType, textureMipmapObject->MapMipmapData(uploadTarget, level)); textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); } } @@ -522,8 +526,8 @@ namespace MobileGL::MG_Backend::DirectGLES { auto backendId = backendBufferObject->GetBackendBufferId(); GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, - &glType); + TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, + &glFormat, &glType); MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); From de17fdf5b0b22f604ae6ba22b191cd837651f256 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 2 Feb 2026 20:35:03 +0800 Subject: [PATCH 08/74] [Misc] (MG_Backend/DirectGLES): Separate BackendTextureObject::SyncToBackend --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 4 +- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 529 ++++++++++-------- MobileGL/MG_Backend/DirectGLES/Managers.h | 4 +- 3 files changed, 297 insertions(+), 240 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 96a4a56c..fb55df5f 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -192,7 +192,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendTextureObject = backendTextureIt->second; } - backendTextureObject->SyncToBackend(textureObject); + backendTextureObject->SyncTextureParamsToBackend(textureObject); + backendTextureObject->SyncBuiltinSamplerToBackend(textureObject); + backendTextureObject->SyncMipmapsToBackend(textureObject); return backendTextureObject; } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index ca00ddf8..31558319 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -319,17 +319,24 @@ namespace MobileGL::MG_Backend::DirectGLES { return m_backendTextureId; } - void BackendTextureObject::SyncToBackend(SharedPtr& stateTextureObject) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - DebugImpl::ErrorLopper errorLopper; + void BackendTextureObject::SyncMipmapsToBackend( + SharedPtr& stateTextureObject) { if (!stateTextureObject) { MGLOG_E("State texture object is null, cannot sync to backend."); return; } - MGLOG_D("Syncing texture with backend ID %u to backend for state ID %u", m_backendTextureId, + if (!stateTextureObject->CheckDirtyBit(MG_State::GLState::TextureDirtyBit::StorageDirtyBit)) { + MGLOG_D("Texture parameters changed but storage is not dirty, skipping mipmap sync for texture ID: %u", + m_backendTextureId); + return; + } +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + DebugImpl::ErrorLopper errorLopper; + + MGLOG_D("Syncing texture mipmaps with backend ID %u to backend for state ID %u", m_backendTextureId, stateTextureObject->GetExternalIndex()); GLenum target = MG_Util::ConvertTextureTargetToGLEnum(stateTextureObject->GetTarget()); @@ -354,11 +361,8 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - // BackendTextureBindingProtector backendTextureBindingProtector(target); Bind(target); - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); + const auto baseSize = stateTextureObject->GetBaseSize(); StateTextureBasicInfo currentTextureInfo = {stateTextureObject->GetFormat(), static_cast(baseSize.x()), @@ -366,182 +370,211 @@ namespace MobileGL::MG_Backend::DirectGLES { static_cast(baseSize.z()), 0, 0}; + switch (stateTextureObject->GetStorageType()) { + case TextureStorageType::Mipmap: { + auto* textureMipmapObject = + static_cast(stateTextureObject.get()); + const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); + currentTextureInfo.mipmapLevels = mipmapCount; - if (stateTextureObject->CheckDirtyBit(MG_State::GLState::TextureDirtyBit::StorageDirtyBit)) { - switch (stateTextureObject->GetStorageType()) { - case TextureStorageType::Mipmap: { - auto* textureMipmapObject = - static_cast(stateTextureObject.get()); - const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); - currentTextureInfo.mipmapLevels = mipmapCount; + Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); - Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); + MGLOG_D("%s: Got texture info: %dx%dx%d, mips %d, format %s", __func__, baseSize.x(), baseSize.y(), + baseSize.z(), mipmapCount, + MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str()); - MGLOG_D("%s: Got texture info: %dx%dx%d, mips %d, format %s", __func__, baseSize.x(), baseSize.y(), - baseSize.z(), mipmapCount, - MG_Util::ConvertTextureInternalFormatToString(textureMipmapObject->GetFormat()).c_str()); - - if (needsRegeneration) { - MGLOG_D( - "Texture state changed significantly or not initialized, regenerating texture with ID: %u", + if (needsRegeneration) { + MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u", m_backendTextureId); - // Regenerate all mipmap levels - GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, - &glFormat, &glType); + // Regenerate all mipmap levels + GLenum glInternalFormat, glType, glFormat; + TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, + &glFormat, &glType); - const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); - for (auto uploadTarget : uploadTargets) { - for (SizeT level = 0; level < mipmapCount; ++level) { - auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); - bool levelDirty = textureMipmapObject->IsStorageDirty(uploadTarget, level); - auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); - auto* pData = (levelDirty && levelByteSize != 0) - ? textureMipmapObject->MapMipmapData(uploadTarget, level) - : nullptr; - MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, " - "levelDirty = %s", - __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), - level, levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), - levelByteSize, pData, levelDirty ? "true" : "false"); + const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + for (auto uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); + auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); + bool levelDirty = textureMipmapObject->IsStorageDirty(uploadTarget, level); + auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); + auto* pData = (levelDirty && levelByteSize != 0) + ? textureMipmapObject->MapMipmapData(uploadTarget, level) + : nullptr; + MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, " + "levelDirty = %s", + __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, + levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, + levelDirty ? "true" : "false"); - errorLopper.Clear(); - MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - auto textureTarget = stateTextureObject->GetTarget(); - // TODO: handle more texture types - switch (textureTarget) { - case TextureTarget::Texture2D: - case TextureTarget::TextureCubeMap: { - MG_External::GLES::glTexImage2D( - glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), - static_cast(levelTexelSize.y()), 0, glFormat, glType, pData); - break; - } - case TextureTarget::Texture3D: { - MG_External::GLES::glTexImage3D( - glUploadTarget, static_cast(level), glInternalFormat, - static_cast(levelTexelSize.x()), - static_cast(levelTexelSize.y()), - static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); - break; - } - default: { - MGLOG_E("Unhandled texture target %s", - MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); - } - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, - glInternalFormat, glFormat, glType, pData](GLenum err) { - MGLOG_D( - "%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " + errorLopper.Clear(); + MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + auto textureTarget = stateTextureObject->GetTarget(); + // TODO: handle more texture types + switch (textureTarget) { + case TextureTarget::Texture2D: + case TextureTarget::TextureCubeMap: { + MG_External::GLES::glTexImage2D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + 0, glFormat, glType, pData); + break; + } + case TextureTarget::Texture3D: { + MG_External::GLES::glTexImage3D( + glUploadTarget, static_cast(level), glInternalFormat, + static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), + static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); + break; + } + default: { + MGLOG_E("Unhandled texture target %s", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + } + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, + glInternalFormat, glFormat, glType, pData](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " "type=%s, pixels=%p", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), MG_Util::ConvertGLEnumToString(glUploadTarget).c_str(), MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), MG_Util::ConvertGLEnumToString(glFormat).c_str(), MG_Util::ConvertGLEnumToString(glType).c_str(), pData); - }); - MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, - m_backendTextureId); - textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); - } - } - - m_isInitialized = true; - } - - { // Update all dirty mipmap levels - const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); - GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, - &glFormat, &glType); - const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); - for (auto uploadTarget : uploadTargets) { - for (SizeT level = 0; level < mipmapCount; ++level) { - if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { - continue; - } - - auto byteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); - if (byteSize == 0) { - MGLOG_W("Mipmap level %d has no data, skipping update.", level); - continue; - } - - if (level > 0) - MGLOG_D("%s: Updating dirty mip %d for texture ID %u, size: %dx%d, " - "byteSize: %d", - __func__, level, m_backendTextureId, - textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).x(), - textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).y(), byteSize); - - auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); - MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, - MG_Util::ConvertGLEnumToString(err).c_str()); - }); - auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - MG_External::GLES::glTexSubImage2D( - glUploadTarget, static_cast(level), 0, 0, - static_cast(texelSize.x()), static_cast(texelSize.y()), glFormat, - glType, textureMipmapObject->MapMipmapData(uploadTarget, level)); - textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); - } + }); + MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, m_backendTextureId); + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); } } - textureMipmapObject->ClearAllStorageDirtyBit(); - break; + m_isInitialized = true; } - case TextureStorageType::Buffer: { - auto* textureBufferObject = - static_cast(stateTextureObject.get()); - auto& slot = textureBufferObject->GetBufferBindingSlot(); - auto buffer = slot.GetBoundObject(); - auto bufferIndex = buffer->GetExternalIndex(); - currentTextureInfo.bufferExternalIndex = bufferIndex; - - Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); - MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) " - "with ID: %u", - m_backendTextureId); - - // Need to sync texture buffer if not synced yet - auto& backendBuffers = BufferImpl::g_backendBufferObjects; - SharedPtr backendBufferObject; - const auto& backendBufferIt = backendBuffers.find(buffer); - if (backendBufferIt == backendBuffers.end()) { - backendBufferObject = MakeShared(); - backendBuffers[buffer] = backendBufferObject; - } else { - backendBufferObject = backendBufferIt->second; - } - backendBufferObject->SyncToBackend(buffer); - - // Bind buffer to texture - auto backendId = backendBufferObject->GetBackendBufferId(); + { // Update all dirty mipmap levels + const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); GLenum glInternalFormat, glType, glFormat; - TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, + TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, &glFormat, &glType); + const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + for (auto uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { + continue; + } - MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); + auto byteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); + if (byteSize == 0) { + MGLOG_W("Mipmap level %d has no data, skipping update.", level); + continue; + } - textureBufferObject->ClearAllStorageDirtyBit(); - break; + if (level > 0) + MGLOG_D("%s: Updating dirty mip %d for texture ID %u, size: %dx%d, " + "byteSize: %d", + __func__, level, m_backendTextureId, + textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).x(), + textureMipmapObject->GetMipmapTexelSize(uploadTarget, level).y(), byteSize); + + auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); + MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, + MG_Util::ConvertGLEnumToString(err).c_str()); + }); + auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); + MG_External::GLES::glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, + static_cast(texelSize.x()), + static_cast(texelSize.y()), glFormat, glType, + textureMipmapObject->MapMipmapData(uploadTarget, level)); + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); + } + } } - default: - THROW_UNIMPL_EXCEPTION; + + textureMipmapObject->ClearAllStorageDirtyBit(); + break; + } + case TextureStorageType::Buffer: { + auto* textureBufferObject = + static_cast(stateTextureObject.get()); + auto& slot = textureBufferObject->GetBufferBindingSlot(); + auto buffer = slot.GetBoundObject(); + auto bufferIndex = buffer->GetExternalIndex(); + currentTextureInfo.bufferExternalIndex = bufferIndex; + + Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); + MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) " + "with ID: %u", + m_backendTextureId); + + // Need to sync texture buffer if not synced yet + auto& backendBuffers = BufferImpl::g_backendBufferObjects; + SharedPtr backendBufferObject; + const auto& backendBufferIt = backendBuffers.find(buffer); + if (backendBufferIt == backendBuffers.end()) { + backendBufferObject = MakeShared(); + backendBuffers[buffer] = backendBufferObject; + } else { + backendBufferObject = backendBufferIt->second; } + backendBufferObject->SyncToBackend(buffer); + + // Bind buffer to texture + auto backendId = backendBufferObject->GetBackendBufferId(); + + GLenum glInternalFormat, glType, glFormat; + TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, + &glType); + + MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); + + textureBufferObject->ClearAllStorageDirtyBit(); + break; + } + default: + THROW_UNIMPL_EXCEPTION; } - { // Update built-in sampler parameters - MGLOG_D("Updating sampler parameters for texture with ID: %u", m_backendTextureId); - const auto& samplerParams = stateTextureObject->GetSamplerObject()->GetAllSamplerParameters(); + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + m_prevTextureInfo = currentTextureInfo; + } + + void BackendTextureObject::SyncBuiltinSamplerToBackend( + SharedPtr& stateTextureObject) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + DebugImpl::ErrorLopper errorLopper; + if (!stateTextureObject) { + MGLOG_E("State texture object is null, cannot sync to backend."); + return; + } + + MGLOG_D("Syncing texture built-in sampler with backend ID %u to backend for state ID %u", + m_backendTextureId, stateTextureObject->GetExternalIndex()); + + GLenum target = MG_Util::ConvertTextureTargetToGLEnum(stateTextureObject->GetTarget()); + auto targetInternal = stateTextureObject->GetTarget(); + MGLOG_D(" Texture target for syncing is %s", + MG_Util::ConvertTextureTargetToString(targetInternal).c_str()); + if (!IsSupportedTextureTarget(targetInternal)) { + MGLOG_E(" Texture target %s is not supported, skipping.", + MG_Util::ConvertTextureTargetToString(targetInternal).c_str()); + return; + } + + Bind(target); + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + // Update built-in sampler parameters + MGLOG_D("Updating sampler parameters for texture with ID: %u", m_backendTextureId); + const auto& samplerParams = stateTextureObject->GetSamplerObject()->GetAllSamplerParameters(); #define SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \ if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \ @@ -555,100 +588,120 @@ namespace MobileGL::MG_Backend::DirectGLES { }); \ } - if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || - m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { - MG_External::GLES::glTexParameteri( - target, GL_TEXTURE_MIN_FILTER, - MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode)); - m_cacheSamplerParameters.minFilter = samplerParams.minFilter; - m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; - } - if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) { - MG_External::GLES::glTexParameteri( - target, GL_TEXTURE_MAG_FILTER, - MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None)); - m_cacheSamplerParameters.magFilter = samplerParams.magFilter; - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); + if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || + m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { + MG_External::GLES::glTexParameteri( + target, GL_TEXTURE_MIN_FILTER, + MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode)); + m_cacheSamplerParameters.minFilter = samplerParams.minFilter; + m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; + } + if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) { + MG_External::GLES::glTexParameteri( + target, GL_TEXTURE_MAG_FILTER, + MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None)); + m_cacheSamplerParameters.magFilter = samplerParams.magFilter; + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); - SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapS, GL_TEXTURE_WRAP_S, WrapMode) - SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapT, GL_TEXTURE_WRAP_T, WrapMode) - SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode) - SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareFunc, GL_TEXTURE_COMPARE_FUNC, CompareFunc) - SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareMode, GL_TEXTURE_COMPARE_MODE, CompareMode) - if (m_cacheSamplerParameters.minLod != samplerParams.minLod) { - MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MIN_LOD, samplerParams.minLod); - m_cacheSamplerParameters.minLod = samplerParams.minLod; - } - if (m_cacheSamplerParameters.maxLod != samplerParams.maxLod) { - MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerParams.maxLod); - m_cacheSamplerParameters.maxLod = samplerParams.maxLod; - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); + SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapS, GL_TEXTURE_WRAP_S, WrapMode) + SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapT, GL_TEXTURE_WRAP_T, WrapMode) + SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode) + SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareFunc, GL_TEXTURE_COMPARE_FUNC, CompareFunc) + SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareMode, GL_TEXTURE_COMPARE_MODE, CompareMode) + if (m_cacheSamplerParameters.minLod != samplerParams.minLod) { + MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MIN_LOD, samplerParams.minLod); + m_cacheSamplerParameters.minLod = samplerParams.minLod; + } + if (m_cacheSamplerParameters.maxLod != samplerParams.maxLod) { + MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerParams.maxLod); + m_cacheSamplerParameters.maxLod = samplerParams.maxLod; + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); #undef SYNC_TEX_SAMPLER_PARAM_IF_CHANGED + } + + void BackendTextureObject::SyncTextureParamsToBackend( + SharedPtr& stateTextureObject) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + DebugImpl::ErrorLopper errorLopper; + if (!stateTextureObject) { + MGLOG_E("State texture object is null, cannot sync to backend."); + return; } - { // Update texture parameters - MGLOG_D("Updating texture parameters for texture with ID: %u", m_backendTextureId); + MGLOG_D("Syncing texture params with backend ID %u to backend for state ID %u", m_backendTextureId, + stateTextureObject->GetExternalIndex()); - const auto& levelRange = stateTextureObject->GetLevelRange(); + GLenum target = MG_Util::ConvertTextureTargetToGLEnum(stateTextureObject->GetTarget()); + auto targetInternal = stateTextureObject->GetTarget(); + MGLOG_D(" Texture target for syncing is %s", + MG_Util::ConvertTextureTargetToString(targetInternal).c_str()); + if (!IsSupportedTextureTarget(targetInternal)) { + MGLOG_E(" Texture target %s is not supported, skipping.", + MG_Util::ConvertTextureTargetToString(targetInternal).c_str()); + return; + } - if (m_cacheLodRange.x() != levelRange.x()) { - MG_External::GLES::glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, - static_cast(levelRange.x())); - m_cacheLodRange.x() = levelRange.x(); - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - if (m_cacheLodRange.y() != levelRange.y()) { - MG_External::GLES::glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, - static_cast(levelRange.y())); - m_cacheLodRange.y() = levelRange.y(); - } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); + Bind(target); + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); - const auto& swizzleParams = stateTextureObject->GetAllSwizzleParams(); - if (swizzleParams != m_cacheSwizzleParams) { + // Update texture parameters + MGLOG_D("Updating texture parameters for texture with ID: %u", m_backendTextureId); + + const auto& levelRange = stateTextureObject->GetLevelRange(); + + if (m_cacheLodRange.x() != levelRange.x()) { + MG_External::GLES::glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast(levelRange.x())); + m_cacheLodRange.x() = levelRange.x(); + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + if (m_cacheLodRange.y() != levelRange.y()) { + MG_External::GLES::glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast(levelRange.y())); + m_cacheLodRange.y() = levelRange.y(); + } + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + const auto& swizzleParams = stateTextureObject->GetAllSwizzleParams(); + if (swizzleParams != m_cacheSwizzleParams) { #define SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(func, glEnum) \ if (m_cacheSwizzleParams.func != swizzleParams.func) { \ MG_External::GLES::glTexParameteri(target, glEnum, \ MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams.func)); \ m_cacheSwizzleParams.func = swizzleParams.func; \ } - SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(r(), GL_TEXTURE_SWIZZLE_R); - SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(g(), GL_TEXTURE_SWIZZLE_G); - SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(b(), GL_TEXTURE_SWIZZLE_B); - SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(a(), GL_TEXTURE_SWIZZLE_A); + SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(r(), GL_TEXTURE_SWIZZLE_R); + SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(g(), GL_TEXTURE_SWIZZLE_G); + SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(b(), GL_TEXTURE_SWIZZLE_B); + SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(a(), GL_TEXTURE_SWIZZLE_A); #undef SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED - m_cacheSwizzleParams = swizzleParams; - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - } - - if (m_cacheBorderColor != stateTextureObject->GetBorderColor()) { - const auto& borderColor = stateTextureObject->GetBorderColor(); - GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()}; - MG_External::GLES::glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray); - m_cacheBorderColor = borderColor; - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - } + m_cacheSwizzleParams = swizzleParams; + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); } - errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { - MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - - m_prevTextureInfo = currentTextureInfo; + if (m_cacheBorderColor != stateTextureObject->GetBorderColor()) { + const auto& borderColor = stateTextureObject->GetBorderColor(); + GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()}; + MG_External::GLES::glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray); + m_cacheBorderColor = borderColor; + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + } } UnorderedMap, SharedPtr> diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 3c884958..bb9ceb69 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -86,7 +86,9 @@ namespace MobileGL::MG_Backend::DirectGLES { class BackendTextureObject { public: BackendTextureObject(); - void SyncToBackend(SharedPtr& stateTextureObject); + void SyncMipmapsToBackend(SharedPtr& stateTextureObject); + void SyncBuiltinSamplerToBackend(SharedPtr& stateTextureObject); + void SyncTextureParamsToBackend(SharedPtr& stateTextureObject); void Bind(GLenum target); Uint GetBackendTextureId(); From 7f1cb14d3386daaf4d4246d21846d320d625f754 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 2 Feb 2026 22:00:29 +0800 Subject: [PATCH 09/74] [Feat] (MG_State/SamplerState): Add resource version. --- .../GLState/SamplerState/SamplerObject.cpp | 14 ++++++++++++++ .../MG_State/GLState/SamplerState/SamplerObject.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp index 2e24802b..9721be49 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp @@ -15,26 +15,32 @@ namespace MobileGL { void SamplerObject::SetWrapS(SamplerWrapMode mode) { m_samplerParameters.wrapS = mode; + ++m_version; } void SamplerObject::SetWrapT(SamplerWrapMode mode) { m_samplerParameters.wrapT = mode; + ++m_version; } void SamplerObject::SetWrapR(SamplerWrapMode mode) { m_samplerParameters.wrapR = mode; + ++m_version; } void SamplerObject::SetMinFilter(SamplerFilterMode mode) { m_samplerParameters.minFilter = mode; + ++m_version; } void SamplerObject::SetMagFilter(SamplerFilterMode mode) { m_samplerParameters.magFilter = mode; + ++m_version; } void SamplerObject::SetMipmapMode(SamplerMipmapMode mode) { m_samplerParameters.mipmapMode = mode; + ++m_version; } void SamplerObject::SetLodRange(Float minLod, Float maxLod) { @@ -43,18 +49,22 @@ namespace MobileGL { } m_samplerParameters.minLod = minLod; m_samplerParameters.maxLod = maxLod; + ++m_version; } void SamplerObject::SetLodBias(Float bias) { m_samplerParameters.lodBias = bias; + ++m_version; } void SamplerObject::SetSamplerCompareFunc(SamplerCompareFunc func) { m_samplerParameters.compareFunc = func; + ++m_version; } void SamplerObject::SetCompareMode(SamplerCompareMode mode) { m_samplerParameters.compareMode = mode; + ++m_version; } SamplerWrapMode SamplerObject::GetWrapS() const { @@ -108,6 +118,10 @@ namespace MobileGL { const SamplerParameters& SamplerObject::GetAllSamplerParameters() const { return m_samplerParameters; } + + Uint16 SamplerObject::GetVersion() const { + return m_version; + } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h index 9b56f4b5..c1468677 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h @@ -98,10 +98,12 @@ namespace MobileGL { SamplerCompareMode GetCompareMode() const; SamplerCompareFunc GetSamplerCompareFunc() const; Uint GetExternalIndex() const; + Uint16 GetVersion() const; const SamplerParameters& GetAllSamplerParameters() const; private: const Uint m_externalIndex; + Uint16 m_version = 0; SamplerParameters m_samplerParameters; }; } // namespace GLState From 3f0744a8ced03f584d41ec19ba286d0a83aa39c5 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 2 Feb 2026 22:57:29 +0800 Subject: [PATCH 10/74] [Chore] (CMake): Use ThinLTO with priority if possible. --- CMakeLists.txt | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e04eae25..f08dce4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,10 +13,29 @@ if (ANDROID) endif() if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug" OR MOBILEGL_FORCE_RELEASE_OPT) + # Check if ThinLTO or LTO is suppported include(CheckIPOSupported) + include(CheckCCompilerFlag) + include(CheckCXXCompilerFlag) + check_ipo_supported(RESULT LTOSupported OUTPUT LTOError) + if (LTOSupported) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + # Check ThinLTO + check_c_compiler_flag("-flto=thin" HAS_THINLTO_C) + check_cxx_compiler_flag("-flto=thin" HAS_THINLTO_CXX) + if (HAS_THINLTO_C AND HAS_THINLTO_CXX) + message(STATUS "ThinLTO supported, using -flto=thin") + + add_compile_options(-flto=thin) + add_link_options(-flto=thin) + else() + # ThinLTO is not supported + message(STATUS "ThinLTO not available, fallback to CMAKE IPO") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() + else() + message(STATUS "IPO not supported: ${LTOError}") endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MATCHES "AppleClang") From 653ca9ea74390e003adb15ce6c6e137f14f15cbb Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 00:08:25 +0800 Subject: [PATCH 11/74] [Feat|Perf] (MG_Backend/DirectGLES): Skip whole sampler object when not dirty. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 46 ++++++++------------- MobileGL/MG_Backend/DirectGLES/Managers.h | 2 + 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 31558319..7c8ffb62 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -554,6 +554,14 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + Uint currentSamplerVersion = stateTextureObject->GetSamplerObject()->GetVersion(); + if (m_syncedSamplerVersion == currentSamplerVersion) { + MGLOG_D("Sampler parameters have not changed for texture ID: %u, skipping sync.", m_backendTextureId); + return; + } + + m_syncedSamplerVersion = currentSamplerVersion; + MGLOG_D("Syncing texture built-in sampler with backend ID %u to backend for state ID %u", m_backendTextureId, stateTextureObject->GetExternalIndex()); @@ -987,16 +995,7 @@ namespace MobileGL::MG_Backend::DirectGLES { source = ProcessOutColorLocations(source); source = ForceSupporterOutput(source); - // TODO: probably a patch system? - // String findStr = "if (distance_weight_sum == 0.0)"; - // String replaceStr = "if (distance_weight_sum <= 0.0001)"; - // auto pos = source.find(findStr); - // while (pos != String::npos) { - // MGLOG_D("Applying patch #1 to Photon..."); - // source.replace(pos, findStr.length(), replaceStr); - // pos = source.find(findStr, pos); - // } - + // Patch for Photon compiler precision issue String findStr = "1000000.0"; String replaceStr = "65500.0"; auto pos = source.find(findStr); @@ -1006,24 +1005,6 @@ namespace MobileGL::MG_Backend::DirectGLES { pos = source.find(findStr, pos); } - // findStr = "if (gtao.w == 0.0)"; - // replaceStr = "if (abs(gtao.w) <= 0.00001)"; - // pos = source.find(findStr); - // while (pos != String::npos) { - // MGLOG_D("Applying patch #3 to Photon..."); - // source.replace(pos, findStr.length(), replaceStr); - // pos = source.find(findStr, pos); - // } - - // findStr = "== 0.0"; - // replaceStr = "<= 0.00001"; - // pos = source.find(findStr); - // while (pos != String::npos) { - // MGLOG_D("Applying patch #4 to Photon..."); - // source.replace(pos, findStr.length(), replaceStr); - // pos = source.find(findStr, pos); - // } - const char* sourceCStr = source.c_str(); MGLOG_D("Setting shader source for backend shader ID: %u\nsrc:\n%s", backendShaderId, sourceCStr); MG_External::GLES::glShaderSource(backendShaderId, 1, &sourceCStr, nullptr); @@ -1110,6 +1091,15 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + Uint currentSamplerVersion = stateSamplerObject->GetVersion(); + if (m_isInitialized && m_syncedSamplerVersion == currentSamplerVersion) { + MGLOG_D("Sampler parameters have not changed for sampler ID: %u, skipping sync.", + stateSamplerObject->GetExternalIndex()); + return; + } + + m_syncedSamplerVersion = currentSamplerVersion; + MGLOG_D("Syncing sampler with backend ID %u to backend for state ID %u", m_backendSamplerId, stateSamplerObject->GetExternalIndex()); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index bb9ceb69..7ca7da85 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -101,6 +101,7 @@ namespace MobileGL::MG_Backend::DirectGLES { FloatVec4 m_cacheBorderColor = {0.0f, 0.0f, 0.0f, 0.0f}; Vec4 m_cacheSwizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; + Uint16 m_syncedSamplerVersion = 0; }; extern UnorderedMap, SharedPtr> @@ -181,6 +182,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint m_backendSamplerId = 0; Bool m_isInitialized = false; SamplerParameters m_cacheSamplerParameters; + Uint16 m_syncedSamplerVersion = 0; }; extern UnorderedMap, SharedPtr> From 7daa69cf73581204cc36891e133eae3458631382 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 00:12:31 +0800 Subject: [PATCH 12/74] [Feat] (MG_State/TextureState): Add resource version for texture params. --- .../GLState/TextureState/TextureObject.cpp | 23 +++++++++++++++---- .../GLState/TextureState/TextureObject.h | 22 ++++++++++-------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index 7f011c1d..b28b2c89 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "TextureObject.h" +#include "MG_Util/Types.h" #include namespace MobileGL { @@ -44,6 +45,7 @@ namespace MobileGL { void TextureObjectBase::SetInternalFormat(TextureInternalFormat format) { m_internalFormat = format; + ++m_textureParamsVersion; } Uint TextureObjectBase::GetExternalIndex() const { @@ -56,6 +58,7 @@ namespace MobileGL { void TextureObjectBase::SetBorderColor(const FloatVec4& color) { m_borderColor = color; + ++m_textureParamsVersion; } TextureSwizzleParam TextureObjectBase::GetSwizzleParam(TextureSwizzleParam param) const { @@ -98,9 +101,12 @@ namespace MobileGL { static_cast(param)); break; } + ++m_textureParamsVersion; } + void TextureObjectBase::SetSwizzleParamRGBA(const Vec4& values) { m_swizzleParams = values; + ++m_textureParamsVersion; } const UintVec2& TextureObjectBase::GetLevelRange() const { @@ -109,19 +115,26 @@ namespace MobileGL { void TextureObjectBase::SetBaseLevel(Uint baseLevel) { m_levelRange.x() = baseLevel; + ++m_textureParamsVersion; } void TextureObjectBase::SetMaxLevel(Uint maxLevel) { m_levelRange.y() = maxLevel; + ++m_textureParamsVersion; } - bool TextureObjectBase::IsDirty() const { + Bool TextureObjectBase::IsDirty() const { return m_dirtyBit; } - bool TextureObjectBase::CheckDirtyBit(TextureDirtyBit bit) const { + + Bool TextureObjectBase::CheckDirtyBit(TextureDirtyBit bit) const { return m_dirtyBit & bit; } + Uint16 TextureObjectBase::GetTextureParamsVersion() const { + return m_textureParamsVersion; + } + Uint TextureObjectWithOneMipmap::GetMipmapLevelCount() const { return m_textureStorage.GetLevelCount(); } @@ -151,14 +164,14 @@ namespace MobileGL { } void TextureObjectWithOneMipmap::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, - bool dirty) { + Bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); if (dirty) { m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); } } - bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { + Bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { return m_textureStorage.IsDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel); } @@ -180,7 +193,7 @@ namespace MobileGL { // For some reason mojang decided to have 0x0 in last level mipmap // Relaxing checks for that - bool hadZero = false; + Bool hadZero = false; for (SizeT i = 0; i < levelCount; ++i) { const auto& levelSize = m_textureStorage.GetTexelSize(0, i); if (levelSize.x() <= 0 || levelSize.y() <= 0 || levelSize.z() <= 0) { diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.h b/MobileGL/MG_State/GLState/TextureState/TextureObject.h index 78d285be..a094be60 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -17,7 +17,7 @@ namespace MobileGL { namespace MG_State { namespace GLState { - enum class TextureDirtyBit: Uint8 { + enum class TextureDirtyBit : Uint8 { None = 0, StorageDirtyBit = 1 << 0, // refers to mipmap or TexBuffer according to tex type ParamDirtyBit = 1 << 1 @@ -47,8 +47,9 @@ namespace MobileGL { virtual const UintVec2& GetLevelRange() const = 0; virtual void SetBaseLevel(Uint baseLevel) = 0; virtual void SetMaxLevel(Uint maxLevel) = 0; - virtual bool IsDirty() const = 0; - virtual bool CheckDirtyBit(TextureDirtyBit bit) const = 0; + virtual Bool IsDirty() const = 0; + virtual Bool CheckDirtyBit(TextureDirtyBit bit) const = 0; + virtual Uint16 GetTextureParamsVersion() const = 0; protected: virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0; @@ -75,8 +76,9 @@ namespace MobileGL { const UintVec2& GetLevelRange() const override; void SetBaseLevel(Uint baseLevel) override; void SetMaxLevel(Uint maxLevel) override; - bool IsDirty() const override; - bool CheckDirtyBit(TextureDirtyBit bit) const override; + Bool IsDirty() const override; + Bool CheckDirtyBit(TextureDirtyBit bit) const override; + Uint16 GetTextureParamsVersion() const override; virtual void ClearAllStorageDirtyBit() = 0; protected: @@ -88,6 +90,7 @@ namespace MobileGL { Vec4 m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; UintVec2 m_levelRange = {0, 1000}; + Uint16 m_textureParamsVersion = 0; Flags m_dirtyBit = TextureDirtyBit::None; }; @@ -105,8 +108,9 @@ namespace MobileGL { virtual void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) = 0; virtual void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) = 0; virtual void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) = 0; - virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty = true) = 0; - virtual bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const = 0; + virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, + Bool dirty = true) = 0; + virtual Bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const = 0; }; class TextureObjectWithOneMipmap : public TextureObjectMipmap { @@ -121,8 +125,8 @@ namespace MobileGL { void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) override; void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) override; void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override; - void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override; - bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; + void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) override; + Bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; void ClearAllStorageDirtyBit() override; IntVec3 GetBaseSize() const override; From 4225cc43338af2f974f9c5ad196976ac15a84674 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 00:17:02 +0800 Subject: [PATCH 13/74] [Feat|Perf] (MG_Backend/DirectGLES): Skip whole texture params sync when not dirty. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 7 +++++++ MobileGL/MG_Backend/DirectGLES/Managers.h | 1 + 2 files changed, 8 insertions(+) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 7c8ffb62..e33b7026 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -644,6 +644,13 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + Uint16 currentTextureParamsVersion = stateTextureObject->GetTextureParamsVersion(); + if (m_syncedTextureParamsVersion == currentTextureParamsVersion) { + MGLOG_D("Texture parameters have not changed for texture ID: %u, skipping sync.", m_backendTextureId); + return; + } + m_syncedTextureParamsVersion = currentTextureParamsVersion; + MGLOG_D("Syncing texture params with backend ID %u to backend for state ID %u", m_backendTextureId, stateTextureObject->GetExternalIndex()); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 7ca7da85..196fd040 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -102,6 +102,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Vec4 m_cacheSwizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; Uint16 m_syncedSamplerVersion = 0; + Uint16 m_syncedTextureParamsVersion = 0; }; extern UnorderedMap, SharedPtr> From 99d32439a803f1121e4a0ba267774a348bbd748c Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 00:39:55 +0800 Subject: [PATCH 14/74] [Perf] (MG_Backend/DirectGLES): Remove binding state protectors. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 20 ----- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 7 -- MobileGL/MG_Backend/DirectGLES/Utils.cpp | 86 +------------------ MobileGL/MG_Backend/DirectGLES/Utils.h | 52 +---------- 4 files changed, 5 insertions(+), 160 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index fb55df5f..820f04e9 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -844,16 +844,6 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - FramebufferImpl::BackendFramebufferBindingProtector drawFboProtector(GL_DRAW_FRAMEBUFFER); - FramebufferImpl::BackendFramebufferBindingProtector readFboProtector(GL_READ_FRAMEBUFFER); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - - FramebufferImpl::BackendFramebufferBindingProtector::BindTempFBO(FramebufferTarget::Draw); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); GLint currentTex; MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); @@ -925,15 +915,6 @@ namespace MobileGL::MG_Backend::DirectGLES { }); } else { MGLOG_D("%s: Backend depth", __func__); - FramebufferImpl::BackendFramebufferBindingProtector drawFboProtector(GL_DRAW_FRAMEBUFFER); - FramebufferImpl::BackendFramebufferBindingProtector readFboProtector(GL_READ_FRAMEBUFFER); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - FramebufferImpl::BackendFramebufferBindingProtector::BindTempFBO(FramebufferTarget::Draw); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); GLint currentTex; MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { @@ -971,7 +952,6 @@ namespace MobileGL::MG_Backend::DirectGLES { auto texture = slot.GetBoundObject(); auto backendTexture = TextureImpl::SyncTextureObjectToBackend(texture); - TextureImpl::BackendTextureBindingProtector protector(target); backendTexture->Bind(target); MG_External::GLES::glGenerateMipmap(target); } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index e33b7026..04af6811 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -105,7 +105,6 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget); MGLOG_D("Syncing buffer data (glBufferData) for object with ID : %u", m_backendBufferId); @@ -122,7 +121,6 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget); MGLOG_D("Syncing buffer sub-data (glBufferSubData) for object with ID : %u", m_backendBufferId); @@ -146,7 +144,6 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget); MGLOG_D("Syncing buffer map (glMapBuffer) for object with ID : %u", m_backendBufferId); MGLOG_D("Mapping buffer with ID: %u", m_backendBufferId); @@ -228,9 +225,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("Syncing VAO with backend ID %u to backend for state ID %u", m_backendVAOId, stateVAOObject->GetExternalIndex()); - BufferImpl::BackendBufferBindingProtector backendBufferBindingProtector(BufferImpl::TempBufferTarget); - BackendVertexArrayBindingProtector backendVAOBindingProtector; - Bind(); for (const auto& attribIndex : stateVAOObject->GetDirtyAttributeIndices()) { @@ -759,7 +753,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("Syncing FBO with backend ID %u to backend for state ID %u, as %s FBO", m_backendFBOId, stateFBOObject->GetExternalIndex(), (asTarget == FramebufferTarget::Draw ? "DRAW" : "READ")); GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget); - BackendFramebufferBindingProtector backendFBOBindingProtector(glFBOTarget); Bind(asTarget); // Handle all attachments diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index 65435cb8..8c528ee7 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -19,55 +19,11 @@ #include namespace MobileGL::MG_Backend::DirectGLES { - namespace BufferImpl { - BackendBufferBindingProtector::BackendBufferBindingProtector(GLenum target) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - m_target = target; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), &m_previousBinding); - } + namespace BufferImpl {} // namespace BufferImpl - BackendBufferBindingProtector::~BackendBufferBindingProtector() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glBindBuffer(m_target, m_previousBinding); - } - } // namespace BufferImpl - - namespace VertexArrayImpl { - BackendVertexArrayBindingProtector::BackendVertexArrayBindingProtector() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &m_previousBinding); - } - - BackendVertexArrayBindingProtector::~BackendVertexArrayBindingProtector() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glBindVertexArray(m_previousBinding); - } - } // namespace VertexArrayImpl + namespace VertexArrayImpl {} // namespace VertexArrayImpl namespace TextureImpl { - BackendTextureBindingProtector::BackendTextureBindingProtector(GLenum target) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - m_target = target; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, true), &m_previousBinding); - } - - BackendTextureBindingProtector::~BackendTextureBindingProtector() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glBindTexture(m_target, m_previousBinding); - } - void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType) { #ifdef TRACY_ENABLE @@ -81,43 +37,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } } // namespace TextureImpl - namespace FramebufferImpl { - BackendFramebufferBindingProtector::BackendFramebufferBindingProtector(GLenum target) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - m_target = target; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), &m_previousBinding); - } - - BackendFramebufferBindingProtector::~BackendFramebufferBindingProtector() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glBindFramebuffer(m_target, m_previousBinding); - } - - GLuint BackendFramebufferBindingProtector::GetTempFBO(FramebufferTarget target) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - GLenum glTarget = MG_Util::ConvertFramebufferTargetToGLEnum(target); - GLuint& fbo = (glTarget == GL_DRAW_FRAMEBUFFER) ? s_tempDrawFBO : s_tempReadFBO; - if (fbo == 0) { - MG_External::GLES::glGenFramebuffers(1, &fbo); - } - return fbo; - } - - void BackendFramebufferBindingProtector::BindTempFBO(MobileGL::FramebufferTarget target) { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - GLuint fbo = GetTempFBO(target); - GLenum glTarget = MG_Util::ConvertFramebufferTargetToGLEnum(target); - MG_External::GLES::glBindFramebuffer(glTarget, fbo); - } - } // namespace FramebufferImpl + namespace FramebufferImpl {} // namespace FramebufferImpl namespace PrgramImpl { String ProcessOutColorLocations(const String& glslCode) { diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.h b/MobileGL/MG_Backend/DirectGLES/Utils.h index 2f239dd7..21777e33 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.h +++ b/MobileGL/MG_Backend/DirectGLES/Utils.h @@ -27,66 +27,18 @@ namespace MobileGL::MG_Backend::DirectGLES { }; } // namespace DebugImpl - namespace BufferImpl { - class BackendBufferBindingProtector { - public: - BackendBufferBindingProtector(GLenum target); - - ~BackendBufferBindingProtector(); - - private: - GLenum m_target; - GLint m_previousBinding = 0; - }; - } // namespace BufferImpl + namespace BufferImpl {} // namespace BufferImpl namespace VertexArrayImpl { GLenum GetBindingQuery(GLenum target, bool isTexture); - - class BackendVertexArrayBindingProtector { - public: - BackendVertexArrayBindingProtector(); - - ~BackendVertexArrayBindingProtector(); - - private: - GLint m_previousBinding = 0; - }; } // namespace VertexArrayImpl namespace TextureImpl { - class BackendTextureBindingProtector { - public: - BackendTextureBindingProtector(GLenum target); - - ~BackendTextureBindingProtector(); - - private: - GLenum m_target; - GLint m_previousBinding = 0; - }; - void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType); } // namespace TextureImpl - namespace FramebufferImpl { - class BackendFramebufferBindingProtector { - public: - BackendFramebufferBindingProtector(GLenum target); - - ~BackendFramebufferBindingProtector(); - - static GLuint GetTempFBO(FramebufferTarget target); - static void BindTempFBO(FramebufferTarget target); - - private: - GLenum m_target; - GLint m_previousBinding = 0; - inline static GLuint s_tempReadFBO = 0; - inline static GLuint s_tempDrawFBO = 0; - }; - } // namespace FramebufferImpl + namespace FramebufferImpl {} // namespace FramebufferImpl namespace PrgramImpl { String ProcessOutColorLocations(const String& glslCode); From 0ae1b762620ca39ef694eb5a4775d980bd517de6 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:27:58 +0800 Subject: [PATCH 15/74] [Feat] (MG_State/VertexArrayState): Replace dirty mark with resource version for VA. --- .../VertexArrayState/VertexArrayObject.cpp | 49 ++++++++++--------- .../VertexArrayState/VertexArrayObject.h | 16 ++++-- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index a5b91cb3..507955bb 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -22,20 +22,20 @@ namespace MobileGL { attr.Offset = 0; attr.Buffer = nullptr; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } } void VertexArrayObject::EnableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; m_attributes[index].Enabled = true; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } void VertexArrayObject::DisableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; m_attributes[index].Enabled = false; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } Bool VertexArrayObject::IsAttributeEnabled(Uint index) const { @@ -59,13 +59,13 @@ namespace MobileGL { attr.Offset = offset; attr.IsInteger = isInteger; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr& buffer) { if (index >= MAX_VERTEX_ATTRIBS) return; m_attributes[index].Buffer = buffer; - MarkAttributeDirty(index); + BumpAttributeBufferVersion(index); } BindingSlot& VertexArrayObject::GetIndexBufferBindingSlot() { @@ -83,22 +83,6 @@ namespace MobileGL { return m_attributes; } - void VertexArrayObject::MarkAttributeDirty(Uint index) { - if (index >= MAX_VERTEX_ATTRIBS) return; - if (std::find(m_dirtyAttributes.begin(), m_dirtyAttributes.end(), index) != m_dirtyAttributes.end()) { - return; - } - m_dirtyAttributes.push_back(index); - } - - const Vector& VertexArrayObject::GetDirtyAttributeIndices() const { - return m_dirtyAttributes; - } - - void VertexArrayObject::ClearDirtyAttributes() { - m_dirtyAttributes.clear(); - } - Uint VertexArrayObject::GetExternalIndex() const { return m_externalIndex; } @@ -107,13 +91,34 @@ namespace MobileGL { if (index >= MAX_VERTEX_ATTRIBS) return; if (m_attributes[index].Divisor == divisor) return; m_attributes[index].Divisor = divisor; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } Uint VertexArrayObject::GetAttributeDivisor(Uint index) const { if (index >= MAX_VERTEX_ATTRIBS) return 0; return m_attributes[index].Divisor; } + + void VertexArrayObject::BumpAttributeFormatVersion(Uint index) { + if (index >= MAX_VERTEX_ATTRIBS) return; + ++m_attributeVersions[index].FormatVersion; + } + + void VertexArrayObject::BumpAttributeBufferVersion(Uint index) { + if (index >= MAX_VERTEX_ATTRIBS) return; + ++m_attributeVersions[index].BufferVersion; + } + + const VertexAttributeVersion& VertexArrayObject::GetAttributeVersion(Uint index) const { + static VertexAttributeVersion emptyVersion; + if (index >= MAX_VERTEX_ATTRIBS) return emptyVersion; + return m_attributeVersions[index]; + } + + const Array& VertexArrayObject:: + GetAllAttributeVersions() const { + return m_attributeVersions; + } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h index 854122c9..ccb8b5c1 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h @@ -9,6 +9,7 @@ #pragma once #include #include "../BufferState/BufferObject.h" +#include "MG_Util/Types.h" namespace MobileGL { namespace MG_State { @@ -25,6 +26,11 @@ namespace MobileGL { SharedPtr Buffer; }; + struct VertexAttributeVersion { + Uint16 FormatVersion = 0; + Uint16 BufferVersion = 0; + }; + class VertexArrayObject { public: static constexpr int MAX_VERTEX_ATTRIBS = 16; @@ -45,19 +51,21 @@ namespace MobileGL { const VertexAttribute& GetAttribute(Uint index) const; const Array& GetAllAttributes() const; - const Vector& GetDirtyAttributeIndices() const; - void ClearDirtyAttributes(); Uint GetExternalIndex() const; void SetAttributeDivisor(Uint index, Uint divisor); Uint GetAttributeDivisor(Uint index) const; + const VertexAttributeVersion& GetAttributeVersion(Uint index) const; + const Array& GetAllAttributeVersions() const; + private: - void MarkAttributeDirty(Uint index); + void BumpAttributeFormatVersion(Uint index); + void BumpAttributeBufferVersion(Uint index); const Uint m_externalIndex = 0; Array m_attributes; - Vector m_dirtyAttributes; + Array m_attributeVersions; // format version, buffer version BindingSlot m_indexBufferBindingSlot; }; } // namespace GLState From 5c63515259e83e7c5136f40ccae34ced8fe24252 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:29:30 +0800 Subject: [PATCH 16/74] [Feat|Perf|Fix] (MG_Backend/DirectGLES): Skip redundant sync for VA when not dirty. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 12 +-- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 75 ++++++++++++------- MobileGL/MG_Backend/DirectGLES/Managers.h | 7 +- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 820f04e9..fb5e555f 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -156,7 +156,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } // namespace BufferImpl namespace VertexArrayImpl { - void SyncCurrentVAO(Bool needDivisor) { + void SyncCurrentVAO() { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -174,7 +174,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendVAOObject = backendVAOIt->second; } - backendVAOObject->SyncToBackend(currentVAOObject, needDivisor); + backendVAOObject->SyncToBackend(currentVAOObject); } } // namespace VertexArrayImpl @@ -388,7 +388,7 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif BufferImpl::SyncNeccessaryBuffers(syncBit & DrawSyncBit::IndexBuffer, syncBit & DrawSyncBit::IndirectBuffer); - VertexArrayImpl::SyncCurrentVAO(syncBit & DrawSyncBit::Instancing); + VertexArrayImpl::SyncCurrentVAO(); TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); PrgramImpl::SyncCurrentProgram(); @@ -856,8 +856,6 @@ namespace MobileGL::MG_Backend::DirectGLES { if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); - - // Protector will automatically revert to previous fbo states return; } @@ -867,7 +865,6 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - // Protector will automatically revert to previous fbo states } } @@ -927,8 +924,6 @@ namespace MobileGL::MG_Backend::DirectGLES { }); if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); - - // Protector will automatically revert to previous fbo states return; } @@ -938,7 +933,6 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - // Protector will automatically revert to previous fbo states } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 04af6811..c9e5f8e8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -212,8 +212,38 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindVertexArray(m_backendVAOId); } - void BackendVertexArrayObject::SyncToBackend(SharedPtr& stateVAOObject, - Bool needDivisor) { + void BackendVertexArrayObject::SyncAttributeBuffer(Uint index, + const MG_State::GLState::VertexAttribute& attrib) { + const auto& bufferObject = attrib.Buffer; + if (!bufferObject) { + MGLOG_W("Attribute has no bound buffer, skipping."); + return; + } + + const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject); + if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { + MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute."); + return; + } + const auto& backendBufferObject = backendBufferIt->second; + + backendBufferObject->Bind(GL_ARRAY_BUFFER); + } + + void BackendVertexArrayObject::SyncAttributeFormat(Uint index, + const MG_State::GLState::VertexAttribute& attrib) { + if (attrib.Enabled) { + MGLOG_D("Binding attribute index %u for VAO ID: %u", index, m_backendVAOId); + MG_External::GLES::glEnableVertexAttribArray(index); + } else { + MGLOG_D("Disabling attribute index %u for VAO ID: %u", index, m_backendVAOId); + MG_External::GLES::glDisableVertexAttribArray(index); + } + + MG_External::GLES::glVertexAttribDivisor(index, attrib.Divisor); + } + + void BackendVertexArrayObject::SyncToBackend(SharedPtr& stateVAOObject) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -227,31 +257,24 @@ namespace MobileGL::MG_Backend::DirectGLES { Bind(); - for (const auto& attribIndex : stateVAOObject->GetDirtyAttributeIndices()) { - const auto& attrib = stateVAOObject->GetAttribute(attribIndex); - if (attrib.Enabled) { - MGLOG_D("Binding attribute index %u for VAO ID: %u", attribIndex, m_backendVAOId); - MG_External::GLES::glEnableVertexAttribArray(attribIndex); - } else { - MGLOG_D("Disabling attribute index %u for VAO ID: %u", attribIndex, m_backendVAOId); - MG_External::GLES::glDisableVertexAttribArray(attribIndex); - continue; + const auto& allAttributeVersions = stateVAOObject->GetAllAttributeVersions(); + const auto& allAttributes = stateVAOObject->GetAllAttributes(); + for (Uint attribIndex = 0; attribIndex < allAttributes.size(); ++attribIndex) { + Bool needsSyncFormat = allAttributeVersions[attribIndex].FormatVersion != + m_syncedAttributeVersions[attribIndex].FormatVersion; + Bool needsSyncBuffer = allAttributeVersions[attribIndex].BufferVersion != + m_syncedAttributeVersions[attribIndex].BufferVersion; + if (!needsSyncFormat && !needsSyncBuffer) continue; + + const auto& attrib = allAttributes[attribIndex]; + if (needsSyncBuffer) { + SyncAttributeBuffer(attribIndex, attrib); } - const auto& bufferObject = attrib.Buffer; - if (!bufferObject) { - MGLOG_W("Attribute has no bound buffer, skipping."); - continue; + if (needsSyncFormat) { + SyncAttributeFormat(attribIndex, attrib); } - const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject); - if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { - MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute."); - continue; - } - const auto& backendBufferObject = backendBufferIt->second; - - backendBufferObject->Bind(GL_ARRAY_BUFFER); if (!attrib.IsInteger) { MG_External::GLES::glVertexAttribPointer( attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), @@ -261,10 +284,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertDataTypeToGLEnum(attrib.Type), attrib.Stride, (const void*)attrib.Offset); } - - if (needDivisor) { - MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); - } } const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); @@ -278,7 +297,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - stateVAOObject->ClearDirtyAttributes(); + m_syncedAttributeVersions = allAttributeVersions; } UnorderedMap, SharedPtr> diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 196fd040..0a21aec8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -44,13 +44,18 @@ namespace MobileGL::MG_Backend::DirectGLES { class BackendVertexArrayObject { public: BackendVertexArrayObject(); - void SyncToBackend(SharedPtr& stateVAOObject, Bool needDivisor); + void SyncToBackend(SharedPtr& stateVAOObject); Uint GetBackendVertexArrayId() { return m_backendVAOId; } void Bind(); private: + void SyncAttributeFormat(Uint index, const MG_State::GLState::VertexAttribute& attrib); + void SyncAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib); + Uint m_backendVAOId = 0; Bool m_isInitialized = false; + Array + m_syncedAttributeVersions; }; extern UnorderedMap, SharedPtr> From c301591dcc506f358b9beb92fea23b546a7bb7c3 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:33:50 +0800 Subject: [PATCH 17/74] [Feat] (MG_State/VertexArrayState): Add SwitchVersion for VA. --- .../GLState/VertexArrayState/VertexArrayObject.cpp | 9 +++++++-- .../GLState/VertexArrayState/VertexArrayObject.h | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index 507955bb..e75c58b0 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -29,13 +29,13 @@ namespace MobileGL { void VertexArrayObject::EnableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; m_attributes[index].Enabled = true; - BumpAttributeFormatVersion(index); + BumpAttributeSwitchVersion(index); } void VertexArrayObject::DisableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; m_attributes[index].Enabled = false; - BumpAttributeFormatVersion(index); + BumpAttributeSwitchVersion(index); } Bool VertexArrayObject::IsAttributeEnabled(Uint index) const { @@ -109,6 +109,11 @@ namespace MobileGL { ++m_attributeVersions[index].BufferVersion; } + void VertexArrayObject::BumpAttributeSwitchVersion(Uint index) { + if (index >= MAX_VERTEX_ATTRIBS) return; + ++m_attributeVersions[index].SwitchVersion; + } + const VertexAttributeVersion& VertexArrayObject::GetAttributeVersion(Uint index) const { static VertexAttributeVersion emptyVersion; if (index >= MAX_VERTEX_ATTRIBS) return emptyVersion; diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h index ccb8b5c1..93f6aa47 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h @@ -29,6 +29,7 @@ namespace MobileGL { struct VertexAttributeVersion { Uint16 FormatVersion = 0; Uint16 BufferVersion = 0; + Uint16 SwitchVersion = 0; }; class VertexArrayObject { @@ -62,10 +63,11 @@ namespace MobileGL { private: void BumpAttributeFormatVersion(Uint index); void BumpAttributeBufferVersion(Uint index); + void BumpAttributeSwitchVersion(Uint index); const Uint m_externalIndex = 0; Array m_attributes; - Array m_attributeVersions; // format version, buffer version + Array m_attributeVersions; BindingSlot m_indexBufferBindingSlot; }; } // namespace GLState From 5e3cb989347ff0772bc346fa1943697509c4d18d Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:37:27 +0800 Subject: [PATCH 18/74] [Perf] (MG_Backend/DirectGLES): Use SwitchDirty in VA. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 27 +++++++++------------ MobileGL/MG_Backend/DirectGLES/Managers.h | 1 - 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index c9e5f8e8..5a440f8c 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -230,19 +230,6 @@ namespace MobileGL::MG_Backend::DirectGLES { backendBufferObject->Bind(GL_ARRAY_BUFFER); } - void BackendVertexArrayObject::SyncAttributeFormat(Uint index, - const MG_State::GLState::VertexAttribute& attrib) { - if (attrib.Enabled) { - MGLOG_D("Binding attribute index %u for VAO ID: %u", index, m_backendVAOId); - MG_External::GLES::glEnableVertexAttribArray(index); - } else { - MGLOG_D("Disabling attribute index %u for VAO ID: %u", index, m_backendVAOId); - MG_External::GLES::glDisableVertexAttribArray(index); - } - - MG_External::GLES::glVertexAttribDivisor(index, attrib.Divisor); - } - void BackendVertexArrayObject::SyncToBackend(SharedPtr& stateVAOObject) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -260,19 +247,29 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& allAttributeVersions = stateVAOObject->GetAllAttributeVersions(); const auto& allAttributes = stateVAOObject->GetAllAttributes(); for (Uint attribIndex = 0; attribIndex < allAttributes.size(); ++attribIndex) { + const auto& attrib = allAttributes[attribIndex]; + Bool needsSyncSwitch = allAttributeVersions[attribIndex].SwitchVersion != + m_syncedAttributeVersions[attribIndex].SwitchVersion; + if (needsSyncSwitch) { + if (attrib.Enabled) { + MG_External::GLES::glEnableVertexAttribArray(attribIndex); + } else { + MG_External::GLES::glDisableVertexAttribArray(attribIndex); + } + } + Bool needsSyncFormat = allAttributeVersions[attribIndex].FormatVersion != m_syncedAttributeVersions[attribIndex].FormatVersion; Bool needsSyncBuffer = allAttributeVersions[attribIndex].BufferVersion != m_syncedAttributeVersions[attribIndex].BufferVersion; if (!needsSyncFormat && !needsSyncBuffer) continue; - const auto& attrib = allAttributes[attribIndex]; if (needsSyncBuffer) { SyncAttributeBuffer(attribIndex, attrib); } if (needsSyncFormat) { - SyncAttributeFormat(attribIndex, attrib); + MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); } if (!attrib.IsInteger) { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 0a21aec8..f85381af 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -49,7 +49,6 @@ namespace MobileGL::MG_Backend::DirectGLES { void Bind(); private: - void SyncAttributeFormat(Uint index, const MG_State::GLState::VertexAttribute& attrib); void SyncAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib); Uint m_backendVAOId = 0; From 863aa77ff0f14235dbe7741e1ef7736123ef2b69 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:49:46 +0800 Subject: [PATCH 19/74] [Feat] (MG_Util): Versioning for BindingSlot. --- MobileGL/MG_Util/Types.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index 94085dc6..316fb174 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -145,17 +145,18 @@ namespace MobileGL { using TargetEnum = typename ObjectType::TargetEnum; BindingSlot() : m_target((TargetEnum)0), m_boundObject(nullptr) {} - explicit BindingSlot(TargetEnum target) : m_target(target), m_boundObject(nullptr) {} - - void Bind(SharedPtr object) { m_boundObject = object; } - + void Bind(SharedPtr object) { + m_boundObject = object; + ++m_version; + } SharedPtr GetBoundObject() const { return m_boundObject; } - TargetEnum GetTarget() const { return m_target; } + Uint16 GetVersion() const { return m_version; } private: TargetEnum m_target; + Uint16 m_version = 0; SharedPtr m_boundObject; }; From 8e897d54a2587733983f58ba9091720df1b25fd7 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 01:52:24 +0800 Subject: [PATCH 20/74] [Perf] (MG_Backend/DirectGLES): Skip IBO binding for VA when not dirty. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 20 ++++++++++++-------- MobileGL/MG_Backend/DirectGLES/Managers.h | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 5a440f8c..e12a6b69 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -283,15 +283,19 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); - if (indexBufferBinding) { - const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding); - if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) { - const auto& backendBufferObject = backendBufferIt->second; - backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER); - } else { - MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer."); + Uint16 currentIndexBufferVersion = stateVAOObject->GetIndexBufferBindingSlot().GetVersion(); + if (currentIndexBufferVersion != m_syncedIndexBufferVersion) { + const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); + if (indexBufferBinding) { + const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding); + if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) { + const auto& backendBufferObject = backendBufferIt->second; + backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER); + } else { + MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer."); + } } + m_syncedIndexBufferVersion = currentIndexBufferVersion; } m_syncedAttributeVersions = allAttributeVersions; diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index f85381af..955d64b6 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -53,6 +53,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint m_backendVAOId = 0; Bool m_isInitialized = false; + Uint16 m_syncedIndexBufferVersion = 0; Array m_syncedAttributeVersions; }; From 42ad1634bfb513fda9d27ec4f5876755d3d80965 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 02:17:09 +0800 Subject: [PATCH 21/74] [Perf] (MG_State|MG_Util): Do not bump resource version when nothing changes. --- .../GLState/SamplerState/SamplerObject.cpp | 20 +++++++++++++++++++ .../GLState/TextureState/TextureObject.cpp | 12 +++++++++++ .../VertexArrayState/VertexArrayObject.cpp | 15 ++++++++++++++ MobileGL/MG_Util/Types.h | 2 ++ 4 files changed, 49 insertions(+) diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp index 9721be49..ddf60478 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp @@ -14,36 +14,50 @@ namespace MobileGL { SamplerObject::SamplerObject(Uint externalIndex) : m_externalIndex(externalIndex) {} void SamplerObject::SetWrapS(SamplerWrapMode mode) { + if (mode == m_samplerParameters.wrapS) return; + m_samplerParameters.wrapS = mode; ++m_version; } void SamplerObject::SetWrapT(SamplerWrapMode mode) { + if (mode == m_samplerParameters.wrapT) return; + m_samplerParameters.wrapT = mode; ++m_version; } void SamplerObject::SetWrapR(SamplerWrapMode mode) { + if (mode == m_samplerParameters.wrapR) return; + m_samplerParameters.wrapR = mode; ++m_version; } void SamplerObject::SetMinFilter(SamplerFilterMode mode) { + if (mode == m_samplerParameters.minFilter) return; + m_samplerParameters.minFilter = mode; ++m_version; } void SamplerObject::SetMagFilter(SamplerFilterMode mode) { + if (mode == m_samplerParameters.magFilter) return; + m_samplerParameters.magFilter = mode; ++m_version; } void SamplerObject::SetMipmapMode(SamplerMipmapMode mode) { + if (mode == m_samplerParameters.mipmapMode) return; + m_samplerParameters.mipmapMode = mode; ++m_version; } void SamplerObject::SetLodRange(Float minLod, Float maxLod) { + if (minLod == m_samplerParameters.minLod && maxLod == m_samplerParameters.maxLod) return; + if (minLod > maxLod) { THROW_EXCEPTION("minLod cannot be greater than maxLod"); } @@ -53,16 +67,22 @@ namespace MobileGL { } void SamplerObject::SetLodBias(Float bias) { + if (bias == m_samplerParameters.lodBias) return; + m_samplerParameters.lodBias = bias; ++m_version; } void SamplerObject::SetSamplerCompareFunc(SamplerCompareFunc func) { + if (func == m_samplerParameters.compareFunc) return; + m_samplerParameters.compareFunc = func; ++m_version; } void SamplerObject::SetCompareMode(SamplerCompareMode mode) { + if (mode == m_samplerParameters.compareMode) return; + m_samplerParameters.compareMode = mode; ++m_version; } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index b28b2c89..d2e23714 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -44,6 +44,8 @@ namespace MobileGL { } void TextureObjectBase::SetInternalFormat(TextureInternalFormat format) { + if (format == m_internalFormat) return; + m_internalFormat = format; ++m_textureParamsVersion; } @@ -57,6 +59,8 @@ namespace MobileGL { } void TextureObjectBase::SetBorderColor(const FloatVec4& color) { + if (color == m_borderColor) return; + m_borderColor = color; ++m_textureParamsVersion; } @@ -83,6 +87,8 @@ namespace MobileGL { } void TextureObjectBase::SetSwizzleParam(TextureSwizzleParam param, TextureSwizzleParam value) { + if (GetSwizzleParam(param) == value) return; + switch (param) { case TextureSwizzleParam::Red: m_swizzleParams.r() = value; @@ -105,6 +111,8 @@ namespace MobileGL { } void TextureObjectBase::SetSwizzleParamRGBA(const Vec4& values) { + if (values == m_swizzleParams) return; + m_swizzleParams = values; ++m_textureParamsVersion; } @@ -114,11 +122,15 @@ namespace MobileGL { } void TextureObjectBase::SetBaseLevel(Uint baseLevel) { + if (baseLevel == m_levelRange.x()) return; + m_levelRange.x() = baseLevel; ++m_textureParamsVersion; } void TextureObjectBase::SetMaxLevel(Uint maxLevel) { + if (maxLevel == m_levelRange.y()) return; + m_levelRange.y() = maxLevel; ++m_textureParamsVersion; } diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index e75c58b0..0914222c 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -28,12 +28,18 @@ namespace MobileGL { void VertexArrayObject::EnableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; + + if (!m_attributes[index].Enabled) return; + m_attributes[index].Enabled = true; BumpAttributeSwitchVersion(index); } void VertexArrayObject::DisableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; + + if (!m_attributes[index].Enabled) return; + m_attributes[index].Enabled = false; BumpAttributeSwitchVersion(index); } @@ -47,6 +53,12 @@ namespace MobileGL { SizeT offset, Bool isInteger) { if (index >= MAX_VERTEX_ATTRIBS) return; + if (m_attributes[index].Size == size && m_attributes[index].Type == type && + m_attributes[index].Normalized == normalized && m_attributes[index].Stride == stride && + m_attributes[index].Offset == offset && m_attributes[index].IsInteger == isInteger) { + return; + } + if (size < 1 || size > 4) { return; } @@ -64,6 +76,9 @@ namespace MobileGL { void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr& buffer) { if (index >= MAX_VERTEX_ATTRIBS) return; + + if (m_attributes[index].Buffer == buffer) return; + m_attributes[index].Buffer = buffer; BumpAttributeBufferVersion(index); } diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index 316fb174..ec390275 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -147,6 +147,8 @@ namespace MobileGL { BindingSlot() : m_target((TargetEnum)0), m_boundObject(nullptr) {} explicit BindingSlot(TargetEnum target) : m_target(target), m_boundObject(nullptr) {} void Bind(SharedPtr object) { + if (m_boundObject == object) return; + m_boundObject = object; ++m_version; } From 2fab6c5b0bd9dabcf47c0ee1181607df125d7e94 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 02:20:48 +0800 Subject: [PATCH 22/74] [Fix] (MG_Backend/DirectGLES): Always bind VBO before glVertexAttribPointer. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 5 ++--- .../MG_State/GLState/VertexArrayState/VertexArrayObject.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index e12a6b69..c658fa18 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -264,9 +264,8 @@ namespace MobileGL::MG_Backend::DirectGLES { m_syncedAttributeVersions[attribIndex].BufferVersion; if (!needsSyncFormat && !needsSyncBuffer) continue; - if (needsSyncBuffer) { - SyncAttributeBuffer(attribIndex, attrib); - } + SyncAttributeBuffer(attribIndex, + attrib); // Always need to bind VBO before setting glVertexAttribPointer if (needsSyncFormat) { MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index 0914222c..d6e8c8c7 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -29,7 +29,7 @@ namespace MobileGL { void VertexArrayObject::EnableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; - if (!m_attributes[index].Enabled) return; + if (m_attributes[index].Enabled) return; m_attributes[index].Enabled = true; BumpAttributeSwitchVersion(index); From 1143c961b68ccd54ad362c2531603736f04106fc Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 02:52:34 +0800 Subject: [PATCH 23/74] [Perf] (MG_Backend/DirectGLES): Use glVertexAttribFormat to avoid redunant buffer binding. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 34 +++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index c658fa18..f1cfd87e 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -264,21 +264,31 @@ namespace MobileGL::MG_Backend::DirectGLES { m_syncedAttributeVersions[attribIndex].BufferVersion; if (!needsSyncFormat && !needsSyncBuffer) continue; - SyncAttributeBuffer(attribIndex, - attrib); // Always need to bind VBO before setting glVertexAttribPointer + if (needsSyncBuffer) { + SyncAttributeBuffer(attribIndex, attrib); + + if (!attrib.IsInteger) { + MG_External::GLES::glVertexAttribPointer( + attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), + attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)attrib.Offset); + } else { + MG_External::GLES::glVertexAttribIPointer(attribIndex, attrib.Size, + MG_Util::ConvertDataTypeToGLEnum(attrib.Type), + attrib.Stride, (const void*)attrib.Offset); + } + continue; // No need to set format again + } if (needsSyncFormat) { MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); - } - - if (!attrib.IsInteger) { - MG_External::GLES::glVertexAttribPointer( - attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), - attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)attrib.Offset); - } else { - MG_External::GLES::glVertexAttribIPointer(attribIndex, attrib.Size, - MG_Util::ConvertDataTypeToGLEnum(attrib.Type), - attrib.Stride, (const void*)attrib.Offset); + if (!attrib.IsInteger) { + MG_External::GLES::glVertexAttribFormat(attribIndex, attrib.Size, + MG_Util::ConvertDataTypeToGLEnum(attrib.Type), + attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Offset); + } else { + MG_External::GLES::glVertexAttribIFormat( + attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), attrib.Offset); + } } } From edff30778227072d69175b9dfda0d0c8c9c6eee3 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 10:17:41 +0800 Subject: [PATCH 24/74] [Chore] (MG_Impl/Framebuffer, MG_Impl/RenderState): move `ReadBuffer` to the right place --- MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp | 8 ++++++++ MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h | 1 + MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp | 8 -------- MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index d590ba4f..6419350a 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -304,6 +304,10 @@ namespace MobileGL { } } + void ReadBuffer_State(GLenum src) { + // TODO: implement + } + void DeleteRenderbuffers_State(GLsizei n, const GLuint* renderbuffers) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -575,6 +579,10 @@ namespace MobileGL { DrawBuffers_State(n, bufs); } + void ReadBuffer(GLenum src) { + ReadBuffer_State(src); + } + void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) { DeleteRenderbuffers_State(n, renderbuffers); } diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h index b06af10b..b332608c 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h @@ -46,6 +46,7 @@ namespace MobileGL { void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); void DrawBuffer(GLenum buf); void DrawBuffers(GLsizei n, const GLenum* bufs); + void ReadBuffer(GLenum src); void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers); GLenum CheckFramebufferStatus(GLenum target); diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp index 540fd0a6..57548582 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp @@ -241,10 +241,6 @@ namespace MobileGL { // TODO: implement } - void ReadBuffer_State(GLenum src) { - // TODO: implement - } - void ClearStencil_State(GLint s) { // TODO: implement } @@ -390,10 +386,6 @@ namespace MobileGL { BlendColor_State(red, green, blue, alpha); } - void ReadBuffer(GLenum src) { - ReadBuffer_State(src); - } - void ClearStencil(GLint s) { ClearStencil_State(s); } diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h index 9d35e01c..0dac02b3 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h @@ -45,7 +45,6 @@ namespace MobileGL { void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); void BlendEquation(GLenum mode); void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); - void ReadBuffer(GLenum src); void ClearStencil(GLint s); void ClearDepth(GLclampd depth); void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); From 207b74627344c82941f74e8b8ec2b774b0b65ab5 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 11:41:23 +0800 Subject: [PATCH 25/74] [Perf] (MG_Backend/DirectGLES): Avoid redunant VBO binding. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 17 +++++++++++++++++ MobileGL/MG_Backend/DirectGLES/Managers.h | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index f1cfd87e..07e3bb86 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -174,10 +174,20 @@ namespace MobileGL::MG_Backend::DirectGLES { } } + SharedPtr BackendBufferObject::GetSharedPtr() { + return shared_from_this(); + } + void BackendBufferObject::Bind() { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + if (TempBufferTarget == GL_ARRAY_BUFFER) { + if (g_boundVertexBufferObject.get() == this) { + return; + } + g_boundVertexBufferObject = GetSharedPtr(); + } MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); } @@ -185,10 +195,17 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + if (target == GL_ARRAY_BUFFER) { + if (g_boundVertexBufferObject.get() == this) { + return; + } + g_boundVertexBufferObject = GetSharedPtr(); + } MG_External::GLES::glBindBuffer(target, m_backendBufferId); } UnorderedMap, SharedPtr> g_backendBufferObjects; + SharedPtr g_boundVertexBufferObject; } // namespace BufferImpl namespace VertexArrayImpl { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 955d64b6..bb7b25d3 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -17,13 +17,14 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { - class BackendBufferObject { + class BackendBufferObject : public std::enable_shared_from_this { public: BackendBufferObject(); void SyncToBackend(SharedPtr& stateBufferObject); Uint GetBackendBufferId() { return m_backendBufferId; } void Bind(); void Bind(GLenum target); + SharedPtr GetSharedPtr(); private: void SyncToBackend_glBufferData(SharedPtr& stateBufferObject); @@ -36,6 +37,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Bool m_isInitialized = false; }; + extern SharedPtr g_boundVertexBufferObject; extern UnorderedMap, SharedPtr> g_backendBufferObjects; } // namespace BufferImpl From 6823b1e46aed984ef9e8a5bfe953768f45330d96 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 12:59:11 +0800 Subject: [PATCH 26/74] [Feat] (MG_Impl/Framebuffer): implement `glReadBuffer` --- .../GLImpl/Framebuffer/GL_Framebuffer.cpp | 19 +++++++- .../FramebufferState/FramebufferObject.cpp | 45 +++++++++---------- .../FramebufferState/FramebufferObject.h | 32 ++++++------- 3 files changed, 55 insertions(+), 41 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 6419350a..f0746053 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -304,8 +304,23 @@ namespace MobileGL { } } - void ReadBuffer_State(GLenum src) { - // TODO: implement + void ReadBuffer_State(GLenum mode) { + auto attType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(mode); + + // ------------------- Check validity begin ------------------------ + if (attType == FramebufferAttachmentType::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", __func__, + std::format("`mode` = {} is not an accepted value.", + MG_Util::ConvertGLEnumToString(mode)))); + return; + } + + // Get bound framebuffer + auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read); + auto fbo = bindingSlot.GetBoundObject(); + fbo->SetReadBuffer(attType); } void DeleteRenderbuffers_State(GLsizei n, const GLuint* renderbuffers) { diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 899e2676..3298e640 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -12,41 +12,41 @@ namespace MobileGL { namespace MG_State { namespace GLState { - // FramebufferAttachment - FramebufferAttachment::FramebufferAttachment(SharedPtr texture, - Int level) + // FramebufferAttachmentObject + FramebufferAttachmentObject::FramebufferAttachmentObject(SharedPtr texture, + Int level) : m_texture(texture), m_textureLevel(level) {} - FramebufferAttachment::FramebufferAttachment(SharedPtr renderbuffer) + FramebufferAttachmentObject::FramebufferAttachmentObject(SharedPtr renderbuffer) : m_renderbuffer(renderbuffer) {} - FramebufferAttachment::FramebufferAttachment(Bool IsValid) : m_texture(nullptr), m_renderbuffer(nullptr) { + FramebufferAttachmentObject::FramebufferAttachmentObject(Bool IsValid) : m_texture(nullptr), m_renderbuffer(nullptr) { m_isValid = IsValid; } - Bool FramebufferAttachment::IsTexture() const { + Bool FramebufferAttachmentObject::IsTexture() const { return m_texture != nullptr; } - Bool FramebufferAttachment::IsRenderbuffer() const { + Bool FramebufferAttachmentObject::IsRenderbuffer() const { return m_renderbuffer != nullptr; } - Bool FramebufferAttachment::IsEmpty() const { + Bool FramebufferAttachmentObject::IsEmpty() const { return m_texture == nullptr && m_renderbuffer == nullptr; } - SharedPtr FramebufferAttachment::GetTexture() const { + SharedPtr FramebufferAttachmentObject::GetTexture() const { return m_texture; } - SharedPtr FramebufferAttachment::GetRenderbuffer() const { + SharedPtr FramebufferAttachmentObject::GetRenderbuffer() const { return m_renderbuffer; } - Int FramebufferAttachment::GetTextureLevel() const { + Int FramebufferAttachmentObject::GetTextureLevel() const { return m_textureLevel; } - Bool FramebufferAttachment::IsComplete() const { + Bool FramebufferAttachmentObject::IsComplete() const { if (IsTexture()) { Bool complete = m_texture->IsComplete(); return complete; @@ -58,7 +58,7 @@ namespace MobileGL { return false; } - IntVec3 FramebufferAttachment::GetSize() const { + IntVec3 FramebufferAttachmentObject::GetSize() const { if (IsTexture()) { // TODO: get correct upload target MOBILEGL_ASSERT(nullptr != dynamic_cast(m_texture.get()), @@ -71,41 +71,39 @@ namespace MobileGL { return {0, 0, 0}; } - Bool FramebufferAttachment::IsValid() const { + Bool FramebufferAttachmentObject::IsValid() const { return m_isValid; } // FramebufferObject FramebufferObject::FramebufferObject(Uint externalIndex) : m_externalIndex(externalIndex) { - m_attachments.fill(FramebufferAttachment(false)); + m_attachments.fill(FramebufferAttachmentObject(false)); m_drawBuffers.fill(FramebufferAttachmentType::None); m_drawBuffers[0] = FramebufferAttachmentType::Color0; } void FramebufferObject::AttachTexture(FramebufferAttachmentType type, SharedPtr texture, int level) { - m_attachments[static_cast(type)] = FramebufferAttachment(std::move(texture), level); + m_attachments[static_cast(type)] = FramebufferAttachmentObject(std::move(texture), level); m_drawBuffersDirty = true; } void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type, std::shared_ptr renderbuffer) { - m_attachments[static_cast(type)] = FramebufferAttachment(renderbuffer); + m_attachments[static_cast(type)] = FramebufferAttachmentObject(renderbuffer); m_drawBuffersDirty = true; } void FramebufferObject::Detach(FramebufferAttachmentType type) { - m_attachments[static_cast(type)] = FramebufferAttachment(false); + m_attachments[static_cast(type)] = FramebufferAttachmentObject(false); m_drawBuffersDirty = true; } - const FramebufferAttachment& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { + const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { return m_attachments[static_cast(type)]; } - const Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>& - FramebufferObject::GetAllAttachments() const { + const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachments() const { return m_attachments; } @@ -155,8 +153,7 @@ namespace MobileGL { // // } - const Array& FramebufferObject:: - GetDrawBuffers() const { + const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const { return m_drawBuffers; } diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index f8356cad..55e8b087 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -69,11 +69,11 @@ namespace MobileGL { namespace MG_State { namespace GLState { - class FramebufferAttachment { + class FramebufferAttachmentObject { public: - explicit FramebufferAttachment(SharedPtr texture, Int level = 0); - explicit FramebufferAttachment(SharedPtr renderbuffer); - explicit FramebufferAttachment(Bool IsValid = true); + explicit FramebufferAttachmentObject(SharedPtr texture, Int level = 0); + explicit FramebufferAttachmentObject(SharedPtr renderbuffer); + explicit FramebufferAttachmentObject(Bool IsValid = true); Bool IsTexture() const; Bool IsRenderbuffer() const; @@ -94,36 +94,38 @@ namespace MobileGL { class FramebufferObject { public: - using TargetEnum = FramebufferTarget; static constexpr Uint MAX_DRAW_BUFFERS = 8; + using TargetEnum = FramebufferTarget; + using FramebufferAttachmentObjectArray = + Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; + using FramebufferAttachmentArray = Array; + FramebufferObject(Uint externalIndex); void AttachTexture(FramebufferAttachmentType type, SharedPtr texture, int level = 0); void AttachRenderbuffer(FramebufferAttachmentType type, std::shared_ptr renderbuffer); void Detach(FramebufferAttachmentType type); - const FramebufferAttachment& GetAttachment(FramebufferAttachmentType type) const; - const Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>& - GetAllAttachments() const; + const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const; + const FramebufferAttachmentObjectArray& GetAllAttachments() const; Bool CheckCompleteness() const; // aka. `buffer` as in glDrawBuffers/glReadBuffers void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer); bool DrawBuffersIsDirty() const { return m_drawBuffersDirty; } void ClearDrawBuffersDirtyState() { m_drawBuffersDirty = false; } - const Array& GetDrawBuffers() const; + const FramebufferAttachmentArray& GetDrawBuffers() const; + void SetReadBuffer(FramebufferAttachmentType buf) { m_readBuffer = buf; } FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; } Uint GetExternalIndex() const; private: const Uint m_externalIndex = 0; - Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)> - m_attachments; - Bool m_drawBuffersDirty = false; - Array m_drawBuffers; + FramebufferAttachmentObjectArray m_attachments; + FramebufferAttachmentArray m_drawBuffers; FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; + + Bool m_drawBuffersDirty = false; }; } // namespace GLState From 751c5745b0001a12f9adcb2f4d208c2ceaebbe91 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 13:06:55 +0800 Subject: [PATCH 27/74] [Fix] (MG_Util/Converters): properly handle `GL_NONE` in `ConvertGLEnumToFramebufferAttachmentType` --- .../GLToMG/FramebufferEnumConverter.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp index 5e17b568..2336dd12 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp @@ -30,13 +30,14 @@ namespace MobileGL { } switch (attachment) { - case GL_DEPTH_ATTACHMENT: - return FramebufferAttachmentType::Depth; - case GL_STENCIL_ATTACHMENT: - return FramebufferAttachmentType::Stencil; - case GL_UNKNOWN_MGL: - default: - return FramebufferAttachmentType::Unknown; + case GL_NONE: + return FramebufferAttachmentType::None; + case GL_DEPTH_ATTACHMENT: + return FramebufferAttachmentType::Depth; + case GL_STENCIL_ATTACHMENT: + return FramebufferAttachmentType::Stencil; + default: + return FramebufferAttachmentType::Unknown; } } From b966ef8b5e4c34986147fc4e979a224c0a75ccbc Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 16:36:40 +0800 Subject: [PATCH 28/74] [Chore] (CMake): DO not link static lib for Android. --- CMakeLists.txt | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f08dce4d..b587c7e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,33 +269,35 @@ target_link_libraries(${CMAKE_PROJECT_NAME} ${MOBILEGL_LINK_LIBRARIES} ) -add_library(${CMAKE_PROJECT_NAME}_s STATIC - ${SOURCE_FILES} -) - -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set_target_properties(${CMAKE_PROJECT_NAME}_s PROPERTIES - C_VISIBILITY_PRESET default - CXX_VISIBILITY_PRESET default - VISIBILITY_INLINES_HIDDEN OFF +if(NOT ANDROID) + add_library(${CMAKE_PROJECT_NAME}_s STATIC + ${SOURCE_FILES} ) -else() - set_target_properties(${CMAKE_PROJECT_NAME}_s PROPERTIES - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN ON + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set_target_properties(${CMAKE_PROJECT_NAME}_s PROPERTIES + C_VISIBILITY_PRESET default + CXX_VISIBILITY_PRESET default + VISIBILITY_INLINES_HIDDEN OFF + ) + else() + set_target_properties(${CMAKE_PROJECT_NAME}_s PROPERTIES + C_VISIBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + ) + endif() + + target_include_directories(${CMAKE_PROJECT_NAME}_s PUBLIC + ${MOBILEGL_INCLUDE_DIR} + ) + + target_link_libraries(${CMAKE_PROJECT_NAME}_s + PRIVATE + ${MOBILEGL_LINK_LIBRARIES} ) endif() -target_include_directories(${CMAKE_PROJECT_NAME}_s PUBLIC - ${MOBILEGL_INCLUDE_DIR} -) - -target_link_libraries(${CMAKE_PROJECT_NAME}_s - PRIVATE - ${MOBILEGL_LINK_LIBRARIES} -) - if (TRACY_ENABLE) target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Tracy::TracyClient) target_link_libraries(${CMAKE_PROJECT_NAME}_s PUBLIC Tracy::TracyClient) @@ -308,9 +310,6 @@ if (ANDROID) android log ) - target_link_libraries(${CMAKE_PROJECT_NAME}_s PUBLIC - android - log) endif() if (MOBILEGL_BUILD_TEST) From 5eff8b2e30fe274475ff2c942e137e9617149e16 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 16:44:00 +0800 Subject: [PATCH 29/74] [Perf] (MG_Backend/DirectGLES): Add cache for Texture and Sampler binding. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 38 +++++++--------- MobileGL/MG_Backend/DirectGLES/DirectGLES.h | 2 + MobileGL/MG_Backend/DirectGLES/Managers.cpp | 43 ++++++++++++++++++- MobileGL/MG_Backend/DirectGLES/Managers.h | 13 +++++- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index fb5e555f..bb90bdc4 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "DirectGLES.h" +#include "MG_State/GLState/SamplerState/SamplerObject.h" #include "Utils.h" #include "Managers.h" #include @@ -419,8 +420,6 @@ namespace MobileGL::MG_Backend::DirectGLES { for (Int unit = 0; unit < maxTextureUnits; ++unit) { auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); - MG_External::GLES::glActiveTexture(GL_TEXTURE0 + unit); - for (const auto& bindingSlot : textureUnit.GetAllBindingSlots()) { const auto& textureObject = bindingSlot.GetBoundObject(); if (!textureObject) continue; @@ -436,18 +435,18 @@ namespace MobileGL::MG_Backend::DirectGLES { if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue; GLenum targetGL = MG_Util::ConvertTextureTargetToGLEnum(target); - backendTextureIt->second->Bind(targetGL); + backendTextureIt->second->Bind(targetGL, unit); } - // Bind sampler object + // Bind sampler object if necessary const auto& samplerObject = textureUnit.GetSamplerObject(); if (samplerObject) { const auto& backendSamplerIt = SamplerImpl::g_backendSamplerObjects.find(samplerObject); if (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end()) { backendSamplerIt->second->Bind(unit); } + } else { - MG_External::GLES::glBindSampler(unit, 0); } } } @@ -551,7 +550,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } backendSamplerObject->SyncToBackend(samplerObject); } else { - MG_External::GLES::glBindSampler(unit, 0); + SamplerImpl::UnbindSampler(unit); } } } @@ -751,14 +750,13 @@ namespace MobileGL::MG_Backend::DirectGLES { }); } - bool UpdateTextureBindingAtTarget(GLenum target) { + Bool UpdateTextureBindingAtTarget(GLenum target) { #ifdef TRACY_ENABLE ZoneScopedNC(__func__, TRACY_ZONECOLOR_BACKEND); #endif auto unit = MG_State::pGLContext->GetActiveTextureUnit(); auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); - MG_External::GLES::glActiveTexture(GL_TEXTURE0 + unit); auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); if (!TextureImpl::IsSupportedTextureTarget(textureTarget)) { MOBILEGL_ASSERT(false, " Texture target %s is not supported, skipping.", @@ -782,7 +780,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendTextureObject = backendTextureIt->second; } - backendTextureObject->Bind(target); + backendTextureObject->Bind(target, unit); } return true; } @@ -809,12 +807,6 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!UpdateTextureBindingAtTarget(target)) return; - // GLint realInternalFormat; - // MG_External::GLES::glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, - // &realInternalFormat); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - // MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - // }); - // internalformat = (GLenum)realInternalFormat; auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); GLenum format = GL_DEPTH_COMPONENT; @@ -827,9 +819,9 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str()); TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); - bool isDepthFormat = + Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)); - bool isStencilFormat = + Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)); if (!isDepthFormat) { @@ -902,8 +894,8 @@ namespace MobileGL::MG_Backend::DirectGLES { }); auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat); - bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(mglInternalFormat); - bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(mglInternalFormat); + Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(mglInternalFormat); + Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(mglInternalFormat); if (!isDepthFormat) { MG_External::GLES::glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); @@ -946,7 +938,7 @@ namespace MobileGL::MG_Backend::DirectGLES { auto texture = slot.GetBoundObject(); auto backendTexture = TextureImpl::SyncTextureObjectToBackend(texture); - backendTexture->Bind(target); + backendTexture->Bind(target, unitIndex); MG_External::GLES::glGenerateMipmap(target); } @@ -995,7 +987,7 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - bool found = false; + Bool found = false; for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { realDrawbuffer = i; @@ -1048,7 +1040,7 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - bool found = false; + Bool found = false; for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { realDrawbuffer = i; @@ -1102,7 +1094,7 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - bool found = false; + Bool found = false; for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { realDrawbuffer = i; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index f57f56ab..f6ad4149 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -8,6 +8,8 @@ #pragma once #include +#include +#include #define CallAndCheck(operation) \ MGLOG_D("Call GLES func: %s", #operation); \ diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 07e3bb86..e71d2342 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -7,6 +7,10 @@ // End of Source File Header #include "Managers.h" +#include "MG_State/GLState/TextureState/TextureEnum.h" +#include "MG_State/GLState/TextureState/TextureObject.h" +#include "MG_State/GLState/TextureState/TextureState.h" +#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h" #include "Utils.h" #include "DirectGLES.h" @@ -345,11 +349,16 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - void BackendTextureObject::Bind(GLenum target) { + void BackendTextureObject::Bind(GLenum target, Uint unit) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); + if (this == g_boundTexturesCache[unit][targetN]) return; + + ActivateTextureUnit(unit); MG_External::GLES::glBindTexture(target, m_backendTextureId); + g_boundTexturesCache[unit][targetN] = this; } Uint BackendTextureObject::GetBackendTextureId() { @@ -759,6 +768,27 @@ namespace MobileGL::MG_Backend::DirectGLES { } } + void ActivateTextureUnit(Uint unit) { + if (unit == g_activeTextureUnit) { + return; + } + MG_External::GLES::glActiveTexture(GL_TEXTURE0 + unit); + g_activeTextureUnit = unit; + } + + void UnbindTexture(Uint unit, GLenum target) { // Active unit will be modified + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); + if (g_boundTexturesCache[unit][targetN] == nullptr) return; + + ActivateTextureUnit(unit); + MG_External::GLES::glBindTexture(target, 0); + g_boundTexturesCache[unit][targetN] = nullptr; + } + + Uint g_activeTextureUnit = 0; + Array, + MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> + g_boundTexturesCache; UnorderedMap, SharedPtr> g_backendTextureObjects; } // namespace TextureImpl @@ -1194,7 +1224,10 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + if (g_boundSamplersCache[unit] == this) return; + MG_External::GLES::glBindSampler(static_cast(unit), m_backendSamplerId); + g_boundSamplersCache[unit] = this; } Uint BackendSamplerObject::GetBackendSamplerId() { @@ -1204,6 +1237,14 @@ namespace MobileGL::MG_Backend::DirectGLES { return m_backendSamplerId; } + void UnbindSampler(Uint unit) { + if (g_boundSamplersCache[unit] == nullptr) return; + + MG_External::GLES::glBindSampler(static_cast(unit), 0); + g_boundSamplersCache[unit] = nullptr; + } + + Array g_boundSamplersCache; UnorderedMap, SharedPtr> g_backendSamplerObjects; } // namespace SamplerImpl diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index bb7b25d3..709f6f49 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -90,13 +90,14 @@ namespace MobileGL::MG_Backend::DirectGLES { bool operator!=(const StateTextureBasicInfo& other) const { return !(*this == other); } }; + inline const Uint TempTextureUnit = 0; class BackendTextureObject { public: BackendTextureObject(); void SyncMipmapsToBackend(SharedPtr& stateTextureObject); void SyncBuiltinSamplerToBackend(SharedPtr& stateTextureObject); void SyncTextureParamsToBackend(SharedPtr& stateTextureObject); - void Bind(GLenum target); + void Bind(GLenum target, Uint unit = TempTextureUnit); Uint GetBackendTextureId(); private: @@ -112,8 +113,14 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint16 m_syncedTextureParamsVersion = 0; }; + void ActivateTextureUnit(Uint unit); + void UnbindTexture(Uint unit, GLenum target); extern UnorderedMap, SharedPtr> g_backendTextureObjects; + extern Array, + MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> + g_boundTexturesCache; + extern Uint g_activeTextureUnit; } // namespace TextureImpl namespace FramebufferImpl { @@ -193,6 +200,10 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint16 m_syncedSamplerVersion = 0; }; + void UnbindSampler(Uint unit); + + extern Array + g_boundSamplersCache; extern UnorderedMap, SharedPtr> g_backendSamplerObjects; } // namespace SamplerImpl From 202922613b10106f6c38ffaacb183531ad4123a0 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 16:50:20 +0800 Subject: [PATCH 30/74] [Refactor] (MG_State/Framebuffer, MG_Backend/DirectGLES): Versioned framebuffer sync --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 2 +- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 240 ++++++++++-------- MobileGL/MG_Backend/DirectGLES/Managers.h | 7 + .../FramebufferState/FramebufferObject.cpp | 43 ++-- .../FramebufferState/FramebufferObject.h | 29 ++- 5 files changed, 187 insertions(+), 134 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index fb5e555f..c09b901d 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -220,7 +220,7 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); if (currentFBO) { - for (const auto& attachment : currentFBO->GetAllAttachments()) { + for (const auto& attachment : currentFBO->GetAllAttachmentObjects()) { if (!attachment.IsTexture()) continue; auto textureObject = attachment.GetTexture(); if (textureObject) { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 07e3bb86..cfdabdcc 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -787,6 +787,42 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId); } + Bool BackendFramebufferObject::SyncAttachmentObject( + GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, + GLenum glBackendAttachment) { + if (attachmentObject.IsTexture()) { + const auto& textureObject = attachmentObject.GetTexture(); + const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("ReadBuffer: No backend texture found for FBO attachment, cannot bind texture."); + return false; + } + const auto& backendTextureObject = backendTextureIt->second; + auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); + backendTextureObject->Bind(glTextureTarget); + MG_External::GLES::glFramebufferTexture2D(glFBOTarget, glBackendAttachment, glTextureTarget, + backendTextureObject->GetBackendTextureId(), + static_cast(attachmentObject.GetTextureLevel())); + } else if (attachmentObject.IsRenderbuffer()) { + const auto& renderbufferObject = attachmentObject.GetRenderbuffer(); + const auto& backendRenderbufferIt = + RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); + SharedPtr backendRenderbufferObject; + if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) { + backendRenderbufferObject = MakeShared(); + RenderbufferImpl::g_backendRenderbufferObjects[renderbufferObject] = backendRenderbufferObject; + } else { + backendRenderbufferObject = backendRenderbufferIt->second; + } + + backendRenderbufferObject->SyncToBackend(renderbufferObject); + backendRenderbufferObject->Bind(); + MG_External::GLES::glFramebufferRenderbuffer(glFBOTarget, glBackendAttachment, GL_RENDERBUFFER, + backendRenderbufferObject->GetBackendRenderbufferId()); + } + return true; + } + void BackendFramebufferObject::SyncToBackend(SharedPtr& stateFBOObject, FramebufferTarget asTarget) { #ifdef TRACY_ENABLE @@ -801,117 +837,117 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget); Bind(asTarget); - // Handle all attachments - const auto& attachments = stateFBOObject->GetAllAttachments(); - for (SizeT i = 0; i < attachments.size(); ++i) { - const auto& attachment = attachments[i]; - if (!attachment.IsValid() || attachment.IsEmpty()) { + FramebufferAttachmentType* frontendAttachmentToSync = nullptr; + GLenum* backendAttachmentToSync = nullptr; + Int frontendAttachmentToSyncCount = 0; + switch (asTarget) { + case FramebufferTarget::Draw: { + auto &stateDrawBuffers = stateFBOObject->GetDrawBuffers(); + + // Check if still clean, skip if clean + if (memcmp(m_frontendDrawBuffers, + stateDrawBuffers.data(), + FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { + break; + } + + // Create mappings for draw buffers + int nBuffers = 0; + std::fill(m_frontendDrawBuffers, + m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + FramebufferAttachmentType::None); + std::fill(m_compactedFrontendDrawBuffers, + m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + FramebufferAttachmentType::None); + std::fill(m_backendDrawBuffers, + m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); + for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { + m_frontendDrawBuffers[i] = FramebufferAttachmentType::None; + continue; + } + + m_frontendDrawBuffers[i] = stateDrawBuffers[i]; + + // Create compacted mapping + m_backendDrawBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; + m_compactedFrontendDrawBuffers[nBuffers] = m_frontendDrawBuffers[i]; + nBuffers++; + } + MG_External::GLES::glDrawBuffers(nBuffers, m_backendDrawBuffers); + + frontendAttachmentToSync = m_compactedFrontendDrawBuffers; + backendAttachmentToSync = m_backendDrawBuffers; + frontendAttachmentToSyncCount = nBuffers; + + break; + } + case FramebufferTarget::Read: { + auto frontendReadBuf = stateFBOObject->GetReadBuffer(); + if (frontendReadBuf == m_frontendReadBuffer) + break; + m_frontendReadBuffer = frontendReadBuf; + + // For consistency, we need to find the compacted attachment index of this read buffer + auto it = std::find( + m_compactedFrontendDrawBuffers, + m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + frontendReadBuf); + + Bool notFound = (it == m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); + if (notFound) { + MGLOG_D("%s: read buffer not found in draw buffer, use as in frontend", __func__); + } + auto backendReadBuffer = notFound ? frontendReadBuf : *it; + GLenum glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(backendReadBuffer); + if (m_backendReadBuffer == glBackendReadBuffer) + break; + m_backendReadBuffer = glBackendReadBuffer; + MG_External::GLES::glReadBuffer(glBackendReadBuffer); + + frontendAttachmentToSync = &m_frontendReadBuffer; + backendAttachmentToSync = &m_backendReadBuffer; + frontendAttachmentToSyncCount = 1; + + break; + } + default: + MOBILEGL_ASSERT(false, "%s: Unreachable!", __func__); + return; + } + + // Sync texture/buffer to attachment + const auto& attachmentObjects = stateFBOObject->GetAllAttachmentObjects(); + auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); + for (Int i = 0; i < frontendAttachmentToSyncCount; ++i) { + auto frontendAttachment = frontendAttachmentToSync[i]; + if (attachmentVersions[(SizeT)frontendAttachment] == m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { continue; } - FramebufferAttachmentType type = static_cast(i); - GLenum glAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(type); - if (attachment.IsTexture()) { - const auto& textureObject = attachment.GetTexture(); - const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); - if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { - MGLOG_E("No backend texture found for FBO attachment, cannot bind texture."); - continue; - } - const auto& backendTextureObject = backendTextureIt->second; - auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); - backendTextureObject->Bind(glTextureTarget); - MG_External::GLES::glFramebufferTexture2D(glFBOTarget, glAttachment, glTextureTarget, - backendTextureObject->GetBackendTextureId(), - static_cast(attachment.GetTextureLevel())); - } else if (attachment.IsRenderbuffer()) { - const auto& renderbufferObject = attachment.GetRenderbuffer(); - const auto& backendRenderbufferIt = - RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); - SharedPtr backendRenderbufferObject; - if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) { - backendRenderbufferObject = MakeShared(); - RenderbufferImpl::g_backendRenderbufferObjects[renderbufferObject] = backendRenderbufferObject; - } else { - backendRenderbufferObject = backendRenderbufferIt->second; - } + m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; - backendRenderbufferObject->SyncToBackend(renderbufferObject); - backendRenderbufferObject->Bind(); - MG_External::GLES::glFramebufferRenderbuffer(glFBOTarget, glAttachment, GL_RENDERBUFFER, - backendRenderbufferObject->GetBackendRenderbufferId()); + const auto& attachmentObject = stateFBOObject->GetAttachment(frontendAttachment); + if (!attachmentObject.IsValid() || attachmentObject.IsEmpty()) { + continue; } + auto glBackendAttachment = backendAttachmentToSync[i]; + SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); } - // Handle draw buffers for DRAW_FRAMEBUFFER - if (asTarget == FramebufferTarget::Draw) { - // Create mappings for draw buffers - int nBuffers = 0; - std::fill(m_frontendDrawBuffers, - m_frontendDrawBuffers + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_backendDrawBuffers, - m_backendDrawBuffers + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); - auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); - for (GLint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) { - if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { - m_frontendDrawBuffers[i] = FramebufferAttachmentType::None; - continue; - } - m_frontendDrawBuffers[i] = stateDrawBuffers[i]; - - // Create compacted mapping - m_backendDrawBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; - m_compactedFrontendDrawBuffers[nBuffers] = m_frontendDrawBuffers[i]; - nBuffers++; + FramebufferAttachmentType auxAtt[] = { FramebufferAttachmentType::Depth, FramebufferAttachmentType::Stencil }; + for (auto & att : auxAtt) { + auto frontendAttachment = att; + if (attachmentVersions[(SizeT)frontendAttachment] == m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { + continue; } + m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; - MG_External::GLES::glDrawBuffers(nBuffers, m_backendDrawBuffers); - stateFBOObject->ClearDrawBuffersDirtyState(); - } - // Handle read buffer for READ_FRAMEBUFFER - else if (asTarget == FramebufferTarget::Read) { - m_frontendReadBuffer = stateFBOObject->GetReadBuffer(); - GLenum frontendAtt = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(m_frontendReadBuffer); - GLenum backendAtt = GL_NONE; - const auto& readAttachment = attachments[(SizeT)m_frontendReadBuffer]; - GLenum glAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(m_frontendReadBuffer); - if (!readAttachment.IsValid() || readAttachment.IsEmpty()) { - return; + const auto& attachmentObject = stateFBOObject->GetAttachment(frontendAttachment); + if (!attachmentObject.IsValid() || attachmentObject.IsEmpty()) { + continue; } - if (readAttachment.IsTexture()) { - const auto& textureObject = readAttachment.GetTexture(); - const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); - if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { - MGLOG_E("ReadBuffer: No backend texture found for FBO attachment, cannot bind texture."); - return; - } - const auto& backendTextureObject = backendTextureIt->second; - auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); - backendTextureObject->Bind(glTextureTarget); - MG_External::GLES::glFramebufferTexture2D(glFBOTarget, glAttachment, glTextureTarget, - backendTextureObject->GetBackendTextureId(), - static_cast(readAttachment.GetTextureLevel())); - } else if (readAttachment.IsRenderbuffer()) { - const auto& renderbufferObject = readAttachment.GetRenderbuffer(); - const auto& backendRenderbufferIt = - RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); - SharedPtr backendRenderbufferObject; - if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) { - backendRenderbufferObject = MakeShared(); - RenderbufferImpl::g_backendRenderbufferObjects[renderbufferObject] = backendRenderbufferObject; - } else { - backendRenderbufferObject = backendRenderbufferIt->second; - } - - backendRenderbufferObject->SyncToBackend(renderbufferObject); - backendRenderbufferObject->Bind(); - MG_External::GLES::glFramebufferRenderbuffer(glFBOTarget, glAttachment, GL_RENDERBUFFER, - backendRenderbufferObject->GetBackendRenderbufferId()); - } - MG_External::GLES::glReadBuffer(glAttachment); + auto glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(att); + SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index bb7b25d3..2ae1c551 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -124,6 +124,9 @@ namespace MobileGL::MG_Backend::DirectGLES { FramebufferTarget asTarget); Uint GetBackendFramebufferId() { return m_backendFBOId; } void Bind(FramebufferTarget target); + bool SyncAttachmentObject(GLenum glFBOTarget, + const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, + GLenum glBackendAttachment); FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); private: @@ -152,6 +155,10 @@ namespace MobileGL::MG_Backend::DirectGLES { */ GLenum m_backendDrawBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = {GL_NONE}; FramebufferAttachmentType m_frontendReadBuffer = FramebufferAttachmentType::Color0; + GLenum m_backendReadBuffer = GL_COLOR_ATTACHMENT0; + + using FramebufferObject = MG_State::GLState::FramebufferObject; + FramebufferObject::FramebufferAttachmentVersionArray m_syncedAttachmentVersions = {0}; }; extern UnorderedMap, SharedPtr> diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 3298e640..294e9aec 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -77,48 +77,49 @@ namespace MobileGL { // FramebufferObject FramebufferObject::FramebufferObject(Uint externalIndex) : m_externalIndex(externalIndex) { - m_attachments.fill(FramebufferAttachmentObject(false)); + m_attachmentObjects.fill(FramebufferAttachmentObject(false)); m_drawBuffers.fill(FramebufferAttachmentType::None); m_drawBuffers[0] = FramebufferAttachmentType::Color0; + m_attachmentVersions.fill(0); } void FramebufferObject::AttachTexture(FramebufferAttachmentType type, SharedPtr texture, int level) { - m_attachments[static_cast(type)] = FramebufferAttachmentObject(std::move(texture), level); - m_drawBuffersDirty = true; + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(std::move(texture), level); + BumpAttachmentVersion(type); } void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type, std::shared_ptr renderbuffer) { - m_attachments[static_cast(type)] = FramebufferAttachmentObject(renderbuffer); - m_drawBuffersDirty = true; + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(renderbuffer); + BumpAttachmentVersion(type); } void FramebufferObject::Detach(FramebufferAttachmentType type) { - m_attachments[static_cast(type)] = FramebufferAttachmentObject(false); - m_drawBuffersDirty = true; + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(false); + BumpAttachmentVersion(type); } const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { - return m_attachments[static_cast(type)]; + return m_attachmentObjects[static_cast(type)]; } - const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachments() const { - return m_attachments; + const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachmentObjects() const { + return m_attachmentObjects; } Bool FramebufferObject::CheckCompleteness() const { - if (m_attachments.empty()) { + if (m_attachmentObjects.empty()) { return false; } Int width = -1, height = -1; Int validAttachmentCount = 0; - for (SizeT i = 0; i < m_attachments.size(); ++i) { - if (!m_attachments[i].IsValid()) continue; + for (SizeT i = 0; i < m_attachmentObjects.size(); ++i) { + if (!m_attachmentObjects[i].IsValid()) continue; ++validAttachmentCount; - const auto& attachment = m_attachments[i]; + const auto& attachment = m_attachmentObjects[i]; auto attachmentSize = attachment.GetSize(); Int w = attachmentSize.x(); Int h = attachmentSize.y(); @@ -141,18 +142,9 @@ namespace MobileGL { void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { if (m_drawBuffers[index] == buffer) return; - m_drawBuffersDirty = true; m_drawBuffers[index] = buffer; } - // void FramebufferObject::SetDrawBuffers(const Vector& buffers) { - // m_drawBuffers = buffers; - // m_drawBuffersDirty = true; - // } - // void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { - // - // } - const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const { return m_drawBuffers; } @@ -160,6 +152,11 @@ namespace MobileGL { Uint FramebufferObject::GetExternalIndex() const { return m_externalIndex; } + + void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) { + ++m_attachmentVersions[static_cast(type)]; + ++m_objectVersion; + } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index 55e8b087..b1cdd071 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -100,6 +100,8 @@ namespace MobileGL { using FramebufferAttachmentObjectArray = Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; using FramebufferAttachmentArray = Array; + using FramebufferAttachmentVersionArray = + Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; FramebufferObject(Uint externalIndex); @@ -108,24 +110,35 @@ namespace MobileGL { std::shared_ptr renderbuffer); void Detach(FramebufferAttachmentType type); const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const; - const FramebufferAttachmentObjectArray& GetAllAttachments() const; + const FramebufferAttachmentObjectArray& GetAllAttachmentObjects() const; Bool CheckCompleteness() const; // aka. `buffer` as in glDrawBuffers/glReadBuffers void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer); - bool DrawBuffersIsDirty() const { return m_drawBuffersDirty; } - void ClearDrawBuffersDirtyState() { m_drawBuffersDirty = false; } const FramebufferAttachmentArray& GetDrawBuffers() const; void SetReadBuffer(FramebufferAttachmentType buf) { m_readBuffer = buf; } FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; } + + const FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const { + return m_attachmentVersions; + } + + Uint16 GetObjectVersion() const { return m_objectVersion; } + Uint GetExternalIndex() const; private: - const Uint m_externalIndex = 0; - FramebufferAttachmentObjectArray m_attachments; - FramebufferAttachmentArray m_drawBuffers; - FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; + void BumpAttachmentVersion(FramebufferAttachmentType type); + void BumpBufferAttachVersion(); - Bool m_drawBuffersDirty = false; + const Uint m_externalIndex = 0; + FramebufferAttachmentObjectArray m_attachmentObjects; + FramebufferAttachmentVersionArray m_attachmentVersions; + + FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality + FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; // ditto + + // This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`) + Uint16 m_objectVersion = 0; }; } // namespace GLState From ca23fe4d6fe42d4dab0cc61b348092cad4469ca6 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 17:29:10 +0800 Subject: [PATCH 31/74] [Perf] (MG_Backend/DirectGLES): use version to skip unnecessary FBO sync --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 9 +++++++-- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 1 + MobileGL/MG_Backend/DirectGLES/Managers.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 4395706b..5863049b 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -242,7 +242,12 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_State::GLState::FramebufferObject* lastUpdatedFBO = nullptr; for (auto target : fboTargets) { - auto currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(target).GetBoundObject(); + auto slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); + auto version = slot.GetVersion(); + if (version == g_fboBindVersions[SizeT(target)]) + continue; + + auto currentFBO = slot.GetBoundObject(); if (!currentFBO) { MGLOG_E("No FBO is currently bound, cannot sync current FBO."); @@ -269,7 +274,7 @@ namespace MobileGL::MG_Backend::DirectGLES { backendFBOObject->SyncToBackend(currentFBO, target); } - backendFBOObject->Bind(target); +// backendFBOObject->Bind(target); lastUpdatedFBO = currentFBO.get(); } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 2f8aa73d..ecbc9135 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -987,6 +987,7 @@ namespace MobileGL::MG_Backend::DirectGLES { UnorderedMap, SharedPtr> g_backendFramebufferObjects; + Array g_fboBindVersions = {0}; } // namespace FramebufferImpl namespace PrgramImpl { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 2981d321..6268e30f 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -170,6 +170,7 @@ namespace MobileGL::MG_Backend::DirectGLES { extern UnorderedMap, SharedPtr> g_backendFramebufferObjects; + extern Array g_fboBindVersions; } // namespace FramebufferImpl namespace PrgramImpl { From afa89772e7d07aa34a38a8fe881f42a285f52a58 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 17:29:55 +0800 Subject: [PATCH 32/74] [Refactor] (MG_State/RenderState): Separate RenderStateParameters from RenderState. --- .../GLState/RenderState/RenderState.cpp | 130 +++++++++--------- .../GLState/RenderState/RenderState.h | 73 +++++----- 2 files changed, 103 insertions(+), 100 deletions(-) diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index d38ddb34..970ffb5e 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -15,27 +15,27 @@ namespace MobileGL { // -------------------- Rasterization -------------------- void RenderState::SetViewport(IntVec4 viewport) { - m_viewport = viewport; + m_parameters.Viewport = viewport; } const IntVec4& RenderState::GetViewport() const { - return m_viewport; + return m_parameters.Viewport; } // -------------------- Capabilities -------------------- void RenderState::SetCapability(CapabilityInput cap, Bool enabled) { switch (cap) { case CapabilityInput::Blend: - m_blendEnabled = enabled; + m_parameters.BlendEnabled = enabled; break; case CapabilityInput::DepthTest: - m_depthTestEnabled = enabled; + m_parameters.DepthTestEnabled = enabled; break; case CapabilityInput::CullFace: - m_cullFaceEnabled = enabled; + m_parameters.CullFaceEnabled = enabled; break; case CapabilityInput::ScissorTest: - m_scissorTestEnabled = enabled; + m_parameters.ScissorTestEnabled = enabled; break; default: // not supported currently break; @@ -45,13 +45,13 @@ namespace MobileGL { Bool RenderState::IsCapabilityEnabled(CapabilityInput cap) const { switch (cap) { case CapabilityInput::Blend: - return m_blendEnabled; + return m_parameters.BlendEnabled; case CapabilityInput::DepthTest: - return m_depthTestEnabled; + return m_parameters.DepthTestEnabled; case CapabilityInput::CullFace: - return m_cullFaceEnabled; + return m_parameters.CullFaceEnabled; case CapabilityInput::ScissorTest: - return m_scissorTestEnabled; + return m_parameters.ScissorTestEnabled; default: return false; } @@ -60,114 +60,114 @@ namespace MobileGL { // -------------------- Blending -------------------- void RenderState::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { - m_srcFactorRGB = srcRGB; - m_dstFactorRGB = dstRGB; - m_srcFactorAlpha = srcAlpha; - m_dstFactorAlpha = dstAlpha; + m_parameters.SrcFactorRGB = srcRGB; + m_parameters.DstFactorRGB = dstRGB; + m_parameters.SrcFactorAlpha = srcAlpha; + m_parameters.DstFactorAlpha = dstAlpha; } void RenderState::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { - srcRGB = m_srcFactorRGB; - dstRGB = m_dstFactorRGB; - srcAlpha = m_srcFactorAlpha; - dstAlpha = m_dstFactorAlpha; + srcRGB = m_parameters.SrcFactorRGB; + dstRGB = m_parameters.DstFactorRGB; + srcAlpha = m_parameters.SrcFactorAlpha; + dstAlpha = m_parameters.DstFactorAlpha; } // -------------------- Depth -------------------- void RenderState::SetDepthFunc(DepthTestFunc func) { - m_depthFunc = func; + m_parameters.DepthFunc = func; } DepthTestFunc RenderState::GetDepthFunc() const { - return m_depthFunc; + return m_parameters.DepthFunc; } void RenderState::SetDepthMask(Bool flag) { - m_depthMask = flag; + m_parameters.DepthMask = flag; } Bool RenderState::GetDepthMask() const { - return m_depthMask; + return m_parameters.DepthMask; } // -------------------- Color Mask -------------------- void RenderState::SetColorMask(BoolVec4 mask) { - m_colorMask = mask; + m_parameters.ColorMask = mask; } const BoolVec4 RenderState::GetColorMask() const { - return m_colorMask; + return m_parameters.ColorMask; } // -------------------- Clear State -------------------- void RenderState::SetClearColor(FloatVec4 color) { - m_clearColor = color; + m_parameters.ClearColor = color; } const FloatVec4& RenderState::GetClearColor() const { - return m_clearColor; + return m_parameters.ClearColor; } void RenderState::SetClearDepth(Float depth) { - m_clearDepth = depth; + m_parameters.ClearDepth = depth; } Float RenderState::GetClearDepth() const { - return m_clearDepth; + return m_parameters.ClearDepth; } // -------------------- Pixel Store -------------------- void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) { switch (param) { case PixelStoreParam::PackAlignment: - m_packParameters.Alignment = value; + m_parameters.PackParameters.Alignment = value; break; case PixelStoreParam::PackRowLength: - m_packParameters.RowLength = value; + m_parameters.PackParameters.RowLength = value; break; case PixelStoreParam::PackImageHeight: - m_packParameters.ImageHeight = value; + m_parameters.PackParameters.ImageHeight = value; break; case PixelStoreParam::PackSkipPixels: - m_packParameters.SkipPixels = value; + m_parameters.PackParameters.SkipPixels = value; break; case PixelStoreParam::PackSkipRows: - m_packParameters.SkipRows = value; + m_parameters.PackParameters.SkipRows = value; break; case PixelStoreParam::PackSkipImages: - m_packParameters.SkipImages = value; + m_parameters.PackParameters.SkipImages = value; break; case PixelStoreParam::PackSwapBytes: - m_packParameters.SwapBytes = value != 0; + m_parameters.PackParameters.SwapBytes = value != 0; break; case PixelStoreParam::PackLsbFirst: - m_packParameters.LSBFirst = value != 0; + m_parameters.PackParameters.LSBFirst = value != 0; break; case PixelStoreParam::UnpackAlignment: - m_unpackParameters.Alignment = value; + m_parameters.UnpackParameters.Alignment = value; break; case PixelStoreParam::UnpackRowLength: - m_unpackParameters.RowLength = value; + m_parameters.UnpackParameters.RowLength = value; break; case PixelStoreParam::UnpackImageHeight: - m_unpackParameters.ImageHeight = value; + m_parameters.UnpackParameters.ImageHeight = value; break; case PixelStoreParam::UnpackSkipPixels: - m_unpackParameters.SkipPixels = value; + m_parameters.UnpackParameters.SkipPixels = value; break; case PixelStoreParam::UnpackSkipRows: - m_unpackParameters.SkipRows = value; + m_parameters.UnpackParameters.SkipRows = value; break; case PixelStoreParam::UnpackSkipImages: - m_unpackParameters.SkipImages = value; + m_parameters.UnpackParameters.SkipImages = value; break; case PixelStoreParam::UnpackSwapBytes: - m_unpackParameters.SwapBytes = value != 0; + m_parameters.UnpackParameters.SwapBytes = value != 0; MGLOG_D("%s: SwapBytes = %s", __func__, value ? "true" : "false"); break; case PixelStoreParam::UnpackLsbFirst: - m_unpackParameters.LSBFirst = value != 0; + m_parameters.UnpackParameters.LSBFirst = value != 0; break; default: MOBILEGL_ASSERT(false, "Invalid PixelStoreParam enum: %d", static_cast(param)); @@ -178,37 +178,37 @@ namespace MobileGL { Int RenderState::GetPixelStoreParam(PixelStoreParam param) const { switch (param) { case PixelStoreParam::PackAlignment: - return m_packParameters.Alignment; + return m_parameters.PackParameters.Alignment; case PixelStoreParam::PackRowLength: - return m_packParameters.RowLength; + return m_parameters.PackParameters.RowLength; case PixelStoreParam::PackImageHeight: - return m_packParameters.ImageHeight; + return m_parameters.PackParameters.ImageHeight; case PixelStoreParam::PackSkipPixels: - return m_packParameters.SkipPixels; + return m_parameters.PackParameters.SkipPixels; case PixelStoreParam::PackSkipRows: - return m_packParameters.SkipRows; + return m_parameters.PackParameters.SkipRows; case PixelStoreParam::PackSkipImages: - return m_packParameters.SkipImages; + return m_parameters.PackParameters.SkipImages; case PixelStoreParam::PackSwapBytes: - return m_packParameters.SwapBytes ? 1 : 0; + return m_parameters.PackParameters.SwapBytes ? 1 : 0; case PixelStoreParam::PackLsbFirst: - return m_packParameters.LSBFirst ? 1 : 0; + return m_parameters.PackParameters.LSBFirst ? 1 : 0; case PixelStoreParam::UnpackAlignment: - return m_unpackParameters.Alignment; + return m_parameters.UnpackParameters.Alignment; case PixelStoreParam::UnpackRowLength: - return m_unpackParameters.RowLength; + return m_parameters.UnpackParameters.RowLength; case PixelStoreParam::UnpackImageHeight: - return m_unpackParameters.ImageHeight; + return m_parameters.UnpackParameters.ImageHeight; case PixelStoreParam::UnpackSkipPixels: - return m_unpackParameters.SkipPixels; + return m_parameters.UnpackParameters.SkipPixels; case PixelStoreParam::UnpackSkipRows: - return m_unpackParameters.SkipRows; + return m_parameters.UnpackParameters.SkipRows; case PixelStoreParam::UnpackSkipImages: - return m_unpackParameters.SkipImages; + return m_parameters.UnpackParameters.SkipImages; case PixelStoreParam::UnpackSwapBytes: - return m_unpackParameters.SwapBytes ? 1 : 0; + return m_parameters.UnpackParameters.SwapBytes ? 1 : 0; case PixelStoreParam::UnpackLsbFirst: - return m_unpackParameters.LSBFirst ? 1 : 0; + return m_parameters.UnpackParameters.LSBFirst ? 1 : 0; default: MOBILEGL_ASSERT(false, "Invalid PixelStoreParam enum: %d", static_cast(param)); return 0; @@ -216,25 +216,25 @@ namespace MobileGL { } PixelStoreParameters RenderState::GetPixelStoreParameters(Bool isUnpack) const { - return isUnpack ? m_unpackParameters : m_packParameters; + return isUnpack ? m_parameters.UnpackParameters : m_parameters.PackParameters; } // -------------------- Cull Face -------------------- void RenderState::SetCullFaceMode(CullFaceMode mode) { - m_cullFaceMode = mode; + m_parameters.CullFaceModeSetting = mode; } CullFaceMode RenderState::GetCullFaceMode() const { - return m_cullFaceMode; + return m_parameters.CullFaceModeSetting; } // --------------------- Scissor --------------------- void RenderState::SetScissorBox(IntVec4 box) { - m_scissorBox = box; + m_parameters.ScissorBox = box; } const IntVec4& RenderState::GetScissorBox() const { - return m_scissorBox; + return m_parameters.ScissorBox; } } // namespace GLState } // namespace MG_State diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index e114aabd..952ea8e0 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -7,9 +7,8 @@ // End of Source File Header #pragma once -#include "MG_Util/Math/VectorTypes.h" -#include "MG_Util/Types.h" #include +#include namespace MobileGL { enum class BlendFactor { @@ -128,6 +127,42 @@ namespace MobileGL { Int Alignment = 4; }; + struct RenderStateParameters { + // Rasterization + IntVec4 Viewport = IntVec4(0, 0, 0, 0); // x, y, width, height + + // Blending + Bool BlendEnabled = false; + BlendFactor SrcFactorRGB = BlendFactor::One; + BlendFactor DstFactorRGB = BlendFactor::Zero; + BlendFactor SrcFactorAlpha = BlendFactor::One; + BlendFactor DstFactorAlpha = BlendFactor::Zero; + + // Depth + Bool DepthTestEnabled = false; + DepthTestFunc DepthFunc = DepthTestFunc::Less; + Bool DepthMask = true; + + // Color Mask + BoolVec4 ColorMask = BoolVec4(true, true, true, true); + + // Clear State + FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); + Float ClearDepth = 1.0f; + + // Pixel Store + PixelStoreParameters PackParameters; + PixelStoreParameters UnpackParameters; + + // Cull Face + Bool CullFaceEnabled = false; + CullFaceMode CullFaceModeSetting = CullFaceMode::Back; + + // Scissor + Bool ScissorTestEnabled = false; + IntVec4 ScissorBox = IntVec4(0, 0, 0, 0); // x, y, width, height + }; + namespace MG_State { namespace GLState { class RenderState { @@ -177,39 +212,7 @@ namespace MobileGL { const IntVec4& GetScissorBox() const; // x, y, width, height private: - // Rasterization - IntVec4 m_viewport = IntVec4(0, 0, 0, 0); // x, y, width, height - - // Blending - Bool m_blendEnabled = false; - BlendFactor m_srcFactorRGB = BlendFactor::One; - BlendFactor m_dstFactorRGB = BlendFactor::Zero; - BlendFactor m_srcFactorAlpha = BlendFactor::One; - BlendFactor m_dstFactorAlpha = BlendFactor::Zero; - - // Depth - Bool m_depthTestEnabled = false; - DepthTestFunc m_depthFunc = DepthTestFunc::Less; - Bool m_depthMask = true; - - // Color Mask - BoolVec4 m_colorMask = BoolVec4(true, true, true, true); - - // Clear State - FloatVec4 m_clearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); - Float m_clearDepth = 1.0f; - - // Pixel Store - PixelStoreParameters m_packParameters; - PixelStoreParameters m_unpackParameters; - - // Cull Face - Bool m_cullFaceEnabled = false; - CullFaceMode m_cullFaceMode = CullFaceMode::Back; - - // Scissor - Bool m_scissorTestEnabled = false; - IntVec4 m_scissorBox = IntVec4(0, 0, 0, 0); // x, y, width, height + RenderStateParameters m_parameters; }; } // namespace GLState } // namespace MG_State From c2ffa93c90bc64eab8fd1d24a6923758579318a5 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 17:49:47 +0800 Subject: [PATCH 33/74] [Perf] (MG_Backend/DirectGLES): use version to skip unnecessary FBO sync in `BindCurrentFBO` --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 5863049b..7cafb295 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -373,7 +373,11 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - const auto& currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(target).GetBoundObject(); + auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); + if (slot.GetVersion() == FramebufferImpl::g_fboBindVersions[(SizeT)target]) + return; + + const auto& currentFBO = slot.GetBoundObject(); if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { const auto& backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find(currentFBO); if (backendFBOIt != FramebufferImpl::g_backendFramebufferObjects.end()) { From 41f6670a1059d8dba3a715cb72a46765e21549a3 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 17:50:20 +0800 Subject: [PATCH 34/74] [Perf] (MG_Backend/DirectGLES): less shared_ptr deref --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index ecbc9135..95bb6170 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -603,7 +603,8 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - Uint currentSamplerVersion = stateTextureObject->GetSamplerObject()->GetVersion(); + auto* samplerObject = stateTextureObject->GetSamplerObject().get(); + Uint currentSamplerVersion = samplerObject->GetVersion(); if (m_syncedSamplerVersion == currentSamplerVersion) { MGLOG_D("Sampler parameters have not changed for texture ID: %u, skipping sync.", m_backendTextureId); return; @@ -631,7 +632,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // Update built-in sampler parameters MGLOG_D("Updating sampler parameters for texture with ID: %u", m_backendTextureId); - const auto& samplerParams = stateTextureObject->GetSamplerObject()->GetAllSamplerParameters(); + const auto& samplerParams = samplerObject->GetAllSamplerParameters(); #define SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \ if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \ From ea098c04094f2adea82f2f7de2380d0d90c5e147 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 17:50:46 +0800 Subject: [PATCH 35/74] [Feat|Refactor] (MG_State/RenderState): Versioning for RenderStateParameters. --- MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp | 4 +- MobileGL/MG_State/GLState/Core.cpp | 8 + MobileGL/MG_State/GLState/Core.h | 2 + .../GLState/RenderState/RenderState.cpp | 197 +++++++++--------- .../GLState/RenderState/RenderState.h | 16 +- .../GLToMG/RenderStateEnumConverter.cpp | 4 +- .../MGToGL/RenderStateEnumConverter.cpp | 4 +- .../MGToStr/RenderStateEnumConverter.cpp | 8 +- 8 files changed, 125 insertions(+), 118 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index 6c15d1ad..d9bc6ebb 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -567,7 +567,7 @@ namespace MobileGL { *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackImageHeight); break; case GL_PACK_LSB_FIRST: - *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackLsbFirst); + *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackLSBFirst); break; case GL_PACK_ROW_LENGTH: *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackRowLength); @@ -839,7 +839,7 @@ namespace MobileGL { *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackImageHeight); break; case GL_UNPACK_LSB_FIRST: - *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackLsbFirst); + *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackLSBFirst); break; case GL_UNPACK_ROW_LENGTH: *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackRowLength); diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 206edfc5..87e313f0 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -220,6 +220,14 @@ namespace MobileGL { } // RenderState + Uint GLContext::GetRenderStateParametersVersion() const { + return m_renderState.GetVersion(); + } + + const RenderStateParameters& GLContext::GetRenderStateParameters() const { + return m_renderState.GetAllParameters(); + } + void GLContext::SetViewport(IntVec4 viewport) { m_renderState.SetViewport(viewport); } diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index a0f258b2..9e7f4001 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -86,6 +86,8 @@ namespace MobileGL { SharedPtr GetCurrentProgram(); // RenderState + Uint GetRenderStateParametersVersion() const; + const RenderStateParameters& GetRenderStateParameters() const; void SetViewport(IntVec4 viewport); // x, y, width, height const IntVec4& GetViewport() const; // x, y, width, height void SetCapability(CapabilityInput cap, Bool enabled); diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index 970ffb5e..d913b6eb 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -13,9 +13,20 @@ namespace MobileGL { namespace GLState { RenderState::RenderState() {} + Uint RenderState::GetVersion() const { + return m_version; + } + + const RenderStateParameters& RenderState::GetAllParameters() const { + return m_parameters; + } + // -------------------- Rasterization -------------------- void RenderState::SetViewport(IntVec4 viewport) { + if (m_parameters.Viewport == viewport) return; + m_parameters.Viewport = viewport; + ++m_version; } const IntVec4& RenderState::GetViewport() const { @@ -24,34 +35,33 @@ namespace MobileGL { // -------------------- Capabilities -------------------- void RenderState::SetCapability(CapabilityInput cap, Bool enabled) { +#define SET_CAPABILITY(capability, flag) \ + case CapabilityInput::capability: \ + if (m_parameters.capability##Enabled == flag) break; \ + m_parameters.capability##Enabled = flag; \ + ++m_version; \ + break; + switch (cap) { - case CapabilityInput::Blend: - m_parameters.BlendEnabled = enabled; - break; - case CapabilityInput::DepthTest: - m_parameters.DepthTestEnabled = enabled; - break; - case CapabilityInput::CullFace: - m_parameters.CullFaceEnabled = enabled; - break; - case CapabilityInput::ScissorTest: - m_parameters.ScissorTestEnabled = enabled; - break; + SET_CAPABILITY(Blend, enabled); + SET_CAPABILITY(DepthTest, enabled); + SET_CAPABILITY(CullFace, enabled); + SET_CAPABILITY(ScissorTest, enabled); default: // not supported currently break; } +#undef SET_CAPABILITY } Bool RenderState::IsCapabilityEnabled(CapabilityInput cap) const { +#define RETURN_CAPABILITY(capability) \ + case CapabilityInput::capability: \ + return m_parameters.capability##Enabled; switch (cap) { - case CapabilityInput::Blend: - return m_parameters.BlendEnabled; - case CapabilityInput::DepthTest: - return m_parameters.DepthTestEnabled; - case CapabilityInput::CullFace: - return m_parameters.CullFaceEnabled; - case CapabilityInput::ScissorTest: - return m_parameters.ScissorTestEnabled; + RETURN_CAPABILITY(Blend); + RETURN_CAPABILITY(DepthTest); + RETURN_CAPABILITY(CullFace); + RETURN_CAPABILITY(ScissorTest); default: return false; } @@ -60,10 +70,15 @@ namespace MobileGL { // -------------------- Blending -------------------- void RenderState::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { + if (m_parameters.SrcFactorRGB == srcRGB && m_parameters.DstFactorRGB == dstRGB && + m_parameters.SrcFactorAlpha == srcAlpha && m_parameters.DstFactorAlpha == dstAlpha) + return; + m_parameters.SrcFactorRGB = srcRGB; m_parameters.DstFactorRGB = dstRGB; m_parameters.SrcFactorAlpha = srcAlpha; m_parameters.DstFactorAlpha = dstAlpha; + ++m_version; } void RenderState::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, @@ -76,7 +91,10 @@ namespace MobileGL { // -------------------- Depth -------------------- void RenderState::SetDepthFunc(DepthTestFunc func) { + if (m_parameters.DepthFunc == func) return; + m_parameters.DepthFunc = func; + ++m_version; } DepthTestFunc RenderState::GetDepthFunc() const { @@ -84,7 +102,10 @@ namespace MobileGL { } void RenderState::SetDepthMask(Bool flag) { + if (m_parameters.DepthMask == flag) return; + m_parameters.DepthMask = flag; + ++m_version; } Bool RenderState::GetDepthMask() const { @@ -102,7 +123,10 @@ namespace MobileGL { // -------------------- Clear State -------------------- void RenderState::SetClearColor(FloatVec4 color) { + if (m_parameters.ClearColor == color) return; + m_parameters.ClearColor = color; + ++m_version; } const FloatVec4& RenderState::GetClearColor() const { @@ -110,7 +134,10 @@ namespace MobileGL { } void RenderState::SetClearDepth(Float depth) { + if (m_parameters.ClearDepth == depth) return; + m_parameters.ClearDepth = depth; + ++m_version; } Float RenderState::GetClearDepth() const { @@ -119,56 +146,29 @@ namespace MobileGL { // -------------------- Pixel Store -------------------- void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) { +#define SET_PIXEL_STORE_PARAM(paramNameHead, paramNameTail, val) \ + case PixelStoreParam::paramNameHead##paramNameTail: \ + if (m_pixelStore##paramNameHead##Parameters.paramNameTail == val) break; \ + m_pixelStore##paramNameHead##Parameters.paramNameTail = val; \ + break; + switch (param) { - case PixelStoreParam::PackAlignment: - m_parameters.PackParameters.Alignment = value; - break; - case PixelStoreParam::PackRowLength: - m_parameters.PackParameters.RowLength = value; - break; - case PixelStoreParam::PackImageHeight: - m_parameters.PackParameters.ImageHeight = value; - break; - case PixelStoreParam::PackSkipPixels: - m_parameters.PackParameters.SkipPixels = value; - break; - case PixelStoreParam::PackSkipRows: - m_parameters.PackParameters.SkipRows = value; - break; - case PixelStoreParam::PackSkipImages: - m_parameters.PackParameters.SkipImages = value; - break; - case PixelStoreParam::PackSwapBytes: - m_parameters.PackParameters.SwapBytes = value != 0; - break; - case PixelStoreParam::PackLsbFirst: - m_parameters.PackParameters.LSBFirst = value != 0; - break; - case PixelStoreParam::UnpackAlignment: - m_parameters.UnpackParameters.Alignment = value; - break; - case PixelStoreParam::UnpackRowLength: - m_parameters.UnpackParameters.RowLength = value; - break; - case PixelStoreParam::UnpackImageHeight: - m_parameters.UnpackParameters.ImageHeight = value; - break; - case PixelStoreParam::UnpackSkipPixels: - m_parameters.UnpackParameters.SkipPixels = value; - break; - case PixelStoreParam::UnpackSkipRows: - m_parameters.UnpackParameters.SkipRows = value; - break; - case PixelStoreParam::UnpackSkipImages: - m_parameters.UnpackParameters.SkipImages = value; - break; - case PixelStoreParam::UnpackSwapBytes: - m_parameters.UnpackParameters.SwapBytes = value != 0; - MGLOG_D("%s: SwapBytes = %s", __func__, value ? "true" : "false"); - break; - case PixelStoreParam::UnpackLsbFirst: - m_parameters.UnpackParameters.LSBFirst = value != 0; - break; + SET_PIXEL_STORE_PARAM(Pack, Alignment, value); + SET_PIXEL_STORE_PARAM(Pack, RowLength, value); + SET_PIXEL_STORE_PARAM(Pack, ImageHeight, value); + SET_PIXEL_STORE_PARAM(Pack, SkipPixels, value); + SET_PIXEL_STORE_PARAM(Pack, SkipRows, value); + SET_PIXEL_STORE_PARAM(Pack, SkipImages, value); + SET_PIXEL_STORE_PARAM(Pack, SwapBytes, value != 0); + SET_PIXEL_STORE_PARAM(Pack, LSBFirst, value != 0); + SET_PIXEL_STORE_PARAM(Unpack, Alignment, value); + SET_PIXEL_STORE_PARAM(Unpack, RowLength, value); + SET_PIXEL_STORE_PARAM(Unpack, ImageHeight, value); + SET_PIXEL_STORE_PARAM(Unpack, SkipPixels, value); + SET_PIXEL_STORE_PARAM(Unpack, SkipRows, value); + SET_PIXEL_STORE_PARAM(Unpack, SkipImages, value); + SET_PIXEL_STORE_PARAM(Unpack, SwapBytes, value != 0); + SET_PIXEL_STORE_PARAM(Unpack, LSBFirst, value != 0); default: MOBILEGL_ASSERT(false, "Invalid PixelStoreParam enum: %d", static_cast(param)); return; @@ -176,39 +176,26 @@ namespace MobileGL { } Int RenderState::GetPixelStoreParam(PixelStoreParam param) const { +#define RETURN_PIXEL_STORE_PARAM(paramNameHead, paramNameTail) \ + case PixelStoreParam::paramNameHead##paramNameTail: \ + return m_pixelStore##paramNameHead##Parameters.paramNameTail; switch (param) { - case PixelStoreParam::PackAlignment: - return m_parameters.PackParameters.Alignment; - case PixelStoreParam::PackRowLength: - return m_parameters.PackParameters.RowLength; - case PixelStoreParam::PackImageHeight: - return m_parameters.PackParameters.ImageHeight; - case PixelStoreParam::PackSkipPixels: - return m_parameters.PackParameters.SkipPixels; - case PixelStoreParam::PackSkipRows: - return m_parameters.PackParameters.SkipRows; - case PixelStoreParam::PackSkipImages: - return m_parameters.PackParameters.SkipImages; - case PixelStoreParam::PackSwapBytes: - return m_parameters.PackParameters.SwapBytes ? 1 : 0; - case PixelStoreParam::PackLsbFirst: - return m_parameters.PackParameters.LSBFirst ? 1 : 0; - case PixelStoreParam::UnpackAlignment: - return m_parameters.UnpackParameters.Alignment; - case PixelStoreParam::UnpackRowLength: - return m_parameters.UnpackParameters.RowLength; - case PixelStoreParam::UnpackImageHeight: - return m_parameters.UnpackParameters.ImageHeight; - case PixelStoreParam::UnpackSkipPixels: - return m_parameters.UnpackParameters.SkipPixels; - case PixelStoreParam::UnpackSkipRows: - return m_parameters.UnpackParameters.SkipRows; - case PixelStoreParam::UnpackSkipImages: - return m_parameters.UnpackParameters.SkipImages; - case PixelStoreParam::UnpackSwapBytes: - return m_parameters.UnpackParameters.SwapBytes ? 1 : 0; - case PixelStoreParam::UnpackLsbFirst: - return m_parameters.UnpackParameters.LSBFirst ? 1 : 0; + RETURN_PIXEL_STORE_PARAM(Pack, Alignment); + RETURN_PIXEL_STORE_PARAM(Pack, RowLength); + RETURN_PIXEL_STORE_PARAM(Pack, ImageHeight); + RETURN_PIXEL_STORE_PARAM(Pack, SkipPixels); + RETURN_PIXEL_STORE_PARAM(Pack, SkipRows); + RETURN_PIXEL_STORE_PARAM(Pack, SkipImages); + RETURN_PIXEL_STORE_PARAM(Pack, SwapBytes); + RETURN_PIXEL_STORE_PARAM(Pack, LSBFirst); + RETURN_PIXEL_STORE_PARAM(Unpack, Alignment); + RETURN_PIXEL_STORE_PARAM(Unpack, RowLength); + RETURN_PIXEL_STORE_PARAM(Unpack, ImageHeight); + RETURN_PIXEL_STORE_PARAM(Unpack, SkipPixels); + RETURN_PIXEL_STORE_PARAM(Unpack, SkipRows); + RETURN_PIXEL_STORE_PARAM(Unpack, SkipImages); + RETURN_PIXEL_STORE_PARAM(Unpack, SwapBytes); + RETURN_PIXEL_STORE_PARAM(Unpack, LSBFirst); default: MOBILEGL_ASSERT(false, "Invalid PixelStoreParam enum: %d", static_cast(param)); return 0; @@ -216,12 +203,15 @@ namespace MobileGL { } PixelStoreParameters RenderState::GetPixelStoreParameters(Bool isUnpack) const { - return isUnpack ? m_parameters.UnpackParameters : m_parameters.PackParameters; + return isUnpack ? m_pixelStoreUnpackParameters : m_pixelStorePackParameters; } // -------------------- Cull Face -------------------- void RenderState::SetCullFaceMode(CullFaceMode mode) { + if (m_parameters.CullFaceModeSetting == mode) return; + m_parameters.CullFaceModeSetting = mode; + ++m_version; } CullFaceMode RenderState::GetCullFaceMode() const { @@ -230,7 +220,10 @@ namespace MobileGL { // --------------------- Scissor --------------------- void RenderState::SetScissorBox(IntVec4 box) { + if (m_parameters.ScissorBox == box) return; + m_parameters.ScissorBox = box; + ++m_version; } const IntVec4& RenderState::GetScissorBox() const { diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index 952ea8e0..c97f92c9 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -52,7 +52,7 @@ namespace MobileGL { PackSkipPixels, PackSkipImages, PackSwapBytes, - PackLsbFirst, + PackLSBFirst, // Unpack Parameters UnpackAlignment, @@ -62,7 +62,7 @@ namespace MobileGL { UnpackSkipPixels, UnpackSkipImages, UnpackSwapBytes, - UnpackLsbFirst, + UnpackLSBFirst, PixelStoreParamCount, Unknown = -1 @@ -150,10 +150,6 @@ namespace MobileGL { FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); Float ClearDepth = 1.0f; - // Pixel Store - PixelStoreParameters PackParameters; - PixelStoreParameters UnpackParameters; - // Cull Face Bool CullFaceEnabled = false; CullFaceMode CullFaceModeSetting = CullFaceMode::Back; @@ -169,6 +165,9 @@ namespace MobileGL { public: RenderState(); + Uint GetVersion() const; + const RenderStateParameters& GetAllParameters() const; + // Rasterization void SetViewport(IntVec4 viewport); // x, y, width, height const IntVec4& GetViewport() const; // x, y, width, height @@ -212,7 +211,12 @@ namespace MobileGL { const IntVec4& GetScissorBox() const; // x, y, width, height private: + Uint16 m_version = 0; RenderStateParameters m_parameters; + + // Pixel Store + PixelStoreParameters m_pixelStorePackParameters; + PixelStoreParameters m_pixelStoreUnpackParameters; }; } // namespace GLState } // namespace MG_State diff --git a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp index 7eac9a58..3ebace71 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp @@ -85,7 +85,7 @@ namespace MobileGL { case GL_PACK_SWAP_BYTES: return PixelStoreParam::PackSwapBytes; case GL_PACK_LSB_FIRST: - return PixelStoreParam::PackLsbFirst; + return PixelStoreParam::PackLSBFirst; case GL_UNPACK_ALIGNMENT: return PixelStoreParam::UnpackAlignment; case GL_UNPACK_ROW_LENGTH: @@ -101,7 +101,7 @@ namespace MobileGL { case GL_UNPACK_SWAP_BYTES: return PixelStoreParam::UnpackSwapBytes; case GL_UNPACK_LSB_FIRST: - return PixelStoreParam::UnpackLsbFirst; + return PixelStoreParam::UnpackLSBFirst; default: return PixelStoreParam::Unknown; } diff --git a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp index ac37b256..3cd42eeb 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp @@ -85,7 +85,7 @@ namespace MobileGL { return GL_PACK_SKIP_IMAGES; case PixelStoreParam::PackSwapBytes: return GL_PACK_SWAP_BYTES; - case PixelStoreParam::PackLsbFirst: + case PixelStoreParam::PackLSBFirst: return GL_PACK_LSB_FIRST; case PixelStoreParam::UnpackAlignment: return GL_UNPACK_ALIGNMENT; @@ -101,7 +101,7 @@ namespace MobileGL { return GL_UNPACK_SKIP_IMAGES; case PixelStoreParam::UnpackSwapBytes: return GL_UNPACK_SWAP_BYTES; - case PixelStoreParam::UnpackLsbFirst: + case PixelStoreParam::UnpackLSBFirst: return GL_UNPACK_LSB_FIRST; default: return GL_UNKNOWN_MGL; diff --git a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp index d057072c..f985aa8f 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp @@ -84,8 +84,8 @@ namespace MobileGL { return "PackSkipImages"; case PixelStoreParam::PackSwapBytes: return "PackSwapBytes"; - case PixelStoreParam::PackLsbFirst: - return "PackLsbFirst"; + case PixelStoreParam::PackLSBFirst: + return "PackLSBFirst"; case PixelStoreParam::UnpackAlignment: return "UnpackAlignment"; case PixelStoreParam::UnpackRowLength: @@ -100,8 +100,8 @@ namespace MobileGL { return "UnpackSkipImages"; case PixelStoreParam::UnpackSwapBytes: return "UnpackSwapBytes"; - case PixelStoreParam::UnpackLsbFirst: - return "UnpackLsbFirst"; + case PixelStoreParam::UnpackLSBFirst: + return "UnpackLSBFirst"; default: return "Unknown"; } From 8e3bbda2f2a35444913b361e25b31e869ac38378 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 18:24:26 +0800 Subject: [PATCH 36/74] [Fix] (MG_State/RenderState): Versioning for SetColorMask. --- MobileGL/MG_State/GLState/RenderState/RenderState.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index d913b6eb..acd7f987 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -114,7 +114,10 @@ namespace MobileGL { // -------------------- Color Mask -------------------- void RenderState::SetColorMask(BoolVec4 mask) { + if (m_parameters.ColorMask == mask) return; + m_parameters.ColorMask = mask; + ++m_version; } const BoolVec4 RenderState::GetColorMask() const { From 5addbb3bddfea3edd7cc9a637d7ee7ee9c9daa96 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 18:42:05 +0800 Subject: [PATCH 37/74] [Perf] (MG_Backend/DirectGLES): Add RenderState sync cache. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 93 ++++++++++++------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 7cafb295..9693e6f8 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -244,8 +244,7 @@ namespace MobileGL::MG_Backend::DirectGLES { for (auto target : fboTargets) { auto slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); auto version = slot.GetVersion(); - if (version == g_fboBindVersions[SizeT(target)]) - continue; + if (version == g_fboBindVersions[SizeT(target)]) continue; auto currentFBO = slot.GetBoundObject(); @@ -274,7 +273,7 @@ namespace MobileGL::MG_Backend::DirectGLES { backendFBOObject->SyncToBackend(currentFBO, target); } -// backendFBOObject->Bind(target); + // backendFBOObject->Bind(target); lastUpdatedFBO = currentFBO.get(); } @@ -282,31 +281,45 @@ namespace MobileGL::MG_Backend::DirectGLES { } // namespace FramebufferImpl namespace RenderStateImpl { + static Uint16 g_syncedRenderStateVersion = 0; + static RenderStateParameters g_syncedRenderStateParameters; void SyncRenderState() { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - MG_External::GLES::glViewport( - MG_State::pGLContext->GetViewport().x(), MG_State::pGLContext->GetViewport().y(), - MG_State::pGLContext->GetViewport().z(), MG_State::pGLContext->GetViewport().w()); + Uint16 currentRenderStateVersion = MG_State::pGLContext->GetRenderStateParametersVersion(); + if (currentRenderStateVersion == g_syncedRenderStateVersion) return; + + const auto& parameters = MG_State::pGLContext->GetRenderStateParameters(); + + if (parameters.Viewport != g_syncedRenderStateParameters.Viewport) { + MG_External::GLES::glViewport(parameters.Viewport.x(), parameters.Viewport.y(), parameters.Viewport.z(), + parameters.Viewport.w()); + } + #define SYNC_CAPABILITY(cap_mg, cap_gl) \ - if (MG_State::pGLContext->IsCapabilityEnabled(cap_mg)) { \ - MG_External::GLES::glEnable(cap_gl); \ - } else { \ - MG_External::GLES::glDisable(cap_gl); \ + if (parameters.cap_mg##Enabled != g_syncedRenderStateParameters.cap_mg##Enabled) { \ + if (parameters.cap_mg##Enabled) { \ + MG_External::GLES::glEnable(cap_gl); \ + } else { \ + MG_External::GLES::glDisable(cap_gl); \ + } \ } - SYNC_CAPABILITY(CapabilityInput::Blend, GL_BLEND); - SYNC_CAPABILITY(CapabilityInput::DepthTest, GL_DEPTH_TEST); - SYNC_CAPABILITY(CapabilityInput::ScissorTest, GL_SCISSOR_TEST); - SYNC_CAPABILITY(CapabilityInput::CullFace, GL_CULL_FACE); + SYNC_CAPABILITY(Blend, GL_BLEND); + SYNC_CAPABILITY(DepthTest, GL_DEPTH_TEST); + SYNC_CAPABILITY(ScissorTest, GL_SCISSOR_TEST); + SYNC_CAPABILITY(CullFace, GL_CULL_FACE); #undef SYNC_CAPABILITY const auto& ToGLBoolean = [](Bool b) -> GLboolean { return b ? GL_TRUE : GL_FALSE; }; - { // Blend func - BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha; - MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); + if (parameters.SrcFactorRGB != g_syncedRenderStateParameters.SrcFactorRGB || + parameters.DstFactorRGB != g_syncedRenderStateParameters.DstFactorRGB || + parameters.SrcFactorAlpha != g_syncedRenderStateParameters.SrcFactorAlpha || + parameters.DstFactorAlpha != g_syncedRenderStateParameters.DstFactorAlpha) { // Blend func + const BlendFactor &srcRGB = parameters.SrcFactorRGB, &dstRGB = parameters.DstFactorRGB, + &srcAlpha = parameters.SrcFactorAlpha, &dstAlpha = parameters.DstFactorAlpha; MG_External::GLES::glBlendFuncSeparate( MG_Util::ConvertBlendFactorToGLEnum(srcRGB), MG_Util::ConvertBlendFactorToGLEnum(dstRGB), @@ -314,33 +327,48 @@ namespace MobileGL::MG_Backend::DirectGLES { } { // Blend equation - DepthTestFunc df = MG_State::pGLContext->GetDepthFunc(); - MG_External::GLES::glDepthFunc(MG_Util::ConvertDepthTestFuncToGLEnum(df)); - - MG_External::GLES::glDepthMask(MG_State::pGLContext->GetDepthMask() ? GL_TRUE : GL_FALSE); + if (parameters.DepthFunc != g_syncedRenderStateParameters.DepthFunc) { + MG_External::GLES::glDepthFunc(MG_Util::ConvertDepthTestFuncToGLEnum(parameters.DepthFunc)); + } + if (parameters.DepthMask != g_syncedRenderStateParameters.DepthMask) { + MG_External::GLES::glDepthMask(parameters.DepthMask ? GL_TRUE : GL_FALSE); + } } { // Color mask - BoolVec4 colorMask = MG_State::pGLContext->GetColorMask(); - MG_External::GLES::glColorMask(ToGLBoolean(colorMask.x()), ToGLBoolean(colorMask.y()), - ToGLBoolean(colorMask.z()), ToGLBoolean(colorMask.w())); + if (parameters.ColorMask != g_syncedRenderStateParameters.ColorMask) { + const BoolVec4& colorMask = parameters.ColorMask; + MG_External::GLES::glColorMask(ToGLBoolean(colorMask.x()), ToGLBoolean(colorMask.y()), + ToGLBoolean(colorMask.z()), ToGLBoolean(colorMask.w())); + } } { // Clear values - const FloatVec4& clearCol = MG_State::pGLContext->GetClearColor(); - MG_External::GLES::glClearColor(clearCol.x(), clearCol.y(), clearCol.z(), clearCol.w()); - MG_External::GLES::glClearDepthf(MG_State::pGLContext->GetClearDepth()); + if (parameters.ClearColor != g_syncedRenderStateParameters.ClearColor) { + const FloatVec4& clearCol = parameters.ClearColor; + MG_External::GLES::glClearColor(clearCol.x(), clearCol.y(), clearCol.z(), clearCol.w()); + } + if (parameters.ClearDepth != g_syncedRenderStateParameters.ClearDepth) { + MG_External::GLES::glClearDepthf(parameters.ClearDepth); + } } { // Cull face mode - CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode(); - MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm)); + if (parameters.CullFaceModeSetting != g_syncedRenderStateParameters.CullFaceModeSetting) { + const CullFaceMode& cfm = parameters.CullFaceModeSetting; + MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm)); + } } { // Scissor box - const IntVec4& scissorBox = MG_State::pGLContext->GetScissorBox(); - MG_External::GLES::glScissor(scissorBox.x(), scissorBox.y(), scissorBox.z(), scissorBox.w()); + if (parameters.ScissorBox != g_syncedRenderStateParameters.ScissorBox) { + const IntVec4& scissorBox = parameters.ScissorBox; + MG_External::GLES::glScissor(scissorBox.x(), scissorBox.y(), scissorBox.z(), scissorBox.w()); + } } + + g_syncedRenderStateVersion = currentRenderStateVersion; + g_syncedRenderStateParameters = parameters; } } // namespace RenderStateImpl @@ -374,8 +402,7 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); - if (slot.GetVersion() == FramebufferImpl::g_fboBindVersions[(SizeT)target]) - return; + if (slot.GetVersion() == FramebufferImpl::g_fboBindVersions[(SizeT)target]) return; const auto& currentFBO = slot.GetBoundObject(); if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { From 850d363cacc5a633857ac5f0cf27de989cb2485c Mon Sep 17 00:00:00 2001 From: BZLZHH <69153940+BZLZHH@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:22:23 +0800 Subject: [PATCH 38/74] [Chore] (Config): Bump version to 26.02 --- MobileGL/Config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MobileGL/Config.h b/MobileGL/Config.h index a789782c..b80440a1 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -15,7 +15,7 @@ namespace MobileGL { inline const String ProjectName = "MobileGL"; inline const String CoreName = "MobileGL Core"; inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)"; - inline const Version CoreVersion = {26, 1, 0, "-dev", VersionType::Development}; + inline const Version CoreVersion = {26, 2, 0, "-dev", VersionType::Development}; inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true}; extern UniquePtr RendererInfoPtr; @@ -28,4 +28,4 @@ namespace MobileGL { } } // namespace Backend } // namespace MG_Config -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL From f0ab82ea2a66737fcce1ade64cf14b79ecc63341 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 21:02:54 +0800 Subject: [PATCH 39/74] [Fix] (MG_Backend/DirectGLES): Fix VBO binding state leakage. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 154 +++++++++----------- MobileGL/MG_Backend/DirectGLES/Managers.h | 4 +- 2 files changed, 70 insertions(+), 88 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 95bb6170..2597e434 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -43,7 +43,6 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - const GLenum TempBufferTarget = GL_ARRAY_BUFFER; void BackendBufferObject::SyncToBackend(SharedPtr& stateBufferObject) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -116,7 +115,7 @@ namespace MobileGL::MG_Backend::DirectGLES { SizeT size = stateBufferObject->GetSize(); GLenum usage = MG_Util::ConvertBufferUsageToGLEnum(stateBufferObject->GetUsage()); - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); + Bind(); MG_External::GLES::glBufferData(TempBufferTarget, size, data, usage); } @@ -137,7 +136,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } for (const auto& range : ranges) { - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); + Bind(); MG_External::GLES::glBufferSubData(TempBufferTarget, range.start, range.end - range.start, reinterpret_cast(data) + range.start); } @@ -158,7 +157,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } SizeT minStart = ranges.GetOverallMinStart(); SizeT maxEnd = ranges.GetOverallMaxEnd(); - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); + Bind(); void* mappedData = MG_External::GLES::glMapBufferRange( TempBufferTarget, minStart, maxEnd - minStart, (invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) | (unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) | @@ -182,19 +181,6 @@ namespace MobileGL::MG_Backend::DirectGLES { return shared_from_this(); } - void BackendBufferObject::Bind() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - if (TempBufferTarget == GL_ARRAY_BUFFER) { - if (g_boundVertexBufferObject.get() == this) { - return; - } - g_boundVertexBufferObject = GetSharedPtr(); - } - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); - } - void BackendBufferObject::Bind(GLenum target) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -872,79 +858,73 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum* backendAttachmentToSync = nullptr; Int frontendAttachmentToSyncCount = 0; switch (asTarget) { - case FramebufferTarget::Draw: { - auto &stateDrawBuffers = stateFBOObject->GetDrawBuffers(); - - // Check if still clean, skip if clean - if (memcmp(m_frontendDrawBuffers, - stateDrawBuffers.data(), - FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { - break; - } - - // Create mappings for draw buffers - int nBuffers = 0; - std::fill(m_frontendDrawBuffers, - m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_backendDrawBuffers, - m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); - for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { - if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { - m_frontendDrawBuffers[i] = FramebufferAttachmentType::None; - continue; - } - - m_frontendDrawBuffers[i] = stateDrawBuffers[i]; - - // Create compacted mapping - m_backendDrawBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; - m_compactedFrontendDrawBuffers[nBuffers] = m_frontendDrawBuffers[i]; - nBuffers++; - } - MG_External::GLES::glDrawBuffers(nBuffers, m_backendDrawBuffers); - - frontendAttachmentToSync = m_compactedFrontendDrawBuffers; - backendAttachmentToSync = m_backendDrawBuffers; - frontendAttachmentToSyncCount = nBuffers; + case FramebufferTarget::Draw: { + auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); + // Check if still clean, skip if clean + if (memcmp(m_frontendDrawBuffers, stateDrawBuffers.data(), + FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { break; } - case FramebufferTarget::Read: { - auto frontendReadBuf = stateFBOObject->GetReadBuffer(); - if (frontendReadBuf == m_frontendReadBuffer) - break; - m_frontendReadBuffer = frontendReadBuf; - // For consistency, we need to find the compacted attachment index of this read buffer - auto it = std::find( - m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, - frontendReadBuf); - - Bool notFound = (it == m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); - if (notFound) { - MGLOG_D("%s: read buffer not found in draw buffer, use as in frontend", __func__); + // Create mappings for draw buffers + int nBuffers = 0; + std::fill(m_frontendDrawBuffers, m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + FramebufferAttachmentType::None); + std::fill(m_compactedFrontendDrawBuffers, + m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + FramebufferAttachmentType::None); + std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); + for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { + m_frontendDrawBuffers[i] = FramebufferAttachmentType::None; + continue; } - auto backendReadBuffer = notFound ? frontendReadBuf : *it; - GLenum glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(backendReadBuffer); - if (m_backendReadBuffer == glBackendReadBuffer) - break; - m_backendReadBuffer = glBackendReadBuffer; - MG_External::GLES::glReadBuffer(glBackendReadBuffer); - frontendAttachmentToSync = &m_frontendReadBuffer; - backendAttachmentToSync = &m_backendReadBuffer; - frontendAttachmentToSyncCount = 1; + m_frontendDrawBuffers[i] = stateDrawBuffers[i]; - break; + // Create compacted mapping + m_backendDrawBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; + m_compactedFrontendDrawBuffers[nBuffers] = m_frontendDrawBuffers[i]; + nBuffers++; } - default: - MOBILEGL_ASSERT(false, "%s: Unreachable!", __func__); - return; + MG_External::GLES::glDrawBuffers(nBuffers, m_backendDrawBuffers); + + frontendAttachmentToSync = m_compactedFrontendDrawBuffers; + backendAttachmentToSync = m_backendDrawBuffers; + frontendAttachmentToSyncCount = nBuffers; + + break; + } + case FramebufferTarget::Read: { + auto frontendReadBuf = stateFBOObject->GetReadBuffer(); + if (frontendReadBuf == m_frontendReadBuffer) break; + m_frontendReadBuffer = frontendReadBuf; + + // For consistency, we need to find the compacted attachment index of this read buffer + auto it = + std::find(m_compactedFrontendDrawBuffers, + m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendReadBuf); + + Bool notFound = (it == m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); + if (notFound) { + MGLOG_D("%s: read buffer not found in draw buffer, use as in frontend", __func__); + } + auto backendReadBuffer = notFound ? frontendReadBuf : *it; + GLenum glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(backendReadBuffer); + if (m_backendReadBuffer == glBackendReadBuffer) break; + m_backendReadBuffer = glBackendReadBuffer; + MG_External::GLES::glReadBuffer(glBackendReadBuffer); + + frontendAttachmentToSync = &m_frontendReadBuffer; + backendAttachmentToSync = &m_backendReadBuffer; + frontendAttachmentToSyncCount = 1; + + break; + } + default: + MOBILEGL_ASSERT(false, "%s: Unreachable!", __func__); + return; } // Sync texture/buffer to attachment @@ -952,7 +932,8 @@ namespace MobileGL::MG_Backend::DirectGLES { auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); for (Int i = 0; i < frontendAttachmentToSyncCount; ++i) { auto frontendAttachment = frontendAttachmentToSync[i]; - if (attachmentVersions[(SizeT)frontendAttachment] == m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { + if (attachmentVersions[(SizeT)frontendAttachment] == + m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { continue; } m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; @@ -965,10 +946,11 @@ namespace MobileGL::MG_Backend::DirectGLES { SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); } - FramebufferAttachmentType auxAtt[] = { FramebufferAttachmentType::Depth, FramebufferAttachmentType::Stencil }; - for (auto & att : auxAtt) { + FramebufferAttachmentType auxAtt[] = {FramebufferAttachmentType::Depth, FramebufferAttachmentType::Stencil}; + for (auto& att : auxAtt) { auto frontendAttachment = att; - if (attachmentVersions[(SizeT)frontendAttachment] == m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { + if (attachmentVersions[(SizeT)frontendAttachment] == + m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { continue; } m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 6268e30f..ca17d17a 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -17,13 +17,13 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { + const GLenum TempBufferTarget = GL_ARRAY_BUFFER; class BackendBufferObject : public std::enable_shared_from_this { public: BackendBufferObject(); void SyncToBackend(SharedPtr& stateBufferObject); Uint GetBackendBufferId() { return m_backendBufferId; } - void Bind(); - void Bind(GLenum target); + void Bind(GLenum target = TempBufferTarget); SharedPtr GetSharedPtr(); private: From 74509ae0a3c698192cfe788351598ae81c365117 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 3 Feb 2026 21:07:14 +0800 Subject: [PATCH 40/74] [Perf] (MG_Backend/DirectGLES): No need to use shared_ptr for g_boundVertexBufferObject. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 10 +++------- MobileGL/MG_Backend/DirectGLES/Managers.h | 5 ++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 2597e434..ce504f61 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -177,25 +177,21 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - SharedPtr BackendBufferObject::GetSharedPtr() { - return shared_from_this(); - } - void BackendBufferObject::Bind(GLenum target) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif if (target == GL_ARRAY_BUFFER) { - if (g_boundVertexBufferObject.get() == this) { + if (g_boundVertexBufferObject == this) { return; } - g_boundVertexBufferObject = GetSharedPtr(); + g_boundVertexBufferObject = this; } MG_External::GLES::glBindBuffer(target, m_backendBufferId); } UnorderedMap, SharedPtr> g_backendBufferObjects; - SharedPtr g_boundVertexBufferObject; + BackendBufferObject* g_boundVertexBufferObject = nullptr; } // namespace BufferImpl namespace VertexArrayImpl { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index ca17d17a..5ae73d4d 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -18,13 +18,12 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { const GLenum TempBufferTarget = GL_ARRAY_BUFFER; - class BackendBufferObject : public std::enable_shared_from_this { + class BackendBufferObject { public: BackendBufferObject(); void SyncToBackend(SharedPtr& stateBufferObject); Uint GetBackendBufferId() { return m_backendBufferId; } void Bind(GLenum target = TempBufferTarget); - SharedPtr GetSharedPtr(); private: void SyncToBackend_glBufferData(SharedPtr& stateBufferObject); @@ -37,7 +36,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Bool m_isInitialized = false; }; - extern SharedPtr g_boundVertexBufferObject; + extern BackendBufferObject* g_boundVertexBufferObject; extern UnorderedMap, SharedPtr> g_backendBufferObjects; } // namespace BufferImpl From 77378bbf07373d55284e76cd61c1fdfd5051ea6d Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 3 Feb 2026 23:43:55 +0800 Subject: [PATCH 41/74] [Fix] (MG_Backend/DirectGLES): fix renaming error in `BackendFramebufferObject::SyncToBackend` --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index ce504f61..7cbdd362 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -898,16 +898,20 @@ namespace MobileGL::MG_Backend::DirectGLES { m_frontendReadBuffer = frontendReadBuf; // For consistency, we need to find the compacted attachment index of this read buffer - auto it = - std::find(m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendReadBuf); + auto it = std::find(m_compactedFrontendDrawBuffers, + m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendReadBuf); + // TODO: what if sync ReadBuffer first then DrawBuffer? + GLenum glBackendReadBuffer = GL_NONE; Bool notFound = (it == m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); if (notFound) { MGLOG_D("%s: read buffer not found in draw buffer, use as in frontend", __func__); + glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendReadBuf); + } else { + MGLOG_D("%s: read buffer found in draw buffer, keep it consistent as in read buffers", __func__); + auto index = std::distance(m_compactedFrontendDrawBuffers, it); + glBackendReadBuffer = m_backendDrawBuffers[index]; } - auto backendReadBuffer = notFound ? frontendReadBuf : *it; - GLenum glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(backendReadBuffer); if (m_backendReadBuffer == glBackendReadBuffer) break; m_backendReadBuffer = glBackendReadBuffer; MG_External::GLES::glReadBuffer(glBackendReadBuffer); From 2088009c012c130003c4da6cc72a3bda6f316310 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 10:27:01 +0800 Subject: [PATCH 42/74] [Fix] (MG_Backend/DirectGLES): direct draw buffer mapping, fix up simple case --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 127 ++++---------------- 1 file changed, 21 insertions(+), 106 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 7cbdd362..8684b92a 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -850,116 +850,31 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget); Bind(asTarget); - FramebufferAttachmentType* frontendAttachmentToSync = nullptr; - GLenum* backendAttachmentToSync = nullptr; - Int frontendAttachmentToSyncCount = 0; - switch (asTarget) { - case FramebufferTarget::Draw: { - auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); - - // Check if still clean, skip if clean - if (memcmp(m_frontendDrawBuffers, stateDrawBuffers.data(), - FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { - break; - } - - // Create mappings for draw buffers - int nBuffers = 0; - std::fill(m_frontendDrawBuffers, m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, - FramebufferAttachmentType::None); - std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); - for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { - if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { - m_frontendDrawBuffers[i] = FramebufferAttachmentType::None; - continue; - } - - m_frontendDrawBuffers[i] = stateDrawBuffers[i]; - - // Create compacted mapping - m_backendDrawBuffers[nBuffers] = GL_COLOR_ATTACHMENT0 + nBuffers; - m_compactedFrontendDrawBuffers[nBuffers] = m_frontendDrawBuffers[i]; - nBuffers++; - } - MG_External::GLES::glDrawBuffers(nBuffers, m_backendDrawBuffers); - - frontendAttachmentToSync = m_compactedFrontendDrawBuffers; - backendAttachmentToSync = m_backendDrawBuffers; - frontendAttachmentToSyncCount = nBuffers; - - break; + // connect attachments (set buffers) + // TODO: remapping + auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); + Bool drawBufferDirty = false; + for (GLint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + auto currentBuf = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateDrawBuffers[i]); + if (m_backendDrawBuffers[i] != currentBuf) + drawBufferDirty = true; + m_backendDrawBuffers[i] = currentBuf; } - case FramebufferTarget::Read: { - auto frontendReadBuf = stateFBOObject->GetReadBuffer(); - if (frontendReadBuf == m_frontendReadBuffer) break; - m_frontendReadBuffer = frontendReadBuf; + if (drawBufferDirty) + MG_External::GLES::glDrawBuffers(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, m_backendDrawBuffers); - // For consistency, we need to find the compacted attachment index of this read buffer - auto it = std::find(m_compactedFrontendDrawBuffers, - m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendReadBuf); + auto currentReadBuf = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateFBOObject->GetReadBuffer()); + if (m_backendReadBuffer != currentReadBuf) + MG_External::GLES::glReadBuffer(m_backendReadBuffer); - // TODO: what if sync ReadBuffer first then DrawBuffer? - GLenum glBackendReadBuffer = GL_NONE; - Bool notFound = (it == m_compactedFrontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); - if (notFound) { - MGLOG_D("%s: read buffer not found in draw buffer, use as in frontend", __func__); - glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendReadBuf); - } else { - MGLOG_D("%s: read buffer found in draw buffer, keep it consistent as in read buffers", __func__); - auto index = std::distance(m_compactedFrontendDrawBuffers, it); - glBackendReadBuffer = m_backendDrawBuffers[index]; - } - if (m_backendReadBuffer == glBackendReadBuffer) break; - m_backendReadBuffer = glBackendReadBuffer; - MG_External::GLES::glReadBuffer(glBackendReadBuffer); + // attach texture to fbo + // TODO: attach according to remapped + const auto& attachments = stateFBOObject->GetAllAttachmentObjects(); + for (SizeT i = 0; i < attachments.size(); ++i) { + const auto& attachmentObject = attachments[i]; + FramebufferAttachmentType type = static_cast(i); + GLenum glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(type); - frontendAttachmentToSync = &m_frontendReadBuffer; - backendAttachmentToSync = &m_backendReadBuffer; - frontendAttachmentToSyncCount = 1; - - break; - } - default: - MOBILEGL_ASSERT(false, "%s: Unreachable!", __func__); - return; - } - - // Sync texture/buffer to attachment - const auto& attachmentObjects = stateFBOObject->GetAllAttachmentObjects(); - auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); - for (Int i = 0; i < frontendAttachmentToSyncCount; ++i) { - auto frontendAttachment = frontendAttachmentToSync[i]; - if (attachmentVersions[(SizeT)frontendAttachment] == - m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { - continue; - } - m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; - - const auto& attachmentObject = stateFBOObject->GetAttachment(frontendAttachment); - if (!attachmentObject.IsValid() || attachmentObject.IsEmpty()) { - continue; - } - auto glBackendAttachment = backendAttachmentToSync[i]; - SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); - } - - FramebufferAttachmentType auxAtt[] = {FramebufferAttachmentType::Depth, FramebufferAttachmentType::Stencil}; - for (auto& att : auxAtt) { - auto frontendAttachment = att; - if (attachmentVersions[(SizeT)frontendAttachment] == - m_syncedAttachmentVersions[(SizeT)frontendAttachment]) { - continue; - } - m_syncedAttachmentVersions[(SizeT)frontendAttachment] = attachmentVersions[(SizeT)frontendAttachment]; - - const auto& attachmentObject = stateFBOObject->GetAttachment(frontendAttachment); - if (!attachmentObject.IsValid() || attachmentObject.IsEmpty()) { - continue; - } - auto glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(att); SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); } } From f4ce17af51548efa351bf0426acf221da66b0092 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 10:42:23 +0800 Subject: [PATCH 43/74] [Perf] (MG_Backend/DirectGLES): skip redundant attachment object sync --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 8 +++++++- MobileGL/MG_Backend/DirectGLES/Managers.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 8684b92a..db099558 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -870,12 +870,18 @@ namespace MobileGL::MG_Backend::DirectGLES { // attach texture to fbo // TODO: attach according to remapped const auto& attachments = stateFBOObject->GetAllAttachmentObjects(); + const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); for (SizeT i = 0; i < attachments.size(); ++i) { const auto& attachmentObject = attachments[i]; FramebufferAttachmentType type = static_cast(i); + // should retrieve BACKEND attachment here GLenum glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(type); - SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); + // relevant FRONTEND!!! version should be checked and updated + if (m_syncedFrontendAttachmentVersions[i] != attachmentVersions[i]) { + SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); + m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i]; + } } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 5ae73d4d..b84c953b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -164,7 +164,7 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum m_backendReadBuffer = GL_COLOR_ATTACHMENT0; using FramebufferObject = MG_State::GLState::FramebufferObject; - FramebufferObject::FramebufferAttachmentVersionArray m_syncedAttachmentVersions = {0}; + FramebufferObject::FramebufferAttachmentVersionArray m_syncedFrontendAttachmentVersions = {0}; }; extern UnorderedMap, SharedPtr> From 1b47f1bb92be1609dfefd91eb066ecd928a40b81 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 14:42:48 +0800 Subject: [PATCH 44/74] [Fix] (MG_Backend/DirectGLES): re-implement FBO sync, attachment remapping --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 138 ++++++++---------- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 67 +++++++-- MobileGL/MG_Backend/DirectGLES/Managers.h | 14 +- 3 files changed, 114 insertions(+), 105 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 9693e6f8..c70ade8e 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -998,44 +998,26 @@ namespace MobileGL::MG_Backend::DirectGLES { RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); - auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( - MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); - if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { - MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); - return; - } - auto backendFBO = backendFBOIt->second; +// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( +// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); +// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { +// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); +// return; +// } +// auto backendFBO = backendFBOIt->second; GLint realDrawbuffer = drawbuffer; if (buffer == GL_COLOR) { - auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); +// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); return; } - FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; - - if (attachmentType == FramebufferAttachmentType::None) { - MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); - return; - } - - Bool found = false; - for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { - if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { - realDrawbuffer = i; - found = true; - break; - } - } - - if (!found) { - MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); - return; - } + // since `drawbuffer` indicates an index into draw buffer array, + // using GL value directly is OK. } else if (buffer == GL_DEPTH || buffer == GL_STENCIL) { if (drawbuffer != 0) { MGLOG_W("Depth/stencil clear buffer index must be 0, got %d. Using 0.", drawbuffer); @@ -1051,44 +1033,44 @@ namespace MobileGL::MG_Backend::DirectGLES { RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); - auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( - MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); - if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { - MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); - return; - } - auto backendFBO = backendFBOIt->second; +// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( +// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); +// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { +// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); +// return; +// } +// auto backendFBO = backendFBOIt->second; GLint realDrawbuffer = drawbuffer; if (buffer == GL_COLOR) { - auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); +// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); return; } - FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; +// FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; - if (attachmentType == FramebufferAttachmentType::None) { - MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); - return; - } +// if (attachmentType == FramebufferAttachmentType::None) { +// MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); +// return; +// } - Bool found = false; - for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { - if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { - realDrawbuffer = i; - found = true; - break; - } - } +// Bool found = false; +// for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { +// if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { +// realDrawbuffer = i; +// found = true; +// break; +// } +// } - if (!found) { - MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); - return; - } +// if (!found) { +// MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); +// return; +// } } else if (buffer == GL_STENCIL) { if (drawbuffer != 0) { MGLOG_W("Stencil clear buffer index must be 0, got %d. Using 0.", drawbuffer); @@ -1105,44 +1087,44 @@ namespace MobileGL::MG_Backend::DirectGLES { RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); - auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( - MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); - if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { - MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); - return; - } - auto backendFBO = backendFBOIt->second; +// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( +// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); +// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { +// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); +// return; +// } +// auto backendFBO = backendFBOIt->second; GLint realDrawbuffer = drawbuffer; if (buffer == GL_COLOR) { - auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); +// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); return; } - FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; +// FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; - if (attachmentType == FramebufferAttachmentType::None) { - MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); - return; - } +// if (attachmentType == FramebufferAttachmentType::None) { +// MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); +// return; +// } - Bool found = false; - for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { - if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { - realDrawbuffer = i; - found = true; - break; - } - } +// Bool found = false; +// for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { +// if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { +// realDrawbuffer = i; +// found = true; +// break; +// } +// } - if (!found) { - MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); - return; - } +// if (!found) { +// MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); +// return; +// } } else { MGLOG_E("ClearBufferuiv can only be used with GL_COLOR buffer, got %s", MG_Util::ConvertGLEnumToString(buffer).c_str()); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index db099558..a5202a91 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -850,24 +850,47 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum glFBOTarget = MG_Util::ConvertFramebufferTargetToGLEnum(asTarget); Bind(asTarget); - // connect attachments (set buffers) - // TODO: remapping + // -------------------- Connect attachments (set buffers) ----------------------- + // 1. Remap draw buffers auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); - Bool drawBufferDirty = false; - for (GLint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) { - auto currentBuf = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateDrawBuffers[i]); - if (m_backendDrawBuffers[i] != currentBuf) - drawBufferDirty = true; - m_backendDrawBuffers[i] = currentBuf; + Bool drawBufferClean = false; + if (memcmp(m_frontendDrawBuffers, stateDrawBuffers.data(), + FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { + drawBufferClean = true; } - if (drawBufferDirty) - MG_External::GLES::glDrawBuffers(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS, m_backendDrawBuffers); - auto currentReadBuf = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(stateFBOObject->GetReadBuffer()); - if (m_backendReadBuffer != currentReadBuf) - MG_External::GLES::glReadBuffer(m_backendReadBuffer); + if (!drawBufferClean) { + memcpy(m_frontendDrawBuffers, stateDrawBuffers.data(), + FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)); + std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); + int nEffectiveBuffers = 0; + for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { + if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { + m_backendDrawBuffers[i] = GL_NONE; + continue; + } - // attach texture to fbo + // Create compacted mapping + m_backendDrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; + nEffectiveBuffers = i + 1; + } + MG_External::GLES::glDrawBuffers(nEffectiveBuffers, m_backendDrawBuffers); + } + + // 2. Remap read buffer + auto frontendReadBuf = stateFBOObject->GetReadBuffer(); + if (frontendReadBuf != m_frontendReadBuffer) { + m_frontendReadBuffer = frontendReadBuf; + + GLenum glBackendReadBuffer = GetBackendAttachmentType(frontendReadBuf); + + if (m_backendReadBuffer != glBackendReadBuffer) { + m_backendReadBuffer = glBackendReadBuffer; + MG_External::GLES::glReadBuffer(glBackendReadBuffer); + } + } + + // -------------------- Attach texture to backend FBO ----------------------- // TODO: attach according to remapped const auto& attachments = stateFBOObject->GetAllAttachmentObjects(); const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); @@ -885,8 +908,20 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - FramebufferAttachmentType BackendFramebufferObject::GetCompactedAttachmentTypeAtDrawBufferIndex(Int index) { - return m_compactedFrontendDrawBuffers[index]; + GLenum BackendFramebufferObject::GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const { + GLenum glBackendReadBuffer = GL_NONE; + auto it = std::find(m_frontendDrawBuffers, + m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendAtt); + Bool notFound = (it == m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); + if (notFound) { + MGLOG_D("%s: frontendAtt not found in draw buffer (probably not remapped), just use the same as frontend", __func__); + glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendAtt); + } else { + MGLOG_D("%s: frontendAtt found in draw buffer, keep it consistent as in read buffers", __func__); + auto index = std::distance(m_frontendDrawBuffers, it); + glBackendReadBuffer = m_backendDrawBuffers[index]; + } + return glBackendReadBuffer; } UnorderedMap, SharedPtr> diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index b84c953b..45185474 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -133,8 +133,8 @@ namespace MobileGL::MG_Backend::DirectGLES { bool SyncAttachmentObject(GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, GLenum glBackendAttachment); - FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); - +// FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); + GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const; private: Uint m_backendFBOId = 0; @@ -146,17 +146,9 @@ namespace MobileGL::MG_Backend::DirectGLES { */ FramebufferAttachmentType m_frontendDrawBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = { FramebufferAttachmentType::None}; - /* this will save buffers in its compacted GL form, - not consecutive is not allowed - i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT5, COLOR_ATTACHMENT4] - (no GL_NONE among those) - */ - FramebufferAttachmentType - m_compactedFrontendDrawBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = { - FramebufferAttachmentType::None}; /* this will save buffers in stricter ES rules reversion, absence or not consecutive are not allowed, according to ES spec - i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT1, NONE, NONE, ...] + i.e. it could be like [COLOR_ATTACHMENT0, COLOR_ATTACHMENT1, NONE, COLOR_ATTACHMENT3, ...] this array could be provided as data directly to ES `glDrawBuffers` function */ GLenum m_backendDrawBuffers[MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS] = {GL_NONE}; From 5bfb55cfd8724a0d807fd20836934f3aea014175 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 14:44:41 +0800 Subject: [PATCH 45/74] [Fix] (MG_Backend/DirectGLES): re-implement glClearBuffer* --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 121 +----------------- 1 file changed, 3 insertions(+), 118 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index c70ade8e..a0bd80fb 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -998,87 +998,15 @@ namespace MobileGL::MG_Backend::DirectGLES { RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); -// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( -// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); -// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { -// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); -// return; -// } -// auto backendFBO = backendFBOIt->second; - GLint realDrawbuffer = drawbuffer; - - if (buffer == GL_COLOR) { -// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); - - if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { - MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); - return; - } - - // since `drawbuffer` indicates an index into draw buffer array, - // using GL value directly is OK. - } else if (buffer == GL_DEPTH || buffer == GL_STENCIL) { - if (drawbuffer != 0) { - MGLOG_W("Depth/stencil clear buffer index must be 0, got %d. Using 0.", drawbuffer); - } - realDrawbuffer = 0; - } - - MG_External::GLES::glClearBufferfv(buffer, realDrawbuffer, value); + MG_External::GLES::glClearBufferfv(buffer, drawbuffer, value); } void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) { TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); RenderStateImpl::SyncRenderState(); - BindCurrentFBO(FramebufferTarget::Draw); -// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( -// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); -// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { -// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); -// return; -// } -// auto backendFBO = backendFBOIt->second; - - GLint realDrawbuffer = drawbuffer; - - if (buffer == GL_COLOR) { -// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); - - if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { - MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); - return; - } - -// FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; - -// if (attachmentType == FramebufferAttachmentType::None) { -// MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); -// return; -// } - -// Bool found = false; -// for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { -// if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { -// realDrawbuffer = i; -// found = true; -// break; -// } -// } - -// if (!found) { -// MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); -// return; -// } - } else if (buffer == GL_STENCIL) { - if (drawbuffer != 0) { - MGLOG_W("Stencil clear buffer index must be 0, got %d. Using 0.", drawbuffer); - } - realDrawbuffer = 0; - } - - MG_External::GLES::glClearBufferiv(buffer, realDrawbuffer, value); + MG_External::GLES::glClearBufferiv(buffer, drawbuffer, value); } void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) { @@ -1087,51 +1015,8 @@ namespace MobileGL::MG_Backend::DirectGLES { RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); -// auto backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find( -// MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()); -// if (backendFBOIt == FramebufferImpl::g_backendFramebufferObjects.end()) { -// MGLOG_E("No backend FBO found for current draw FBO, cannot clear buffer."); -// return; -// } -// auto backendFBO = backendFBOIt->second; - GLint realDrawbuffer = drawbuffer; - - if (buffer == GL_COLOR) { -// auto& stateDrawBuffers = backendFBOIt->first->GetDrawBuffers(); - - if (drawbuffer < 0 || drawbuffer >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { - MGLOG_E("Invalid drawbuffer index: %d", drawbuffer); - return; - } - -// FramebufferAttachmentType attachmentType = stateDrawBuffers[drawbuffer]; - -// if (attachmentType == FramebufferAttachmentType::None) { -// MGLOG_D("Drawbuffer %d has no attachment, skipping clear", drawbuffer); -// return; -// } - -// Bool found = false; -// for (int i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; i++) { -// if (backendFBO->GetCompactedAttachmentTypeAtDrawBufferIndex(i) == attachmentType) { -// realDrawbuffer = i; -// found = true; -// break; -// } -// } - -// if (!found) { -// MGLOG_E("Failed to find backend drawbuffer for attachment type: %d", static_cast(attachmentType)); -// return; -// } - } else { - MGLOG_E("ClearBufferuiv can only be used with GL_COLOR buffer, got %s", - MG_Util::ConvertGLEnumToString(buffer).c_str()); - return; - } - - MG_External::GLES::glClearBufferuiv(buffer, realDrawbuffer, value); + MG_External::GLES::glClearBufferuiv(buffer, drawbuffer, value); } } // namespace MobileGL::MG_Backend::DirectGLES From fd5cc2e5c10449ee3c8bbeb2ddc6ecd1c989655b Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 15:34:41 +0800 Subject: [PATCH 46/74] [Feat] (MG_Util/Converters): add symbolic constants for default FBO into `ConvertGLEnumToFramebufferAttachmentType` --- .../Converters/GLToMG/FramebufferEnumConverter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp index 2336dd12..4f094c0f 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp @@ -36,6 +36,14 @@ namespace MobileGL { return FramebufferAttachmentType::Depth; case GL_STENCIL_ATTACHMENT: return FramebufferAttachmentType::Stencil; + case GL_FRONT_LEFT: + return FramebufferAttachmentType::FrontLeft; + case GL_FRONT_RIGHT: + return FramebufferAttachmentType::FrontRight; + case GL_BACK_LEFT: + return FramebufferAttachmentType::BackLeft; + case GL_BACK_RIGHT: + return FramebufferAttachmentType::BackRight; default: return FramebufferAttachmentType::Unknown; } From cb27ebc576b65ece6f9bfa6e8a6900b354a2c160 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 15:51:16 +0800 Subject: [PATCH 47/74] [Feat] (MG_Util/Converters): add symbolic constants for default FBO into `ConvertFramebufferAttachmentTypeToGLEnum` --- .../MGToGL/FramebufferEnumConverter.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp index e9a6855e..18bced6d 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp @@ -29,12 +29,20 @@ namespace MobileGL { } switch (type) { - case FramebufferAttachmentType::Depth: - return GL_DEPTH_ATTACHMENT; - case FramebufferAttachmentType::Stencil: - return GL_STENCIL_ATTACHMENT; - default: - return GL_NONE; + case FramebufferAttachmentType::Depth: + return GL_DEPTH_ATTACHMENT; + case FramebufferAttachmentType::Stencil: + return GL_STENCIL_ATTACHMENT; + case FramebufferAttachmentType::FrontLeft: + return GL_FRONT_LEFT; + case FramebufferAttachmentType::FrontRight: + return GL_FRONT_RIGHT; + case FramebufferAttachmentType::BackLeft: + return GL_BACK_LEFT; + case FramebufferAttachmentType::BackRight: + return GL_BACK_RIGHT; + default: + return GL_NONE; } } From dfaa2c259b2a72c5593a5c97a14b492a8487fd93 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 16:19:57 +0800 Subject: [PATCH 48/74] [Fix] (MG_State/Framebuffer): bump attachmentVersions on SetDrawBuffer --- MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 294e9aec..5aa799ee 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -143,6 +143,7 @@ namespace MobileGL { void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { if (m_drawBuffers[index] == buffer) return; m_drawBuffers[index] = buffer; + ++m_attachmentVersions[static_cast(buffer)]; } const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const { From d13a19126bb02c7e587102c3f8a8dd244d269e53 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 16:20:51 +0800 Subject: [PATCH 49/74] [Fix] (MG_Backend/DirectGLES): properly handle default fbo's attachment? --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 24 +++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index a5202a91..28d188f1 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -865,13 +865,22 @@ namespace MobileGL::MG_Backend::DirectGLES { std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); int nEffectiveBuffers = 0; for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { - if (stateDrawBuffers[i] == FramebufferAttachmentType::None) { + auto frontendBuf = stateDrawBuffers[i]; + if (frontendBuf == FramebufferAttachmentType::None) { m_backendDrawBuffers[i] = GL_NONE; continue; } // Create compacted mapping - m_backendDrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; + if (frontendBuf == FramebufferAttachmentType::FrontLeft || + frontendBuf == FramebufferAttachmentType::FrontRight || + frontendBuf == FramebufferAttachmentType::BackLeft || + frontendBuf == FramebufferAttachmentType::BackRight) { + MGLOG_D("%s: frontend buf token found for default fbo, shouldn't remap", __func__); + m_backendDrawBuffers[i] = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendBuf); + } else { + m_backendDrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; + } nEffectiveBuffers = i + 1; } MG_External::GLES::glDrawBuffers(nEffectiveBuffers, m_backendDrawBuffers); @@ -891,14 +900,17 @@ namespace MobileGL::MG_Backend::DirectGLES { } // -------------------- Attach texture to backend FBO ----------------------- - // TODO: attach according to remapped const auto& attachments = stateFBOObject->GetAllAttachmentObjects(); const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); for (SizeT i = 0; i < attachments.size(); ++i) { const auto& attachmentObject = attachments[i]; - FramebufferAttachmentType type = static_cast(i); - // should retrieve BACKEND attachment here - GLenum glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(type); + FramebufferAttachmentType frontendType = static_cast(i); + GLenum glBackendAttachment = GL_NONE; + if (frontendType >= FramebufferAttachmentType::Color0 && + frontendType <= FramebufferAttachmentType::Color31) + glBackendAttachment = GetBackendAttachmentType(frontendType); + else + glBackendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendType); // relevant FRONTEND!!! version should be checked and updated if (m_syncedFrontendAttachmentVersions[i] != attachmentVersions[i]) { From 07638b8577c3c8f65a0b64a1c62f8c5bfcb68339 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Wed, 4 Feb 2026 17:45:08 +0800 Subject: [PATCH 50/74] [Fix] (MG_Backend/DirectGLES): Fix incorrect active texture unit when binding not dirty. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 28d188f1..a6a2cff8 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -335,10 +335,13 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + if (g_activeTextureUnit != unit) { + ActivateTextureUnit(unit); + } + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); if (this == g_boundTexturesCache[unit][targetN]) return; - ActivateTextureUnit(unit); MG_External::GLES::glBindTexture(target, m_backendTextureId); g_boundTexturesCache[unit][targetN] = this; } @@ -760,10 +763,13 @@ namespace MobileGL::MG_Backend::DirectGLES { } void UnbindTexture(Uint unit, GLenum target) { // Active unit will be modified + if (unit != g_activeTextureUnit) { + ActivateTextureUnit(unit); + } + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); if (g_boundTexturesCache[unit][targetN] == nullptr) return; - ActivateTextureUnit(unit); MG_External::GLES::glBindTexture(target, 0); g_boundTexturesCache[unit][targetN] = nullptr; } @@ -922,11 +928,13 @@ namespace MobileGL::MG_Backend::DirectGLES { GLenum BackendFramebufferObject::GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const { GLenum glBackendReadBuffer = GL_NONE; - auto it = std::find(m_frontendDrawBuffers, - m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, frontendAtt); + auto it = std::find(m_frontendDrawBuffers, m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, + frontendAtt); Bool notFound = (it == m_frontendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS); if (notFound) { - MGLOG_D("%s: frontendAtt not found in draw buffer (probably not remapped), just use the same as frontend", __func__); + MGLOG_D( + "%s: frontendAtt not found in draw buffer (probably not remapped), just use the same as frontend", + __func__); glBackendReadBuffer = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendAtt); } else { MGLOG_D("%s: frontendAtt found in draw buffer, keep it consistent as in read buffers", __func__); From 6577e1bcc13065aaf115846c22bf6c2401d753dc Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 22:01:44 +0800 Subject: [PATCH 51/74] [Feat] (MG_Util/Converters): add depth formats to `ConvertInternalFormatToSized` --- .../MGToMG/TextureEnumConverter.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp index 6bb12ac4..d5f00ddb 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp @@ -183,6 +183,36 @@ namespace MobileGL { return internalformat; } } + case TextureInternalFormat::DepthComponent: { + switch (type) { + case TexturePixelDataType::UnsignedShort: + return TextureInternalFormat::DepthComponent16; + case TexturePixelDataType::UnsignedInt: + return TextureInternalFormat::DepthComponent32; + case TexturePixelDataType::Float: + return TextureInternalFormat::DepthComponent32F; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } + case TextureInternalFormat::DepthStencil: { + switch (type) { + case TexturePixelDataType::UnsignedInt248: + return TextureInternalFormat::Depth24Stencil8; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; + } + } default: { MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning " "original.", From de8342a96b3d6e5ed22e90f76c4ee0187c11305e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 4 Feb 2026 22:50:01 +0800 Subject: [PATCH 52/74] Revert "[Optimize] (MG_Backend/DirectGLES): Texture object mipmap dirty bit" This reverts commit 8f551794 --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 2 -- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 23 +++++++------------ MobileGL/MG_Backend/DirectGLES/Managers.h | 1 - .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 1 - .../GLState/TextureState/TextureObject.cpp | 19 --------------- .../GLState/TextureState/TextureObject.h | 17 +------------- .../TextureState/TextureObject2DCube.cpp | 14 ----------- .../TextureState/TextureObject2DCube.h | 1 - .../TextureState/TextureObjectBuffer.cpp | 8 ------- .../TextureState/TextureObjectBuffer.h | 2 -- MobileGL/MG_Util/Types.h | 10 -------- 11 files changed, 9 insertions(+), 89 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index a0bd80fb..20490d7c 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -273,8 +273,6 @@ namespace MobileGL::MG_Backend::DirectGLES { backendFBOObject->SyncToBackend(currentFBO, target); } - // backendFBOObject->Bind(target); - lastUpdatedFBO = currentFBO.get(); } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index a6a2cff8..919769fc 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -360,11 +360,6 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - if (!stateTextureObject->CheckDirtyBit(MG_State::GLState::TextureDirtyBit::StorageDirtyBit)) { - MGLOG_D("Texture parameters changed but storage is not dirty, skipping mipmap sync for texture ID: %u", - m_backendTextureId); - return; - } #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -396,7 +391,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } Bind(target); - + errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { + MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); const auto baseSize = stateTextureObject->GetBaseSize(); StateTextureBasicInfo currentTextureInfo = {stateTextureObject->GetFormat(), static_cast(baseSize.x()), @@ -436,11 +433,11 @@ namespace MobileGL::MG_Backend::DirectGLES { auto* pData = (levelDirty && levelByteSize != 0) ? textureMipmapObject->MapMipmapData(uploadTarget, level) : nullptr; - MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, " - "levelDirty = %s", - __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, - levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, - levelDirty ? "true" : "false"); + MGLOG_D( + "%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, levelDirty = %s", + __func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), level, + levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, + levelDirty ? "true" : "false"); errorLopper.Clear(); MG_External::GLES::glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); @@ -525,8 +522,6 @@ namespace MobileGL::MG_Backend::DirectGLES { } } } - - textureMipmapObject->ClearAllStorageDirtyBit(); break; } case TextureStorageType::Buffer: { @@ -562,8 +557,6 @@ namespace MobileGL::MG_Backend::DirectGLES { &glType); MG_External::GLES::glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); - - textureBufferObject->ClearAllStorageDirtyBit(); break; } default: diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 45185474..b964ac01 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -9,7 +9,6 @@ #pragma once #include #include "DirectGLES.h" -#include "Utils.h" #include "MG_State/GLState/SamplerState/SamplerObject.h" #include "MG_State/GLState/TextureState/TextureEnum.h" #include diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 61a9d9f3..911fca93 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -788,7 +788,6 @@ namespace MobileGL { auto* texBufferObject = static_cast(textureObject.get()); auto& bufferSlot = texBufferObject->GetBufferBindingSlot(); bufferSlot.Bind(bufferObject); - texBufferObject->SetStorageDirtyBit(); texBufferObject->SetInternalFormat(textureInternalFormat); } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index d2e23714..aaac2c01 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -135,14 +135,6 @@ namespace MobileGL { ++m_textureParamsVersion; } - Bool TextureObjectBase::IsDirty() const { - return m_dirtyBit; - } - - Bool TextureObjectBase::CheckDirtyBit(TextureDirtyBit bit) const { - return m_dirtyBit & bit; - } - Uint16 TextureObjectBase::GetTextureParamsVersion() const { return m_textureParamsVersion; } @@ -178,9 +170,6 @@ namespace MobileGL { void TextureObjectWithOneMipmap::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); - if (dirty) { - m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); - } } Bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { @@ -224,14 +213,6 @@ namespace MobileGL { return true; } - void TextureObjectWithOneMipmap::ClearAllStorageDirtyBit() { - auto levelCount = GetMipmapLevelCount(); - for (Uint i = 0; i < levelCount; ++i) { - m_textureStorage.MarkDirty(0, i, false); - } - m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); - } - // TODO: add other texture types as needed } // namespace GLState diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.h b/MobileGL/MG_State/GLState/TextureState/TextureObject.h index a094be60..cef2bde3 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -17,12 +17,6 @@ namespace MobileGL { namespace MG_State { namespace GLState { - enum class TextureDirtyBit : Uint8 { - None = 0, - StorageDirtyBit = 1 << 0, // refers to mipmap or TexBuffer according to tex type - ParamDirtyBit = 1 << 1 - }; - class ITextureObject { public: using TargetEnum = TextureTarget; @@ -47,8 +41,6 @@ namespace MobileGL { virtual const UintVec2& GetLevelRange() const = 0; virtual void SetBaseLevel(Uint baseLevel) = 0; virtual void SetMaxLevel(Uint maxLevel) = 0; - virtual Bool IsDirty() const = 0; - virtual Bool CheckDirtyBit(TextureDirtyBit bit) const = 0; virtual Uint16 GetTextureParamsVersion() const = 0; protected: @@ -76,11 +68,7 @@ namespace MobileGL { const UintVec2& GetLevelRange() const override; void SetBaseLevel(Uint baseLevel) override; void SetMaxLevel(Uint maxLevel) override; - Bool IsDirty() const override; - Bool CheckDirtyBit(TextureDirtyBit bit) const override; Uint16 GetTextureParamsVersion() const override; - virtual void ClearAllStorageDirtyBit() = 0; - protected: const Uint m_externalIndex; const TextureTarget m_target = TextureTarget::Unknown; @@ -91,8 +79,6 @@ namespace MobileGL { TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; UintVec2 m_levelRange = {0, 1000}; Uint16 m_textureParamsVersion = 0; - - Flags m_dirtyBit = TextureDirtyBit::None; }; class TextureObjectMipmap : public TextureObjectBase { @@ -126,8 +112,7 @@ namespace MobileGL { void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) override; void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override; void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) override; - Bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; - void ClearAllStorageDirtyBit() override; + bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; IntVec3 GetBaseSize() const override; Bool IsComplete() const override; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp index e7eea174..8ed9113b 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp @@ -42,9 +42,6 @@ namespace MobileGL { void TextureObject2DCube::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); - if (dirty) { - m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); - } } bool TextureObject2DCube::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { @@ -85,17 +82,6 @@ namespace MobileGL { // TODO: add more completeness checks based on texture type and mipmap levels return true; } - - void TextureObject2DCube::ClearAllStorageDirtyBit() { - auto uploadTargetCount = GetUploadTargets().size(); - auto levelCount = GetMipmapLevelCount(); - for (SizeT target = 0; target < uploadTargetCount; ++target) { - for (Uint level = 0; level < levelCount; ++level) { - m_textureStorage.MarkDirty(target, level, false); - } - } - m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); - } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h index a7f8abe9..2d97a8b2 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h @@ -26,7 +26,6 @@ namespace MobileGL { void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override; void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override; bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; - void ClearAllStorageDirtyBit() override; IntVec3 GetBaseSize() const override; Bool IsComplete() const override; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp index 481e9760..4cb4f712 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp @@ -23,14 +23,6 @@ namespace MobileGL { MOBILEGL_ASSERT(target == TextureUploadTarget::TextureBuffer, "Invalid TextureUploadTarget!"); return m_bufferBindingSlot; } - - void TextureObjectBuffer::ClearAllStorageDirtyBit() { - m_dirtyBit.Clear(TextureDirtyBit::StorageDirtyBit); - } - - void TextureObjectBuffer::SetStorageDirtyBit() { - m_dirtyBit.Set(TextureDirtyBit::StorageDirtyBit); - } } // namespace GLState } // namespace MG_State } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h index c2b4badf..f5d7928e 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h @@ -20,8 +20,6 @@ namespace MobileGL { const Vector& GetUploadTargets() const override { return m_uploadTargets; } BindingSlot& GetBufferBindingSlot( TextureUploadTarget target = TextureUploadTarget::TextureBuffer); - void ClearAllStorageDirtyBit() override; - void SetStorageDirtyBit(); protected: Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override; diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index ec390275..725be14d 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -281,16 +281,6 @@ namespace MobileGL { Flags(typename Underlying::type b) : flags(b) {} - Flags Set(const Bit b) { - flags |= static_cast(b); - return flags; - } - - Flags Clear(const Bit b) { - flags &= ~static_cast(b); - return flags; - } - // Flags - Bit Flags operator|(const Bit b) const { return Flags(flags | static_cast(b)); } From 319806c48a3c2a5473d0546b2ae877f7ec34f8d7 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 00:55:42 +0800 Subject: [PATCH 53/74] [Chore] (CMake): Support using check_c*_compiler_flag to detect LTO capability. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b587c7e0..fee592ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,10 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug" OR MOBILEGL_FORCE_RELEASE_OPT) check_ipo_supported(RESULT LTOSupported OUTPUT LTOError) - if (LTOSupported) + check_c_compiler_flag("-flto" HAS_LTO_C) + check_cxx_compiler_flag("-flto" HAS_LTO_CXX) + + if (LTOSupported OR (HAS_LTO_C AND HAS_LTO_CXX)) # Check ThinLTO check_c_compiler_flag("-flto=thin" HAS_THINLTO_C) check_cxx_compiler_flag("-flto=thin" HAS_THINLTO_CXX) From 6855ae42ef80c2df13c5778ec827d8c1425febe0 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 10:34:24 +0800 Subject: [PATCH 54/74] [Feat] (MG_Backend/DirectGLES): add assertion checks against FBO cache --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 919769fc..9242fae4 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -916,6 +917,48 @@ namespace MobileGL::MG_Backend::DirectGLES { SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i]; } +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG + else { + MGLOG_D("%s: Skipped SyncAttachmentObject(target=%s, frontendObj=(%dx%dx%d, %s), backendAtt=%s)", __func__, + MG_Util::ConvertGLEnumToString(glFBOTarget).c_str(), + attachmentObject.GetSize().x(), attachmentObject.GetSize().y(), attachmentObject.GetSize().z(), + MG_Util::ConvertFramebufferAttachmentTypeToString(frontendType).c_str(), + MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str() + ); + GLint objectType = GL_NONE; + MG_External::GLES::glGetFramebufferAttachmentParameteriv(glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); + MOBILEGL_ASSERT( + (objectType == GL_NONE) || + (attachmentObject.IsTexture() && objectType == GL_TEXTURE) || (attachmentObject.IsRenderbuffer() && objectType == GL_RENDERBUFFER), + "Attachment type not match!"); + GLint objectName = 0; + MG_External::GLES::glGetFramebufferAttachmentParameteriv(glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectName); + // Verify that the backend object's name and parameters match the frontend attachment state + if (attachmentObject.IsTexture()) { + const auto& textureObject = attachmentObject.GetTexture(); + auto backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + MOBILEGL_ASSERT(backendTextureIt != TextureImpl::g_backendTextureObjects.end(), + "No backend texture found while framebuffer reports texture attachment."); + GLuint backendTexId = backendTextureIt->second->GetBackendTextureId(); + MOBILEGL_ASSERT(static_cast(backendTexId) == objectName, + "Attachment texture name mismatch between GLES and state object."); + + GLint texLevel = 0; + MG_External::GLES::glGetFramebufferAttachmentParameteriv( + glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &texLevel); + MOBILEGL_ASSERT(texLevel == static_cast(attachmentObject.GetTextureLevel()), + "Attachment texture level mismatch between GLES and state object."); + } else if (attachmentObject.IsRenderbuffer()) { + const auto& renderbufferObject = attachmentObject.GetRenderbuffer(); + auto backendRboIt = RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); + MOBILEGL_ASSERT(backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(), + "No backend renderbuffer found while framebuffer reports renderbuffer attachment."); + GLuint backendRboId = backendRboIt->second->GetBackendRenderbufferId(); + MOBILEGL_ASSERT(static_cast(backendRboId) == objectName, + "Attachment renderbuffer name mismatch between GLES and state object."); + } + } +#endif } } From 4bfa8ce934a80c83105297032f1143199a0339b4 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 12:20:06 +0800 Subject: [PATCH 55/74] [Fix] (MG_State/Framebuffer): properly bump FBO version --- .../MG_State/GLState/FramebufferState/FramebufferObject.cpp | 2 +- MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 5aa799ee..c89d064c 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -143,7 +143,7 @@ namespace MobileGL { void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { if (m_drawBuffers[index] == buffer) return; m_drawBuffers[index] = buffer; - ++m_attachmentVersions[static_cast(buffer)]; + BumpAttachmentVersion(buffer); } const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const { diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index b1cdd071..2b83bffa 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -128,7 +128,6 @@ namespace MobileGL { private: void BumpAttachmentVersion(FramebufferAttachmentType type); - void BumpBufferAttachVersion(); const Uint m_externalIndex = 0; FramebufferAttachmentObjectArray m_attachmentObjects; From e351ef9f412c0fbc07964173124a5b438ca89cfb Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 12:20:42 +0800 Subject: [PATCH 56/74] [Fix] (MG_Impl/Texture): mark dirty only when pixel transfer happens --- MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 911fca93..ba104d13 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -714,9 +714,6 @@ namespace MobileGL { MGLOG_D("%s: Allocating %d bytes at mip %d", __func__, internalBytes, level); textureMipmapObject->AllocateStorage(textureUploadingTarget, level, {{width, height, 1}, internalBytes}); - MGLOG_D("%s: mark mip %d as dirty", __func__, level); - textureMipmapObject->MarkStorageDirty(textureUploadingTarget, level, true); - if (!originalPixels) { MGLOG_D("%s: No input pixel and no PBO bound, no pixel transfer", __func__); return; @@ -740,6 +737,9 @@ namespace MobileGL { } free(processedPixels); + + MGLOG_D("%s: mark mip %d as dirty", __func__, level); + textureMipmapObject->MarkStorageDirty(textureUploadingTarget, level, true); } void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, From 5319367869258fcd8c4c4569d9b1ed83c53231af Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 12:21:32 +0800 Subject: [PATCH 57/74] [Feat] (MG_Backend/DirectGLES): assertion to check FBO cache integrity --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 9242fae4..05dfa17e 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -807,7 +807,7 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& textureObject = attachmentObject.GetTexture(); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { - MGLOG_E("ReadBuffer: No backend texture found for FBO attachment, cannot bind texture."); + MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__); return false; } const auto& backendTextureObject = backendTextureIt->second; @@ -919,11 +919,12 @@ namespace MobileGL::MG_Backend::DirectGLES { } #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG else { - MGLOG_D("%s: Skipped SyncAttachmentObject(target=%s, frontendObj=(%dx%dx%d, %s), backendAtt=%s)", __func__, + MGLOG_D("%s: Skipped SyncAttachmentObject(target=%s, frontendObj=(%dx%dx%d, %s), backendAtt=%s), version = %u", __func__, MG_Util::ConvertGLEnumToString(glFBOTarget).c_str(), attachmentObject.GetSize().x(), attachmentObject.GetSize().y(), attachmentObject.GetSize().z(), MG_Util::ConvertFramebufferAttachmentTypeToString(frontendType).c_str(), - MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str() + MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str(), + m_syncedFrontendAttachmentVersions[i] ); GLint objectType = GL_NONE; MG_External::GLES::glGetFramebufferAttachmentParameteriv(glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); @@ -941,7 +942,8 @@ namespace MobileGL::MG_Backend::DirectGLES { "No backend texture found while framebuffer reports texture attachment."); GLuint backendTexId = backendTextureIt->second->GetBackendTextureId(); MOBILEGL_ASSERT(static_cast(backendTexId) == objectName, - "Attachment texture name mismatch between GLES and state object."); + "Attachment texture name mismatch between GLES (%d) and backend texture object (%d), frontend texture object ID=%d.", + objectName, backendTexId, textureObject->GetExternalIndex()); GLint texLevel = 0; MG_External::GLES::glGetFramebufferAttachmentParameteriv( From 8e962345e4305aeb0e1fd03ce77719904913741c Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 12:22:11 +0800 Subject: [PATCH 58/74] [Fix] (MG_Backend/DirectGLES): remove depth tex copy codepath, fixing FBO state leak --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 20490d7c..291fa438 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -864,33 +864,8 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); } else { - MGLOG_D("%s: Backend depth", __func__); - MG_External::GLES::glTexImage2D(target, level, (GLint)internalformat, width, height, border, format, type, - nullptr); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); + MGLOG_D("%s: Backend depth (Not implemented)", __func__); - GLint currentTex; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - - GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; - MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); - - if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); - return; - } - - MG_External::GLES::glBlitFramebuffer(x, y, x + width, y + height, 0, 0, width, height, - GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), - GL_NEAREST); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); } } @@ -937,28 +912,8 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); } else { - MGLOG_D("%s: Backend depth", __func__); - GLint currentTex; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; - MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); - if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); - return; - } + MGLOG_D("%s: Backend depth (Not implemented)", __func__); - MG_External::GLES::glBlitFramebuffer( - x, y, x + width, y + height, xoffset, yoffset, xoffset + width, yoffset + height, - GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), GL_NEAREST); - errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { - MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); - }); } } From 9f4c9b61a0203fbc9f5418f9bf803905d053f4c6 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 12:54:22 +0800 Subject: [PATCH 59/74] [Fix] (MG_Backend/DirectGLES): Correctly sync attrib.Divisor when attrib.Buffer changes. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 37 +++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 05dfa17e..1a054688 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -280,6 +280,10 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertDataTypeToGLEnum(attrib.Type), attrib.Stride, (const void*)attrib.Offset); } + + if (needsSyncFormat) { + MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); + } continue; // No need to set format again } @@ -919,21 +923,24 @@ namespace MobileGL::MG_Backend::DirectGLES { } #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG else { - MGLOG_D("%s: Skipped SyncAttachmentObject(target=%s, frontendObj=(%dx%dx%d, %s), backendAtt=%s), version = %u", __func__, - MG_Util::ConvertGLEnumToString(glFBOTarget).c_str(), - attachmentObject.GetSize().x(), attachmentObject.GetSize().y(), attachmentObject.GetSize().z(), + MGLOG_D("%s: Skipped SyncAttachmentObject(target=%s, frontendObj=(%dx%dx%d, %s), backendAtt=%s), " + "version = %u", + __func__, MG_Util::ConvertGLEnumToString(glFBOTarget).c_str(), + attachmentObject.GetSize().x(), attachmentObject.GetSize().y(), + attachmentObject.GetSize().z(), MG_Util::ConvertFramebufferAttachmentTypeToString(frontendType).c_str(), MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str(), - m_syncedFrontendAttachmentVersions[i] - ); + m_syncedFrontendAttachmentVersions[i]); GLint objectType = GL_NONE; - MG_External::GLES::glGetFramebufferAttachmentParameteriv(glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); - MOBILEGL_ASSERT( - (objectType == GL_NONE) || - (attachmentObject.IsTexture() && objectType == GL_TEXTURE) || (attachmentObject.IsRenderbuffer() && objectType == GL_RENDERBUFFER), - "Attachment type not match!"); + MG_External::GLES::glGetFramebufferAttachmentParameteriv( + glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); + MOBILEGL_ASSERT((objectType == GL_NONE) || + (attachmentObject.IsTexture() && objectType == GL_TEXTURE) || + (attachmentObject.IsRenderbuffer() && objectType == GL_RENDERBUFFER), + "Attachment type not match!"); GLint objectName = 0; - MG_External::GLES::glGetFramebufferAttachmentParameteriv(glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectName); + MG_External::GLES::glGetFramebufferAttachmentParameteriv( + glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectName); // Verify that the backend object's name and parameters match the frontend attachment state if (attachmentObject.IsTexture()) { const auto& textureObject = attachmentObject.GetTexture(); @@ -942,7 +949,8 @@ namespace MobileGL::MG_Backend::DirectGLES { "No backend texture found while framebuffer reports texture attachment."); GLuint backendTexId = backendTextureIt->second->GetBackendTextureId(); MOBILEGL_ASSERT(static_cast(backendTexId) == objectName, - "Attachment texture name mismatch between GLES (%d) and backend texture object (%d), frontend texture object ID=%d.", + "Attachment texture name mismatch between GLES (%d) and backend texture object " + "(%d), frontend texture object ID=%d.", objectName, backendTexId, textureObject->GetExternalIndex()); GLint texLevel = 0; @@ -953,8 +961,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } else if (attachmentObject.IsRenderbuffer()) { const auto& renderbufferObject = attachmentObject.GetRenderbuffer(); auto backendRboIt = RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); - MOBILEGL_ASSERT(backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(), - "No backend renderbuffer found while framebuffer reports renderbuffer attachment."); + MOBILEGL_ASSERT( + backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(), + "No backend renderbuffer found while framebuffer reports renderbuffer attachment."); GLuint backendRboId = backendRboIt->second->GetBackendRenderbufferId(); MOBILEGL_ASSERT(static_cast(backendRboId) == objectName, "Attachment renderbuffer name mismatch between GLES and state object."); From 9af964de6701d6341de4dc36b4252d18af3ebc60 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 13:30:30 +0800 Subject: [PATCH 60/74] [Fix] (MG_Backend/DirectGLES): Remove the use of glVertexAttribFormat. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 35 ++++++--------------- MobileGL/MG_Backend/DirectGLES/Managers.h | 5 +-- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 1a054688..f639ab63 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -216,7 +216,7 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindVertexArray(m_backendVAOId); } - void BackendVertexArrayObject::SyncAttributeBuffer(Uint index, + void BackendVertexArrayObject::BindAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib) { const auto& bufferObject = attrib.Buffer; if (!bufferObject) { @@ -268,35 +268,20 @@ namespace MobileGL::MG_Backend::DirectGLES { m_syncedAttributeVersions[attribIndex].BufferVersion; if (!needsSyncFormat && !needsSyncBuffer) continue; - if (needsSyncBuffer) { - SyncAttributeBuffer(attribIndex, attrib); + BindAttributeBuffer(attribIndex, attrib); - if (!attrib.IsInteger) { - MG_External::GLES::glVertexAttribPointer( - attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), - attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)attrib.Offset); - } else { - MG_External::GLES::glVertexAttribIPointer(attribIndex, attrib.Size, - MG_Util::ConvertDataTypeToGLEnum(attrib.Type), - attrib.Stride, (const void*)attrib.Offset); - } - - if (needsSyncFormat) { - MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); - } - continue; // No need to set format again + if (!attrib.IsInteger) { + MG_External::GLES::glVertexAttribPointer( + attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), + attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride, (const void*)attrib.Offset); + } else { + MG_External::GLES::glVertexAttribIPointer(attribIndex, attrib.Size, + MG_Util::ConvertDataTypeToGLEnum(attrib.Type), + attrib.Stride, (const void*)attrib.Offset); } if (needsSyncFormat) { MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); - if (!attrib.IsInteger) { - MG_External::GLES::glVertexAttribFormat(attribIndex, attrib.Size, - MG_Util::ConvertDataTypeToGLEnum(attrib.Type), - attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Offset); - } else { - MG_External::GLES::glVertexAttribIFormat( - attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), attrib.Offset); - } } } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index b964ac01..b08aee3a 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -49,7 +49,7 @@ namespace MobileGL::MG_Backend::DirectGLES { void Bind(); private: - void SyncAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib); + void BindAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib); Uint m_backendVAOId = 0; Bool m_isInitialized = false; @@ -132,8 +132,9 @@ namespace MobileGL::MG_Backend::DirectGLES { bool SyncAttachmentObject(GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, GLenum glBackendAttachment); -// FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); + // FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const; + private: Uint m_backendFBOId = 0; From 8fe266e02731811292d40766d8840030e67c2dd8 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 14:00:23 +0800 Subject: [PATCH 61/74] [Fix] (MG_Test/BufferObject): Migrate to the new API for dirty ranges. --- MobileGL/MG_Test/Buffer/BufferTest.cpp | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/MobileGL/MG_Test/Buffer/BufferTest.cpp b/MobileGL/MG_Test/Buffer/BufferTest.cpp index 0f113a91..681c3652 100644 --- a/MobileGL/MG_Test/Buffer/BufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/BufferTest.cpp @@ -72,7 +72,8 @@ TEST_F(BufferTest, PingPong) { Vector bufdata(data.size()); memcpy(bufdata.data(), p, byteSize); ASSERT_EQ(data, bufdata); - auto range = bufRead->GetDirtyRange(); + ASSERT_EQ(bufRead->GetDirtyRanges().size() >= 1, true); + auto range = bufRead->GetDirtyRanges()[0]; ASSERT_EQ(range.start, 0); ASSERT_EQ(range.end, byteSize); } @@ -122,7 +123,8 @@ TEST_F(BufferTest, AcquireMemory) { void* p = bufObj->AcquireMemory(false, true, false); memcpy(actual.data(), p, byteSize); ASSERT_EQ(actual, expected); - auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + auto dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, 0); ASSERT_EQ(dirty.end, sizeof(Int) * 5); @@ -150,7 +152,8 @@ TEST_F(BufferTest, AcquireMemoryRangeWithoutExplicit) { void* p = bufObj->AcquireMemory(false, true, false); memcpy(actual.data(), p, byteSize); ASSERT_EQ(actual, expected); - auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + auto dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, sizeof(Int)); ASSERT_EQ(dirty.end, sizeof(Int) * 4); } @@ -177,13 +180,15 @@ TEST_F(BufferTest, AcquireMemoryRangeWithExplicit) { mappedPtr[1] = 300; bufObj->FlushMemoryRange(0, sizeof(Int)); - auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + auto dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, sizeof(Int)); ASSERT_EQ(dirty.end, sizeof(Int) * 2); bufObj->ReleaseMemory(); - dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, sizeof(Int)); ASSERT_EQ(dirty.end, sizeof(Int) * 2); @@ -193,7 +198,8 @@ TEST_F(BufferTest, AcquireMemoryRangeWithExplicit) { memcpy(actual.data(), p, byteSize); ASSERT_EQ(actual, expected); - dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, sizeof(Int)); ASSERT_EQ(dirty.end, sizeof(Int) * 2); } @@ -235,7 +241,8 @@ TEST_F(BufferTest, CopyBufferSubData) { ASSERT_EQ(actual, expected); - auto dirty = dstObj->GetDirtyRange(); + ASSERT_EQ(dstObj->GetDirtyRanges().size() >= 1, true); + auto dirty = dstObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, 5 * sizeof(Int)); ASSERT_EQ(dirty.end, 9 * sizeof(Int)); } @@ -265,7 +272,8 @@ TEST_F(BufferTest, WriteWhileMapped) { ASSERT_EQ(actual, expected); - auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + auto dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, 0); ASSERT_EQ(dirty.end, byteSize); } @@ -293,7 +301,8 @@ TEST_F(BufferTest, PartialUpdate) { ASSERT_EQ(actual, expected); - auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(bufObj->GetDirtyRanges().size() >= 1, true); + auto dirty = bufObj->GetDirtyRanges()[0]; ASSERT_EQ(dirty.start, sizeof(Int)); ASSERT_EQ(dirty.end, 3 * sizeof(Int)); } From 41c8850846a84bbb17007158556d839d33690654 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 15:03:13 +0800 Subject: [PATCH 62/74] [Feat] (MG_Backend/DirectGLES): Try to handle CopyTex*Image2D for depth textures. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 76 ++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 291fa438..8233d06b 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -7,7 +7,9 @@ // End of Source File Header #include "DirectGLES.h" +#include "GLES3/gl32.h" #include "MG_State/GLState/SamplerState/SamplerObject.h" +#include "MG_Util/Debug/Log.h" #include "Utils.h" #include "Managers.h" #include @@ -819,6 +821,29 @@ namespace MobileGL::MG_Backend::DirectGLES { return true; } + static GLuint s_prevDrawFBO = 0; + void BindTempDrawFBO() { + MGLOG_D("%s: Binding temporary FBO for operations like CopyTexImage2D that require framebuffer binding, " + "previous draw FBO=%u", + __func__, s_prevDrawFBO); + static GLuint tempFBO = 0; + if (!tempFBO) { + MG_External::GLES::glGenFramebuffers(1, &tempFBO); + } + MG_External::GLES::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&s_prevDrawFBO); + MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFBO); + } + void RestoreDrawFBOFromTemp() { + MGLOG_D("%s: Restoring previous draw FBO=%u", __func__, s_prevDrawFBO); + MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_prevDrawFBO); + } + + class TempFBOBinder { + public: + TempFBOBinder() { BindTempDrawFBO(); } + ~TempFBOBinder() { RestoreDrawFBOFromTemp(); } + }; + void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG @@ -864,8 +889,34 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); } else { - MGLOG_D("%s: Backend depth (Not implemented)", __func__); + MGLOG_D("%s: Backend depth", __func__); + MG_External::GLES::glTexImage2D(target, level, (GLint)internalformat, width, height, border, format, type, + nullptr); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + GLint currentTex; + MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, true), ¤tTex); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + TempFBOBinder tempFBOBinder; + MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); + + if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); + return; + } + + MG_External::GLES::glBlitFramebuffer(x, y, x + width, y + height, 0, 0, width, height, + GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), + GL_NEAREST); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); } } @@ -912,8 +963,29 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); } else { - MGLOG_D("%s: Backend depth (Not implemented)", __func__); + MGLOG_D("%s: Backend depth", __func__); + GLint currentTex; + MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + TempFBOBinder tempFBOBinder; + MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); + return; + } + MG_External::GLES::glBlitFramebuffer( + x, y, x + width, y + height, xoffset, yoffset, xoffset + width, yoffset + height, + GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), GL_NEAREST); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); } } From cd3936666d99ab92a8d4a76d0f1fdd7362de554b Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 15:27:55 +0800 Subject: [PATCH 63/74] [Fix] (MG_Backend/DirectGLES): Bind current read FBO in CopyTexImage2D. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 8233d06b..3988fb26 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -863,7 +863,7 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - + BindCurrentFBO(FramebufferTarget::Read); if (!UpdateTextureBindingAtTarget(target)) return; auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); From 93f08e251d1794f2baf2f46f6237fed04ce51aa1 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 5 Feb 2026 15:59:14 +0800 Subject: [PATCH 64/74] [Fix] (MG_Util/ShaderTranspiler): forcefully disable validator of spvtools --- MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 09246fd9..803a93f3 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -230,11 +230,14 @@ namespace MobileGL { bool ShaderCompiler::SanitizeAndOptimizeBinary(const Vector& inputBinary, Vector& outputBinary) { using namespace spvtools; + OptimizerOptions options; + options.set_run_validator(false); + Optimizer optimizer(SPV_ENV_UNIVERSAL_1_5); optimizer.RegisterPass(EliminateFloatEqualsZeroPass::CreateEliminateFloatEqualsZeroPass()); - return optimizer.Run(inputBinary.data(), inputBinary.size(), &outputBinary); + return optimizer.Run(inputBinary.data(), inputBinary.size(), &outputBinary, options); } Result ShaderCompiler::DecompileShader(SpvcSession& session) { From 3b2753379326a62cc324feae870eb6eaeb7ca2df Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 16:50:56 +0800 Subject: [PATCH 65/74] [Fix] (MG_Backend/DirectGLES): Bind current texture in CopyTexImage2D. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 3988fb26..b18a3d2e 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -863,11 +863,23 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - BindCurrentFBO(FramebufferTarget::Read); if (!UpdateTextureBindingAtTarget(target)) return; - auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + // Bind necessary FBO and texture + BindCurrentFBO(FramebufferTarget::Read); + Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); + const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) + .GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)) + .GetBoundObject(); + const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.", + textureObject ? textureObject->GetExternalIndex() : 0); + return; + } + backendTextureIt->second->Bind(target, activeTextureUnit); + auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); GLenum format = GL_DEPTH_COMPONENT; GLenum type = GL_UNSIGNED_INT; TextureImpl::GenerateTextureFormatInfo(mglInternalFormat, &internalformat, &format, &type); @@ -896,8 +908,7 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - GLint currentTex; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, true), ¤tTex); + GLint currentTex = backendTextureIt->second->GetBackendTextureId(); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); @@ -943,7 +954,20 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!UpdateTextureBindingAtTarget(target)) return; + // Bind necessary FBO and texture BindCurrentFBO(FramebufferTarget::Read); + Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); + const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) + .GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)) + .GetBoundObject(); + const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.", + textureObject ? textureObject->GetExternalIndex() : 0); + return; + } + backendTextureIt->second->Bind(target, activeTextureUnit); + errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); @@ -964,8 +988,7 @@ namespace MobileGL::MG_Backend::DirectGLES { }); } else { MGLOG_D("%s: Backend depth", __func__); - GLint currentTex; - MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), ¤tTex); + GLint currentTex = backendTextureIt->second->GetBackendTextureId(); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); From 00eb1581961ef64877490339258da060cd2b38c4 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 18:06:26 +0800 Subject: [PATCH 66/74] [Feat] (MG_Util/Converters): Add ConvertInternalFormatToUnsized. --- .../MGToMG/TextureEnumConverter.cpp | 132 +++++++++++++++--- .../Converters/MGToMG/TextureEnumConverter.h | 1 + 2 files changed, 111 insertions(+), 22 deletions(-) diff --git a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp index d5f00ddb..92c2b1fd 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp @@ -185,32 +185,32 @@ namespace MobileGL { } case TextureInternalFormat::DepthComponent: { switch (type) { - case TexturePixelDataType::UnsignedShort: - return TextureInternalFormat::DepthComponent16; - case TexturePixelDataType::UnsignedInt: - return TextureInternalFormat::DepthComponent32; - case TexturePixelDataType::Float: - return TextureInternalFormat::DepthComponent32F; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " - "returning original.", - __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; + case TexturePixelDataType::UnsignedShort: + return TextureInternalFormat::DepthComponent16; + case TexturePixelDataType::UnsignedInt: + return TextureInternalFormat::DepthComponent32; + case TexturePixelDataType::Float: + return TextureInternalFormat::DepthComponent32F; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; } } case TextureInternalFormat::DepthStencil: { switch (type) { - case TexturePixelDataType::UnsignedInt248: - return TextureInternalFormat::Depth24Stencil8; - default: - MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " - "returning original.", - __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), - MG_Util::ConvertTextureInputFormatToString(format).c_str(), - MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); - return internalformat; + case TexturePixelDataType::UnsignedInt248: + return TextureInternalFormat::Depth24Stencil8; + default: + MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, " + "returning original.", + __func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(), + MG_Util::ConvertTextureInputFormatToString(format).c_str(), + MG_Util::ConvertTexturePixelDataTypeToString(type).c_str()); + return internalformat; } } default: { @@ -223,5 +223,93 @@ namespace MobileGL { } } } + + TextureInternalFormat ConvertInternalFormatToUnsized(TextureInternalFormat internalformat) { + switch (internalformat) { + case TextureInternalFormat::R8: + case TextureInternalFormat::R8Snorm: + case TextureInternalFormat::R16: + case TextureInternalFormat::R16Snorm: + case TextureInternalFormat::R16F: + case TextureInternalFormat::R32F: + case TextureInternalFormat::R8I: + case TextureInternalFormat::R8UI: + case TextureInternalFormat::R16I: + case TextureInternalFormat::R16UI: + case TextureInternalFormat::R32I: + case TextureInternalFormat::R32UI: + case TextureInternalFormat::Red: + return TextureInternalFormat::Red; + case TextureInternalFormat::RG8: + case TextureInternalFormat::RG8Snorm: + case TextureInternalFormat::RG16: + case TextureInternalFormat::RG16Snorm: + case TextureInternalFormat::RG16F: + case TextureInternalFormat::RG32F: + case TextureInternalFormat::RG8I: + case TextureInternalFormat::RG8UI: + case TextureInternalFormat::RG16I: + case TextureInternalFormat::RG16UI: + case TextureInternalFormat::RG32I: + case TextureInternalFormat::RG32UI: + case TextureInternalFormat::RG: + return TextureInternalFormat::RG; + case TextureInternalFormat::R3G3B2: + case TextureInternalFormat::RGB4: + case TextureInternalFormat::RGB5: + case TextureInternalFormat::RGB8: + case TextureInternalFormat::RGB8Snorm: + case TextureInternalFormat::RGB10: + case TextureInternalFormat::RGB12: + case TextureInternalFormat::RGB16Snorm: + case TextureInternalFormat::RGB16F: + case TextureInternalFormat::RGB32F: + case TextureInternalFormat::R11FG11FB10F: + case TextureInternalFormat::RGB9E5: + case TextureInternalFormat::SRGB8: + case TextureInternalFormat::RGB8I: + case TextureInternalFormat::RGB8UI: + case TextureInternalFormat::RGB16I: + case TextureInternalFormat::RGB16UI: + case TextureInternalFormat::RGB32I: + case TextureInternalFormat::RGB32UI: + case TextureInternalFormat::RGB: + return TextureInternalFormat::RGB; + case TextureInternalFormat::RGBA2: + case TextureInternalFormat::RGBA4: + case TextureInternalFormat::RGB5A1: + case TextureInternalFormat::RGBA8: + case TextureInternalFormat::RGBA8Snorm: + case TextureInternalFormat::RGB10A2: + case TextureInternalFormat::RGB10A2UI: + case TextureInternalFormat::RGBA12: + case TextureInternalFormat::RGBA16: + case TextureInternalFormat::SRGB8Alpha8: + case TextureInternalFormat::RGBA16F: + case TextureInternalFormat::RGBA32F: + case TextureInternalFormat::RGBA8I: + case TextureInternalFormat::RGBA8UI: + case TextureInternalFormat::RGBA16I: + case TextureInternalFormat::RGBA16UI: + case TextureInternalFormat::RGBA32I: + case TextureInternalFormat::RGBA32UI: + case TextureInternalFormat::RGBA: + return TextureInternalFormat::RGBA; + case TextureInternalFormat::DepthComponent16: + case TextureInternalFormat::DepthComponent24: + case TextureInternalFormat::DepthComponent32: + case TextureInternalFormat::DepthComponent32F: + case TextureInternalFormat::DepthComponent: + return TextureInternalFormat::DepthComponent; + case TextureInternalFormat::Depth24Stencil8: + case TextureInternalFormat::Depth32FStencil8: + case TextureInternalFormat::DepthStencil: + return TextureInternalFormat::DepthStencil; + default: + MGLOG_W("%s: Unknown or unhandled internal format %s, returning original.", __func__, + MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str()); + return internalformat; + } + } } // namespace MG_Util } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h index e04bdf66..0cfbbe6f 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h @@ -15,5 +15,6 @@ namespace MobileGL { TextureTarget ConvertTextureUploadTargetToTextureTarget(TextureUploadTarget target); TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat, TextureInputFormat format, TexturePixelDataType type); + TextureInternalFormat ConvertInternalFormatToUnsized(TextureInternalFormat internalformat); } // namespace MG_Util } // namespace MobileGL \ No newline at end of file From 68bdea41d2ff89eb29262873aa0822a5fbb2eb97 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Thu, 5 Feb 2026 21:50:14 +0800 Subject: [PATCH 67/74] [Fix] (*/Texture*): Correctly handle the format in CopyTexImage2D. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 12 ++-- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 68 ++++++++++++++++--- .../MG_Impl/GLImpl/Texture/Validators.cpp | 22 +++++- MobileGL/MG_Impl/GLImpl/Texture/Validators.h | 2 + .../FramebufferState/FramebufferObject.h | 8 ++- .../MG_Util/Texture/TextureFormatProcessor.h | 11 +-- 6 files changed, 96 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index b18a3d2e..4710e3a4 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -879,13 +879,13 @@ namespace MobileGL::MG_Backend::DirectGLES { } backendTextureIt->second->Bind(target, activeTextureUnit); - auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + auto mgInternalFormat = textureObject->GetFormat(); GLenum format = GL_DEPTH_COMPONENT; GLenum type = GL_UNSIGNED_INT; - TextureImpl::GenerateTextureFormatInfo(mglInternalFormat, &internalformat, &format, &type); + TextureImpl::GenerateTextureFormatInfo(mgInternalFormat, &internalformat, &format, &type); MOBILEGL_ASSERT(format != GL_NONE && type != GL_NONE, "%s: cannot GenerateTextureFormatInfo(%s): out internalformat=%s, format=%s, type=%s", - MG_Util::ConvertTextureInternalFormatToString(mglInternalFormat).c_str(), + MG_Util::ConvertTextureInternalFormatToString(mgInternalFormat).c_str(), MG_Util::ConvertGLEnumToString(internalformat).c_str(), MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str()); TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); @@ -976,10 +976,10 @@ namespace MobileGL::MG_Backend::DirectGLES { errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); - auto mglInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat); + auto mgInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat); - Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(mglInternalFormat); - Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(mglInternalFormat); + Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(mgInternalFormat); + Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(mgInternalFormat); if (!isDepthFormat) { MG_External::GLES::glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index ba104d13..3fc71b68 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -7,26 +7,27 @@ // End of Source File Header #include "GL_Texture.h" -#include "GL/gl.h" #include "Config.h" -#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES -#include -#endif #include "MG_Util/Types.h" #include "Validators.h" #include "ProxyTexture.h" -#include "MG_State/GLState/TextureState/TextureObjectBuffer.h" -#include "MG_Util/Converters/GLToStr/GLEnumConverter.h" -#include "MG_Util/Texture/TextureFormatProcessor.h" #include #include #include #include +#include +#include +#include #include #include #include #include +#include + +#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES +#include +#endif namespace MobileGL { namespace MG_Impl::GLImpl { @@ -1228,12 +1229,57 @@ namespace MobileGL { void CopyTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { - GLenum outInternalFormat, format, type; - MG_Util::TextureFormatProcessor::NormalizePixelFormat(internalformat, 0, &outInternalFormat, &format, - &type); + auto internalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + const auto& currentReadFBO = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + if (!currentReadFBO) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared( + "MG_Impl/GLImpl", "CopyTexImage2D_State", + "No framebuffer is currently bound to the GL_READ_FRAMEBUFFER target.")); + return; + } + + Bool isDepth = MG_Util::IsDepthFormatInternalFormat(internalFormat); + Bool isStencil = MG_Util::IsStencilFormatInternalFormat(internalFormat); + TextureInternalFormat srcInternalFormat = TextureInternalFormat::Unknown; +#define GET_SRC_INTERNAL_FORMAT(AttachmentType) \ + const auto& srcAttachment = currentReadFBO->GetAttachment(AttachmentType); \ + if (srcAttachment.IsTexture()) { \ + const auto& texObj = srcAttachment.GetTexture(); \ + srcInternalFormat = texObj->GetFormat(); \ + } else if (srcAttachment.IsRenderbuffer()) { \ + const auto& rboObj = srcAttachment.GetRenderbuffer(); \ + srcInternalFormat = rboObj->GetInternalFormat(); \ + } else { \ + MG_State::pGLContext->RecordError( \ + ErrorCode::InvalidOperation, \ + MakeShared("MG_Impl/GLImpl", "CopyTexImage2D_State", \ + "The attachment specified by the read buffer is incomplete.")); \ + return; \ + } + if (isDepth) { + GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Depth); + } else if (isStencil) { + GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Stencil); + } else { + const auto& readBufferType = currentReadFBO->GetReadBuffer(); + GET_SRC_INTERNAL_FORMAT(readBufferType); + } + + if (!TextureImpl::ValidateBaseInternalFormatMatch(internalFormat, srcInternalFormat)) + THROW_UNIMPL_EXCEPTION; + + GLenum outInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(srcInternalFormat); + GLenum realInternalFormat = GL_RGBA8; + GLenum format = GL_DEPTH_COMPONENT; + GLenum type = GL_UNSIGNED_INT; + MG_Util::TextureFormatProcessor::NormalizePixelFormat( + outInternalFormat, PixelFormatNormalizeOptionBit::None, &realInternalFormat, &format, &type); const auto pixelUnpackBufferObject = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); - TexImage2D_State(target, level, outInternalFormat, width, height, border, format, type, nullptr); + TexImage2D_State(target, level, realInternalFormat, width, height, border, format, type, nullptr); MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).Bind(pixelUnpackBufferObject); } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp index e2e86f49..1978765b 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp @@ -7,13 +7,12 @@ // End of Source File Header #include "Validators.h" -#include "MG_State/GLState/TextureState/TextureObject.h" -#include "MG_Util/Types.h" #include #include #include #include #include +#include #include namespace MobileGL::MG_Impl::GLImpl { @@ -170,6 +169,7 @@ namespace MobileGL::MG_Impl::GLImpl { } return true; } + Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, TextureInternalFormat internalFormat, TexturePixelDataType type) { @@ -303,5 +303,21 @@ namespace MobileGL::MG_Impl::GLImpl { } return true; } + + Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2) { + auto unsizedFormat1 = MG_Util::ConvertInternalFormatToUnsized(format1); + auto unsizedFormat2 = MG_Util::ConvertInternalFormatToUnsized(format2); + if (unsizedFormat1 != unsizedFormat2) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared( + std::format("MG_Impl/GLImpl", "ValidateBaseInternalFormatMatch", + "The base internal format of the two formats do not match ({} vs. {})", + MG_Util::ConvertTextureInternalFormatToString(unsizedFormat1).c_str(), + MG_Util::ConvertTextureInternalFormatToString(unsizedFormat2).c_str()))); + return false; + } + return true; + } // namespace TextureImpl } // namespace TextureImpl -} // namespace MobileGL::MG_Impl::GLImpl +} // namespace MobileGL::MG_Impl::GLImpl \ No newline at end of file diff --git a/MobileGL/MG_Impl/GLImpl/Texture/Validators.h b/MobileGL/MG_Impl/GLImpl/Texture/Validators.h index 2ee4f0a4..2a15aba6 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.h @@ -7,6 +7,7 @@ // End of Source File Header #pragma once +#include "MG_State/GLState/TextureState/TextureEnum.h" #include "MG_Util/Types.h" #include #include @@ -32,5 +33,6 @@ namespace MobileGL::MG_Impl::GLImpl { TextureTarget target); Bool ValidateTextureSubImageOffsets(SharedPtr textureObject, Int xoffset, Int width, Int yoffset = 0, Int height = 0, Int zoffset = 0, Int depth = 0); + Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2); } // namespace TextureImpl } // namespace MobileGL::MG_Impl::GLImpl \ No newline at end of file diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index 2b83bffa..c0061971 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -71,7 +71,8 @@ namespace MobileGL { namespace GLState { class FramebufferAttachmentObject { public: - explicit FramebufferAttachmentObject(SharedPtr texture, Int level = 0); + explicit FramebufferAttachmentObject(SharedPtr texture, + Int level = 0); explicit FramebufferAttachmentObject(SharedPtr renderbuffer); explicit FramebufferAttachmentObject(Bool IsValid = true); @@ -98,10 +99,11 @@ namespace MobileGL { using TargetEnum = FramebufferTarget; using FramebufferAttachmentObjectArray = - Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; + Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; using FramebufferAttachmentArray = Array; using FramebufferAttachmentVersionArray = - Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; + Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; FramebufferObject(Uint externalIndex); diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h index 6027fd46..a8038d6b 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h @@ -9,11 +9,14 @@ #pragma once #include -namespace MobileGL::MG_Util::TextureFormatProcessor { +namespace MobileGL { enum class PixelFormatNormalizeOptionBit : Uint { NoNorm16 = 1 << 0, None = 0, }; - void NormalizePixelFormat(GLenum internalFormat, Flags options, - GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType); -} // namespace MobileGL::MG_Util::TextureFormatProcessor \ No newline at end of file + + namespace MG_Util::TextureFormatProcessor { + void NormalizePixelFormat(GLenum internalFormat, Flags options, + GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType); + } +} // namespace MobileGL \ No newline at end of file From a109675f1fad764f942bdd547d780878be35e242 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 00:38:46 +0800 Subject: [PATCH 68/74] [Feat] (MG_State/RenderState): Support indexed blend states. --- MobileGL/MG_State/GLState/Core.cpp | 18 +++ MobileGL/MG_State/GLState/Core.h | 6 + .../GLState/RenderState/RenderState.cpp | 103 +++++++++++++++--- .../GLState/RenderState/RenderState.h | 21 +++- 4 files changed, 129 insertions(+), 19 deletions(-) diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 87e313f0..93008e65 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -244,6 +244,14 @@ namespace MobileGL { return m_renderState.IsCapabilityEnabled(cap); } + void GLContext::SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled) { + m_renderState.SetCapabilityIndexed(cap, index, enabled); + } + + Bool GLContext::IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const { + return m_renderState.IsCapabilityEnabledIndexed(cap, index); + } + void GLContext::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { m_renderState.SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); @@ -254,6 +262,16 @@ namespace MobileGL { m_renderState.GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); } + void GLContext::SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, + BlendFactor srcAlpha, BlendFactor dstAlpha) { + m_renderState.SetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); + } + + void GLContext::GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, + BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { + m_renderState.GetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); + } + void GLContext::SetDepthFunc(DepthTestFunc func) { m_renderState.SetDepthFunc(func); } diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index 9e7f4001..3ac2eeac 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -92,9 +92,15 @@ namespace MobileGL { const IntVec4& GetViewport() const; // x, y, width, height void SetCapability(CapabilityInput cap, Bool enabled); Bool IsCapabilityEnabled(CapabilityInput cap) const; + void SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled); + Bool IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const; void SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha); void GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const; + void SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, + BlendFactor dstAlpha); + void GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, + BlendFactor& dstAlpha) const; void SetDepthFunc(DepthTestFunc func); DepthTestFunc GetDepthFunc() const; void SetDepthMask(Bool flag); diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index acd7f987..b23f5c34 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "RenderState.h" +#include "MG_Util/Types.h" namespace MobileGL { namespace MG_State { @@ -43,10 +44,19 @@ namespace MobileGL { break; switch (cap) { - SET_CAPABILITY(Blend, enabled); SET_CAPABILITY(DepthTest, enabled); SET_CAPABILITY(CullFace, enabled); SET_CAPABILITY(ScissorTest, enabled); + case CapabilityInput::Blend: { + Bool stateChanged = false; + for (auto& blendState : m_parameters.BlendStates) { + if (blendState.Enabled == enabled) continue; + blendState.Enabled = enabled; + stateChanged = true; + } + if (stateChanged) ++m_version; + break; + } default: // not supported currently break; } @@ -58,35 +68,100 @@ namespace MobileGL { case CapabilityInput::capability: \ return m_parameters.capability##Enabled; switch (cap) { - RETURN_CAPABILITY(Blend); RETURN_CAPABILITY(DepthTest); RETURN_CAPABILITY(CullFace); RETURN_CAPABILITY(ScissorTest); + case CapabilityInput::Blend: + return m_parameters.BlendStates[0].Enabled; default: return false; } } + void RenderState::SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled) { + // Only for BlendState currently + if (cap != CapabilityInput::Blend) { + THROW_UNIMPL_EXCEPTION; + return; + } + if (index >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MOBILEGL_ASSERT(false, "Blend capability index out of range: %d", index); + return; + } + if (m_parameters.BlendStates[index].Enabled == enabled) return; + + m_parameters.BlendStates[index].Enabled = enabled; + ++m_version; + } + + Bool RenderState::IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const { + // Only for BlendState currently + if (cap != CapabilityInput::Blend) { + THROW_UNIMPL_EXCEPTION; + return false; + } + if (index >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MOBILEGL_ASSERT(false, "Blend capability index out of range: %d", index); + return false; + } + return m_parameters.BlendStates[index].Enabled; + } + // -------------------- Blending -------------------- void RenderState::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { - if (m_parameters.SrcFactorRGB == srcRGB && m_parameters.DstFactorRGB == dstRGB && - m_parameters.SrcFactorAlpha == srcAlpha && m_parameters.DstFactorAlpha == dstAlpha) - return; - - m_parameters.SrcFactorRGB = srcRGB; - m_parameters.DstFactorRGB = dstRGB; - m_parameters.SrcFactorAlpha = srcAlpha; - m_parameters.DstFactorAlpha = dstAlpha; + Bool stateChanged = false; + for (auto& blendState : m_parameters.BlendStates) { + if (blendState.SrcFactorRGB == srcRGB && blendState.DstFactorRGB == dstRGB && + blendState.SrcFactorAlpha == srcAlpha && blendState.DstFactorAlpha == dstAlpha) { + continue; + } + blendState.SrcFactorRGB = srcRGB; + blendState.DstFactorRGB = dstRGB; + blendState.SrcFactorAlpha = srcAlpha; + blendState.DstFactorAlpha = dstAlpha; + stateChanged = true; + } + if (!stateChanged) return; ++m_version; } void RenderState::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { - srcRGB = m_parameters.SrcFactorRGB; - dstRGB = m_parameters.DstFactorRGB; - srcAlpha = m_parameters.SrcFactorAlpha; - dstAlpha = m_parameters.DstFactorAlpha; + srcRGB = m_parameters.BlendStates[0].SrcFactorRGB; + dstRGB = m_parameters.BlendStates[0].DstFactorRGB; + srcAlpha = m_parameters.BlendStates[0].SrcFactorAlpha; + dstAlpha = m_parameters.BlendStates[0].DstFactorAlpha; + } + + void RenderState::SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, + BlendFactor srcAlpha, BlendFactor dstAlpha) { + if (index >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MOBILEGL_ASSERT(false, "Blend function index out of range: %d", index); + return; + } + PerBufferBlendState& blendState = m_parameters.BlendStates[index]; + if (blendState.SrcFactorRGB == srcRGB && blendState.DstFactorRGB == dstRGB && + blendState.SrcFactorAlpha == srcAlpha && blendState.DstFactorAlpha == dstAlpha) { + return; + } + blendState.SrcFactorRGB = srcRGB; + blendState.DstFactorRGB = dstRGB; + blendState.SrcFactorAlpha = srcAlpha; + blendState.DstFactorAlpha = dstAlpha; + ++m_version; + } + + void RenderState::GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, + BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { + if (index >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MOBILEGL_ASSERT(false, "Blend function index out of range: %d", index); + return; + } + srcRGB = m_parameters.BlendStates[index].SrcFactorRGB; + dstRGB = m_parameters.BlendStates[index].DstFactorRGB; + srcAlpha = m_parameters.BlendStates[index].SrcFactorAlpha; + dstAlpha = m_parameters.BlendStates[index].DstFactorAlpha; } // -------------------- Depth -------------------- diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index c97f92c9..0b1a3214 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -9,6 +9,7 @@ #pragma once #include #include +#include namespace MobileGL { enum class BlendFactor { @@ -127,16 +128,20 @@ namespace MobileGL { Int Alignment = 4; }; + struct PerBufferBlendState { + Bool Enabled = false; + BlendFactor SrcFactorRGB = BlendFactor::One; + BlendFactor DstFactorRGB = BlendFactor::Zero; + BlendFactor SrcFactorAlpha = BlendFactor::One; + BlendFactor DstFactorAlpha = BlendFactor::Zero; + }; + struct RenderStateParameters { // Rasterization IntVec4 Viewport = IntVec4(0, 0, 0, 0); // x, y, width, height // Blending - Bool BlendEnabled = false; - BlendFactor SrcFactorRGB = BlendFactor::One; - BlendFactor DstFactorRGB = BlendFactor::Zero; - BlendFactor SrcFactorAlpha = BlendFactor::One; - BlendFactor DstFactorAlpha = BlendFactor::Zero; + Array BlendStates; // Depth Bool DepthTestEnabled = false; @@ -175,11 +180,17 @@ namespace MobileGL { // Capabilities void SetCapability(CapabilityInput cap, Bool enabled); Bool IsCapabilityEnabled(CapabilityInput cap) const; + void SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled); + Bool IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const; // Blending void SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha); void GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const; + void SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, + BlendFactor dstAlpha); + void GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, + BlendFactor& dstAlpha) const; // Depth void SetDepthFunc(DepthTestFunc func); From 91eb75a95d952bd32cdce6bd778885f1ce55b18c Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 01:03:51 +0800 Subject: [PATCH 69/74] [Feat] (MG_Impl/RenderState): Implement Enablei, Disablei, BlendFuncSeparatei, IsEnabled, IsEnabledi. --- .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 6 +- .../GLImpl/RenderState/GL_RenderState.cpp | 87 ++++++++++++++++++- .../GLImpl/RenderState/GL_RenderState.h | 3 + 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 4847a903..3f40d24c 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -385,15 +385,15 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetObjectLabel, GLenum identifier, GLuint na DECLARE_GL_FUNCTION_STUB_HEAD(void, ObjectPtrLabel, const void* ptr, GLsizei length, const GLchar* label) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ObjectPtrLabel, ptr, length, label) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetObjectPtrLabel, const void* ptr, GLsizei bufSize, GLsizei* length, GLchar* label) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetObjectPtrLabel, ptr, bufSize, length, label) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPointerv, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetPointerv, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Enablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Enablei, target, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Disablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Disablei, target, index) +DECLARE_GL_FUNCTION_HEAD(void, Enablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Enablei, target, index) +DECLARE_GL_FUNCTION_HEAD(void, Disablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Disablei, target, index) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationi, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationi, buf, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationiARB, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationi, buf, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparateiARB, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFunci, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFunci, buf, src, dst) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFunciARB, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFunci, buf, src, dst) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha) +DECLARE_GL_FUNCTION_HEAD(void, BlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFuncSeparateiARB, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha) DECLARE_GL_FUNCTION_STUB_HEAD(void, ColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ColorMaski, index, r, g, b, a) DECLARE_GL_FUNCTION_HEAD(GLboolean, IsEnabledi, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END(GLboolean, IsEnabledi, target, index) diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp index 57548582..3f4f2b55 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp @@ -111,13 +111,32 @@ namespace MobileGL { } GLboolean IsEnabledi_State(GLenum target, GLuint index) { - // TODO: implement - return GL_FALSE; + CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); + if (capInput == CapabilityInput::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "IsEnabledi_State", + "Capability enum " + + MG_Util::ConvertCapabilityInputToString(capInput) + "(" + + MG_Util::ConvertGLEnumToString(target) + ") is not supported.")); + return GL_FALSE; + } + + return MG_State::pGLContext->IsCapabilityEnabledIndexed(capInput, index) ? GL_TRUE : GL_FALSE; } GLboolean IsEnabled_State(GLenum cap) { - // TODO: implement - return GL_FALSE; + CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); + if (capInput == CapabilityInput::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, MakeShared( + "MG_Impl/GLImpl", "IsEnabled_State", + "Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) + + "(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported.")); + return GL_FALSE; + } + + return MG_State::pGLContext->IsCapabilityEnabled(capInput) ? GL_TRUE : GL_FALSE; } void Hint_State(GLenum target, GLenum mode) { @@ -253,7 +272,67 @@ namespace MobileGL { MG_State::pGLContext->SetClearColor(FloatVec4(red, green, blue, alpha)); } + void BlendFuncSeparatei_State(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { + if (buf >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeShared( + "MG_Impl/GLImpl", "BlendFuncSeparatei_State", + "Buffer index " + std::to_string(buf) + " is out of range. Max supported is " + + std::to_string(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS - 1) + ".")); + return; + } + + BlendFactor srcRGBM = MG_Util::ConvertGLEnumToBlendFactor(srcRGB); + BlendFactor dstRGBM = MG_Util::ConvertGLEnumToBlendFactor(dstRGB); + BlendFactor srcAlphaM = MG_Util::ConvertGLEnumToBlendFactor(srcAlpha); + BlendFactor dstAlphaM = MG_Util::ConvertGLEnumToBlendFactor(dstAlpha); + MG_State::pGLContext->SetBlendFuncIndexed(buf, srcRGBM, dstRGBM, srcAlphaM, dstAlphaM); + } + + void Disablei_State(GLenum target, GLuint index) { + auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); + if (capInput == CapabilityInput::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "Disablei_State", + "Capability enum " + + MG_Util::ConvertCapabilityInputToString(capInput) + "(" + + MG_Util::ConvertGLEnumToString(target) + ") is not supported.")); + return; + } + + MG_State::pGLContext->SetCapabilityIndexed(capInput, index, false); + } + + void Enablei_State(GLenum target, GLuint index) { + auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); + if (capInput == CapabilityInput::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "Enablei_State", + "Capability enum " + + MG_Util::ConvertCapabilityInputToString(capInput) + "(" + + MG_Util::ConvertGLEnumToString(target) + ") is not supported.")); + return; + } + + MG_State::pGLContext->SetCapabilityIndexed(capInput, index, true); + } + /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { + BlendFuncSeparatei_State(buf, srcRGB, dstRGB, srcAlpha, dstAlpha); + } + + void Disablei(GLenum target, GLuint index) { + Disablei_State(target, index); + } + + void Enablei(GLenum target, GLuint index) { + Enablei_State(target, index); + } + void BlendFunc(GLenum sfactor, GLenum dfactor) { BlendFunc_State(sfactor, dfactor); } diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h index 0dac02b3..36e1551d 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h @@ -12,6 +12,9 @@ namespace MobileGL { namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ + void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void Disablei(GLenum target, GLuint index); + void Enablei(GLenum target, GLuint index); void BlendFunc(GLenum sfactor, GLenum dfactor); void Viewport(GLint x, GLint y, GLsizei width, GLsizei height); void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); From 07927dc4a3333581f23f714a16274d690d6b82fa Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 01:05:18 +0800 Subject: [PATCH 70/74] [Feat] (MG_Backend/DirectGLES): Sync indexed blend states. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 109 ++++++++++++++++-- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 4710e3a4..25afd4ef 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -305,7 +305,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glDisable(cap_gl); \ } \ } - SYNC_CAPABILITY(Blend, GL_BLEND); SYNC_CAPABILITY(DepthTest, GL_DEPTH_TEST); SYNC_CAPABILITY(ScissorTest, GL_SCISSOR_TEST); SYNC_CAPABILITY(CullFace, GL_CULL_FACE); @@ -314,19 +313,107 @@ namespace MobileGL::MG_Backend::DirectGLES { const auto& ToGLBoolean = [](Bool b) -> GLboolean { return b ? GL_TRUE : GL_FALSE; }; - if (parameters.SrcFactorRGB != g_syncedRenderStateParameters.SrcFactorRGB || - parameters.DstFactorRGB != g_syncedRenderStateParameters.DstFactorRGB || - parameters.SrcFactorAlpha != g_syncedRenderStateParameters.SrcFactorAlpha || - parameters.DstFactorAlpha != g_syncedRenderStateParameters.DstFactorAlpha) { // Blend func - const BlendFactor &srcRGB = parameters.SrcFactorRGB, &dstRGB = parameters.DstFactorRGB, - &srcAlpha = parameters.SrcFactorAlpha, &dstAlpha = parameters.DstFactorAlpha; + { // Blend State + using FBO = MG_State::GLState::FramebufferObject; + const auto& targetStates = parameters.BlendStates; + auto& syncedStates = g_syncedRenderStateParameters.BlendStates; - MG_External::GLES::glBlendFuncSeparate( - MG_Util::ConvertBlendFactorToGLEnum(srcRGB), MG_Util::ConvertBlendFactorToGLEnum(dstRGB), - MG_Util::ConvertBlendFactorToGLEnum(srcAlpha), MG_Util::ConvertBlendFactorToGLEnum(dstAlpha)); + Bool allEnabled = true; + Bool allDisabled = true; + Bool anyCapDirty = false; + + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + Bool enabled = targetStates[i].Enabled; + if (enabled) + allDisabled = false; + else + allEnabled = false; + + if (enabled != syncedStates[i].Enabled) { + anyCapDirty = true; + } + } + + if (anyCapDirty) { + if (allEnabled) { + MG_External::GLES::glEnable(GL_BLEND); + for (auto& s : syncedStates) + s.Enabled = true; + } else if (allDisabled) { + MG_External::GLES::glDisable(GL_BLEND); + for (auto& s : syncedStates) + s.Enabled = false; + } else { + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + if (targetStates[i].Enabled != syncedStates[i].Enabled) { + syncedStates[i].Enabled = targetStates[i].Enabled; + syncedStates[i].Enabled ? MG_External::GLES::glEnablei(GL_BLEND, i) + : MG_External::GLES::glDisablei(GL_BLEND, i); + } + } + } + } + + Bool allFuncsSame = true; + Bool anyFuncDirty = false; + const auto& first = targetStates[0]; + + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + const auto& cur = targetStates[i]; + const auto& syn = syncedStates[i]; + + Bool isDiffFromSyn = + (cur.SrcFactorRGB != syn.SrcFactorRGB || cur.DstFactorRGB != syn.DstFactorRGB || + cur.SrcFactorAlpha != syn.SrcFactorAlpha || cur.DstFactorAlpha != syn.DstFactorAlpha); + + if (isDiffFromSyn) anyFuncDirty = true; + + if (allFuncsSame && i > 0) { + if (cur.SrcFactorRGB != first.SrcFactorRGB || cur.DstFactorRGB != first.DstFactorRGB || + cur.SrcFactorAlpha != first.SrcFactorAlpha || cur.DstFactorAlpha != first.DstFactorAlpha) { + allFuncsSame = false; + } + } + } + + if (anyFuncDirty) { + if (allFuncsSame) { + MG_External::GLES::glBlendFuncSeparate( + MG_Util::ConvertBlendFactorToGLEnum(first.SrcFactorRGB), + MG_Util::ConvertBlendFactorToGLEnum(first.DstFactorRGB), + MG_Util::ConvertBlendFactorToGLEnum(first.SrcFactorAlpha), + MG_Util::ConvertBlendFactorToGLEnum(first.DstFactorAlpha)); + + for (auto& syn : syncedStates) { + syn.SrcFactorRGB = first.SrcFactorRGB; + syn.DstFactorRGB = first.DstFactorRGB; + syn.SrcFactorAlpha = first.SrcFactorAlpha; + syn.DstFactorAlpha = first.DstFactorAlpha; + } + } else { + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + const auto& cur = targetStates[i]; + auto& syn = syncedStates[i]; + + if (cur.SrcFactorRGB != syn.SrcFactorRGB || cur.DstFactorRGB != syn.DstFactorRGB || + cur.SrcFactorAlpha != syn.SrcFactorAlpha || cur.DstFactorAlpha != syn.DstFactorAlpha) { + syn.SrcFactorRGB = cur.SrcFactorRGB; + syn.DstFactorRGB = cur.DstFactorRGB; + syn.SrcFactorAlpha = cur.SrcFactorAlpha; + syn.DstFactorAlpha = cur.DstFactorAlpha; + + MG_External::GLES::glBlendFuncSeparatei( + i, MG_Util::ConvertBlendFactorToGLEnum(cur.SrcFactorRGB), + MG_Util::ConvertBlendFactorToGLEnum(cur.DstFactorRGB), + MG_Util::ConvertBlendFactorToGLEnum(cur.SrcFactorAlpha), + MG_Util::ConvertBlendFactorToGLEnum(cur.DstFactorAlpha)); + } + } + } + } } - { // Blend equation + { // Depth state if (parameters.DepthFunc != g_syncedRenderStateParameters.DepthFunc) { MG_External::GLES::glDepthFunc(MG_Util::ConvertDepthTestFuncToGLEnum(parameters.DepthFunc)); } From a7d8e90600e0d62c7b71eb33b3a8ae8054e36532 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 20:14:18 +0800 Subject: [PATCH 71/74] [Feat] (MG_Backend/DirectGLES|MG_Impl): Initial implementions of GetTexImage and ReadPixels. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 327 +++++++++++++++++- MobileGL/MG_Backend/DirectGLES/DirectGLES.h | 3 + .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 2 +- .../GLImpl/Framebuffer/GL_Framebuffer.cpp | 144 +++++++- .../GLImpl/Framebuffer/GL_Framebuffer.h | 1 + .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 137 +++++++- MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h | 2 +- MobileGL/MG_Util/Metrics/TextureMetrics.cpp | 6 + MobileGL/MG_Util/Metrics/TextureMetrics.h | 1 + 9 files changed, 596 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 25afd4ef..7a5d8386 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -8,8 +8,11 @@ #include "DirectGLES.h" #include "GLES3/gl32.h" +#include "MG_State/GLState/ErrorState/Error.h" +#include "MG_State/GLState/RenderState/RenderState.h" #include "MG_State/GLState/SamplerState/SamplerObject.h" #include "MG_Util/Debug/Log.h" +#include "MG_Util/Types.h" #include "Utils.h" #include "Managers.h" #include @@ -882,8 +885,8 @@ namespace MobileGL::MG_Backend::DirectGLES { auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); if (!TextureImpl::IsSupportedTextureTarget(textureTarget)) { - MOBILEGL_ASSERT(false, " Texture target %s is not supported, skipping.", - MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + MGLOG_E(" Texture target %s is not supported, skipping.", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); return false; } @@ -909,7 +912,8 @@ namespace MobileGL::MG_Backend::DirectGLES { } static GLuint s_prevDrawFBO = 0; - void BindTempDrawFBO() { + static GLuint s_prevReadFBO = 0; + void BindTempFBO(Bool isRead) { MGLOG_D("%s: Binding temporary FBO for operations like CopyTexImage2D that require framebuffer binding, " "previous draw FBO=%u", __func__, s_prevDrawFBO); @@ -917,18 +921,31 @@ namespace MobileGL::MG_Backend::DirectGLES { if (!tempFBO) { MG_External::GLES::glGenFramebuffers(1, &tempFBO); } - MG_External::GLES::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&s_prevDrawFBO); - MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFBO); + if (isRead) { + MG_External::GLES::glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)&s_prevReadFBO); + MG_External::GLES::glBindFramebuffer(GL_READ_FRAMEBUFFER, tempFBO); + } else { + MG_External::GLES::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&s_prevDrawFBO); + MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFBO); + } } - void RestoreDrawFBOFromTemp() { - MGLOG_D("%s: Restoring previous draw FBO=%u", __func__, s_prevDrawFBO); - MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_prevDrawFBO); + void RestoreFBOFromTemp(Bool isRead) { + if (isRead) { + MGLOG_D("%s: Restoring previous read FBO=%u", __func__, s_prevReadFBO); + MG_External::GLES::glBindFramebuffer(GL_READ_FRAMEBUFFER, s_prevReadFBO); + } else { + MGLOG_D("%s: Restoring previous draw FBO=%u", __func__, s_prevDrawFBO); + MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_prevDrawFBO); + } } class TempFBOBinder { public: - TempFBOBinder() { BindTempDrawFBO(); } - ~TempFBOBinder() { RestoreDrawFBOFromTemp(); } + TempFBOBinder(Bool isRead) : m_isRead(isRead) { BindTempFBO(isRead); } + ~TempFBOBinder() { RestoreFBOFromTemp(m_isRead); } + + private: + const Bool m_isRead = false; }; void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, @@ -1001,7 +1018,7 @@ namespace MobileGL::MG_Backend::DirectGLES { }); GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; - TempFBOBinder tempFBOBinder; + TempFBOBinder tempFBOBinder(false); MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); if (MG_External::GLES::glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { @@ -1080,7 +1097,7 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; - TempFBOBinder tempFBOBinder; + TempFBOBinder tempFBOBinder(false); MG_External::GLES::glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); @@ -1136,6 +1153,7 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glClearBufferfv(buffer, drawbuffer, value); } + void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) { TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); @@ -1154,4 +1172,287 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glClearBufferuiv(buffer, drawbuffer, value); } -} // namespace MobileGL::MG_Backend::DirectGLES + class TempPixelStoreParameterSync { + public: + TempPixelStoreParameterSync(Bool isUnpack) : m_isUnpack(isUnpack) { + const auto& currentParams = MG_State::pGLContext->GetPixelStoreParameters(isUnpack); + m_prevParams = QueryCurrentGLPixelStoreParams(isUnpack); + Sync(isUnpack, currentParams); + } + + ~TempPixelStoreParameterSync() { Sync(m_isUnpack, m_prevParams); } + + private: + const Bool m_isUnpack; + + PixelStoreParameters m_prevParams; + + PixelStoreParameters QueryCurrentGLPixelStoreParams(Bool isUnpack) { + PixelStoreParameters p; + if (!isUnpack) { + MG_External::GLES::glGetIntegerv(GL_PACK_ALIGNMENT, (GLint*)&p.Alignment); + MG_External::GLES::glGetIntegerv(GL_PACK_ROW_LENGTH, (GLint*)&p.RowLength); + MG_External::GLES::glGetIntegerv(GL_PACK_SKIP_ROWS, (GLint*)&p.SkipRows); + MG_External::GLES::glGetIntegerv(GL_PACK_SKIP_PIXELS, (GLint*)&p.SkipPixels); + // MG_External::GLES::glGetIntegerv(GL_PACK_IMAGE_HEIGHT, (GLint*)&p.ImageHeight); + // MG_External::GLES::glGetIntegerv(GL_PACK_SKIP_IMAGES, (GLint*)&p.SkipImages); + // GLint tmp; + // MG_External::GLES::glGetIntegerv(GL_PACK_SWAP_BYTES, &tmp); + // p.SwapBytes = tmp ? true : false; + // MG_External::GLES::glGetIntegerv(GL_PACK_LSB_FIRST, &tmp); + // p.LSBFirst = tmp ? true : false; + } else { + MG_External::GLES::glGetIntegerv(GL_UNPACK_ALIGNMENT, (GLint*)&p.Alignment); + MG_External::GLES::glGetIntegerv(GL_UNPACK_ROW_LENGTH, (GLint*)&p.RowLength); + MG_External::GLES::glGetIntegerv(GL_UNPACK_SKIP_ROWS, (GLint*)&p.SkipRows); + MG_External::GLES::glGetIntegerv(GL_UNPACK_SKIP_PIXELS, (GLint*)&p.SkipPixels); + MG_External::GLES::glGetIntegerv(GL_UNPACK_IMAGE_HEIGHT, (GLint*)&p.ImageHeight); + MG_External::GLES::glGetIntegerv(GL_UNPACK_SKIP_IMAGES, (GLint*)&p.SkipImages); + // GLint tmp; + // MG_External::GLES::glGetIntegerv(GL_UNPACK_SWAP_BYTES, &tmp); + // p.SwapBytes = tmp ? true : false; + // MG_External::GLES::glGetIntegerv(GL_UNPACK_LSB_FIRST, &tmp); + // p.LSBFirst = tmp ? true : false; + } + return p; + } + + void Sync(Bool isUnpack, const PixelStoreParameters& params) { + if (!isUnpack) { + MG_External::GLES::glPixelStorei(GL_PACK_ALIGNMENT, params.Alignment); + MG_External::GLES::glPixelStorei(GL_PACK_ROW_LENGTH, params.RowLength); + MG_External::GLES::glPixelStorei(GL_PACK_SKIP_ROWS, params.SkipRows); + MG_External::GLES::glPixelStorei(GL_PACK_SKIP_PIXELS, params.SkipPixels); + // MG_External::GLES::glPixelStorei(GL_PACK_IMAGE_HEIGHT, params.ImageHeight); + // MG_External::GLES::glPixelStorei(GL_PACK_SKIP_IMAGES, params.SkipImages); + // MG_External::GLES::glPixelStorei(GL_PACK_SWAP_BYTES, params.SwapBytes ? GL_TRUE : GL_FALSE); + // MG_External::GLES::glPixelStorei(GL_PACK_LSB_FIRST, params.LSBFirst ? GL_TRUE : GL_FALSE); + } else { + MG_External::GLES::glPixelStorei(GL_UNPACK_ALIGNMENT, params.Alignment); + MG_External::GLES::glPixelStorei(GL_UNPACK_ROW_LENGTH, params.RowLength); + MG_External::GLES::glPixelStorei(GL_UNPACK_SKIP_ROWS, params.SkipRows); + MG_External::GLES::glPixelStorei(GL_UNPACK_SKIP_PIXELS, params.SkipPixels); + MG_External::GLES::glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, params.ImageHeight); + MG_External::GLES::glPixelStorei(GL_UNPACK_SKIP_IMAGES, params.SkipImages); + // MG_External::GLES::glPixelStorei(GL_UNPACK_SWAP_BYTES, params.SwapBytes ? GL_TRUE : GL_FALSE); + // MG_External::GLES::glPixelStorei(GL_UNPACK_LSB_FIRST, params.LSBFirst ? GL_TRUE : GL_FALSE); + } + } + }; + + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { + MGLOG_D("ReadPixels: x=%d y=%d w=%d h=%d format=%s type=%s pixels=%p", x, y, width, height, + MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); + + MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, + "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", + MG_Util::ConvertGLEnumToString(format).c_str()); + MOBILEGL_ASSERT(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV || + type == GL_INT || type == GL_FLOAT, + "Only GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, GL_UNSIGNED_INT_2_10_10_10_REV, " + "GL_INT and GL_FLOAT are supported currently, while requested %s.", + MG_Util::ConvertGLEnumToString(type).c_str()); + + MGLOG_D("ReadPixels: SyncNeccessaryTextures()"); + TextureImpl::SyncNeccessaryTextures(); + + MGLOG_D("ReadPixels: SyncCurrentFBO()"); + FramebufferImpl::SyncCurrentFBO(); + + MGLOG_D("ReadPixels: BindCurrentFBO(Read)"); + BindCurrentFBO(FramebufferTarget::Read); + + MGLOG_D("ReadPixels: Applying TempPixelStoreParameterSync (PACK)"); + TempPixelStoreParameterSync tempPackParamsSync(false); + + GLenum fbStatus = MG_External::GLES::glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + MGLOG_D("ReadPixels: GL_READ_FRAMEBUFFER status = %s", MG_Util::ConvertGLEnumToString(fbStatus).c_str()); + + if (fbStatus != GL_FRAMEBUFFER_COMPLETE) { + MGLOG_E("ReadPixels: bound READ FBO is not complete"); + return; + } + + // Handle PBO + auto pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + Bool usePBO; + GLuint prevPixelPackBuffer = 0; + if (pixelPackBufferObject) { + BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject); + MGLOG_D("ReadPixels: Using PBO %u", pixelPackBufferObject->GetExternalIndex()); + usePBO = true; + const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject); + + if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { + MGLOG_E("ReadPixels: No backend buffer found for PBO %u.", + pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0); + return; + } + const auto& backendBufferObject = backendBufferIt->second; + backendBufferObject->Bind(GL_PIXEL_PACK_BUFFER); + MG_External::GLES::glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&prevPixelPackBuffer); + } else { + usePBO = false; + MGLOG_D("ReadPixels: Not using PBO"); + } + + MGLOG_D("ReadPixels: glReadPixels()"); + MG_External::GLES::glReadPixels(x, y, width, height, format, type, pixels); + if (usePBO) { + // pull back to client memory if PBO is used + MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory"); + GLvoid* pboMappedPtr = MG_External::GLES::glMapBufferRange( + GL_PIXEL_PACK_BUFFER, 0, pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT); + if (pboMappedPtr) { + MGLOG_D("ReadPixels: Copying data from PBO to client memory"); + SizeT size = pixelPackBufferObject->GetSize(); + pixelPackBufferObject->UploadSubData({pboMappedPtr, size}, 0); + pixelPackBufferObject->ClearDirty(); + MGLOG_D("ReadPixels: Unmapping PBO"); + MG_External::GLES::glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } else { + MGLOG_E("ReadPixels: glMapBufferRange returned nullptr"); + MGLOG_E("ReadPixels: glMapBufferRange returned nullptr"); + } + MGLOG_D("ReadPixels: Restoring previous pixel pack buffer binding %u", prevPixelPackBuffer); + MG_External::GLES::glBindBuffer(GL_PIXEL_PACK_BUFFER, prevPixelPackBuffer); + } + MGLOG_D("ReadPixels: finished"); + } + + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels) { + MGLOG_D("GetTexImage: target=%s level=%d format=%s type=%s pixels=%p", + MG_Util::ConvertGLEnumToString(target).c_str(), level, MG_Util::ConvertGLEnumToString(format).c_str(), + MG_Util::ConvertGLEnumToString(type).c_str(), pixels); + + MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, + "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", + MG_Util::ConvertGLEnumToString(format).c_str()); + MOBILEGL_ASSERT(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV || + type == GL_INT || type == GL_FLOAT, + "Only GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, GL_UNSIGNED_INT_2_10_10_10_REV, " + "GL_INT and GL_FLOAT are supported currently, while requested %s.", + MG_Util::ConvertGLEnumToString(type).c_str()); + + MGLOG_D("GetTexImage: SyncNeccessaryTextures()"); + TextureImpl::SyncNeccessaryTextures(); + + MGLOG_D("GetTexImage: SyncCurrentFBO()"); + FramebufferImpl::SyncCurrentFBO(); + + Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); + MGLOG_D("GetTexImage: active texture unit = %u", activeTextureUnit); + + const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) + .GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)) + .GetBoundObject(); + + MGLOG_D("GetTexImage: bound texture object = %p (name=%u)", textureObject.get(), + textureObject ? textureObject->GetExternalIndex() : 0); + + const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); + + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + MGLOG_E("GetTexImage: No backend texture found for texture %u.", + textureObject ? textureObject->GetExternalIndex() : 0); + return; + } + + GLuint backendTexId = backendTextureIt->second->GetBackendTextureId(); + MGLOG_D("GetTexImage: backend texture id = %u", backendTexId); + + MGLOG_D("GetTexImage: Binding temporary FBO"); + TempFBOBinder tempFBOBinder(true); + + MGLOG_D("GetTexImage: glFramebufferTexture2D(level=%d)", level); + MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)"); + MG_External::GLES::glReadBuffer(GL_COLOR_ATTACHMENT0); + + GLenum fbStatus = MG_External::GLES::glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + MGLOG_D("GetTexImage: GL_READ_FRAMEBUFFER status = %s", MG_Util::ConvertGLEnumToString(fbStatus).c_str()); + + if (fbStatus != GL_FRAMEBUFFER_COMPLETE) { + MGLOG_E("GetTexImage: READ FBO incomplete"); + MGLOG_E("GetTexImage: bound READ FBO is not complete"); + return; + } + + MGLOG_D("GetTexImage: Applying TempPixelStoreParameterSync (PACK)"); + TempPixelStoreParameterSync tempPackParamsSync(false); + + const auto& storageType = textureObject->GetStorageType(); + MGLOG_D("GetTexImage: texture storage type = %d", (int)storageType); + + if (storageType == TextureStorageType::Buffer) { + MGLOG_E("GetTexImage: Texture storage type Buffer is not supported."); + MGLOG_E("GetTexImage: Texture storage type Buffer is not supported."); + return; + } + + auto* textureMipmapObject = static_cast(textureObject.get()); + + auto levelRange = textureMipmapObject->GetLevelRange(); + MGLOG_D("GetTexImage: mipmap level range = [%d, %d)", levelRange.x(), levelRange.y()); + + if (level < levelRange.x() || level >= levelRange.y()) { + MGLOG_E("GetTexImage: Requested level %d out of range", level); + MOBILEGL_ASSERT(false, + "GetTexImage: Requested level %d is out of range " + "(base level %d, max level %d).", + level, levelRange.x(), levelRange.y()); + return; + } + + auto size = textureMipmapObject->GetMipmapTexelSize(MG_Util::ConvertGLEnumToTextureUploadTarget(target), level); + + MGLOG_D("GetTexImage: mip level %d size = %dx%d", level, size.x(), size.y()); + + // Handle PBO + auto pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + Bool usePBO; + GLuint prevPixelPackBuffer = 0; + if (pixelPackBufferObject) { + BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject); + MGLOG_D("GetTexImage: Using PBO %u", pixelPackBufferObject->GetExternalIndex()); + usePBO = true; + const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject); + if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { + MGLOG_E("GetTexImage: No backend buffer found for PBO %u.", + pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0); + return; + } + const auto& backendBufferObject = backendBufferIt->second; + backendBufferObject->Bind(GL_PIXEL_PACK_BUFFER); + MG_External::GLES::glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&prevPixelPackBuffer); + } else { + usePBO = false; + MGLOG_D("GetTexImage: Not using PBO"); + } + MGLOG_D("GetTexImage: glReadPixels()"); + MG_External::GLES::glReadPixels(0, 0, size.x(), size.y(), format, type, pixels); + if (usePBO) { + // pull back to client memory if PBO is used + MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory"); + GLvoid* pboMappedPtr = MG_External::GLES::glMapBufferRange( + GL_PIXEL_PACK_BUFFER, 0, pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT); + if (pboMappedPtr) { + MGLOG_D("ReadPixels: Copying data from PBO to client memory"); + SizeT size = pixelPackBufferObject->GetSize(); + pixelPackBufferObject->UploadSubData({pboMappedPtr, size}, 0); + pixelPackBufferObject->ClearDirty(); + MGLOG_D("ReadPixels: Unmapping PBO"); + MG_External::GLES::glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } else { + MGLOG_E("ReadPixels: glMapBufferRange returned nullptr"); + MGLOG_E("ReadPixels: glMapBufferRange returned nullptr"); + } + MGLOG_D("ReadPixels: Restoring previous pixel pack buffer binding %u", prevPixelPackBuffer); + + MG_External::GLES::glBindBuffer(GL_PIXEL_PACK_BUFFER, prevPixelPackBuffer); + } + MGLOG_D("GetTexImage: finished"); + } + +} // namespace MobileGL::MG_Backend::DirectGLES \ No newline at end of file diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index f6ad4149..4c5241cc 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -53,4 +53,7 @@ namespace MobileGL::MG_Backend::DirectGLES { GLsizei height); void GenerateMipmap(GLenum target); const GLubyte* GetString(GLenum name); + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); + } // namespace MobileGL::MG_Backend::DirectGLES \ No newline at end of file diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 3f40d24c..30e87679 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -151,7 +151,7 @@ DECLARE_GL_FUNCTION_HEAD(void, LineWidth, GLfloat width) DECLARE_GL_FUNCTION_END DECLARE_GL_FUNCTION_HEAD(void, LinkProgram, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, LinkProgram, program) DECLARE_GL_FUNCTION_HEAD(void, PixelStorei, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PixelStorei, pname, param) DECLARE_GL_FUNCTION_HEAD(void, PolygonOffset, GLfloat factor, GLfloat units) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PolygonOffset, factor, units) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReadPixels, x, y, width, height, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, ReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ReadPixels, x, y, width, height, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, ReleaseShaderCompiler) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReleaseShaderCompiler) DECLARE_GL_FUNCTION_HEAD(void, RenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, RenderbufferStorage, target, internalformat, width, height) DECLARE_GL_FUNCTION_HEAD(void, SampleCoverage, GLfloat value, GLboolean invert) DECLARE_GL_FUNCTION_END_NO_RETURN(void, SampleCoverage, value, invert) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index f0746053..d463fca3 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -9,6 +9,7 @@ #include "GL_Framebuffer.h" #include "Validators.h" #include "Config.h" +#include #include #include #include @@ -310,10 +311,10 @@ namespace MobileGL { // ------------------- Check validity begin ------------------------ if (attType == FramebufferAttachmentType::Unknown) { MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeShared("MG_Impl/GLImpl", __func__, - std::format("`mode` = {} is not an accepted value.", - MG_Util::ConvertGLEnumToString(mode)))); + ErrorCode::InvalidEnum, + MakeShared( + "MG_Impl/GLImpl", __func__, + std::format("`mode` = {} is not an accepted value.", MG_Util::ConvertGLEnumToString(mode)))); return; } @@ -507,7 +508,142 @@ namespace MobileGL { #endif } + void ReadPixels_State(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + void* pixels) { + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + + // Check width/height + if (width < 0 || height < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "Width and height must be non-negative")); + return; + } + + // Validate format + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", "Invalid format")); + return; + } + + // Validate type + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", "Invalid pixel data type")); + return; + } + + // Get bound framebuffer + auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read); + auto framebufferObject = bindingSlot.GetBoundObject(); + + if (!framebufferObject) { + MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "No framebuffer bound to read target")); + return; + } + + // Check framebuffer completeness + if (!framebufferObject->CheckCompleteness()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidFramebufferOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete")); + return; + } + + // Check for required buffers + if (textureInputFormat == TextureInputFormat::StencilIndex) { + if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "No stencil buffer for stencil index format")); + return; + } + } else if (textureInputFormat == TextureInputFormat::DepthComponent) { + if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "No depth buffer for depth component format")); + return; + } + } else if (textureInputFormat == TextureInputFormat::DepthStencil) { + if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid() || + !framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "No depth/stencil buffer for depth-stencil format")); + return; + } + + // Validate type for depth/stencil + if (texturePixelDataType != TexturePixelDataType::UnsignedInt248 && + texturePixelDataType != TexturePixelDataType::Float32UnsignedInt248Rev) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "Invalid type for depth-stencil format")); + return; + } + } + + // Check PBO state + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + + if (pixelPackBufferObject) { + // Check if PBO is mapped + if (pixelPackBufferObject->IsMapped()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "Pixel pack buffer is currently mapped")); + return; + } + + // Check alignment + const SizeT typeSize = MG_Util::GetTexturePixelDataTypeSize(texturePixelDataType); + if (reinterpret_cast(pixels) % typeSize != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "Pixel data not aligned for pixel pack buffer")); + return; + } + } + + // Check multisampling + if (framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).IsRenderbuffer()) { + auto rbo = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).GetRenderbuffer(); + if (rbo && rbo->GetSamples() > 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "ReadPixels_State", + "ReadPixels not supported for multisampled framebuffers")); + return; + } + } + } + + void ReadPixels_Backend(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + void* pixels) { +#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES + MG_Backend::DirectGLES::ReadPixels(x, y, width, height, format, type, pixels); +#endif + } + /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { + ReadPixels_State(x, y, width, height, format, type, pixels); + ReadPixels_Backend(x, y, width, height, format, type, pixels); + } + void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { ClearBufferfi_Backend(buffer, drawbuffer, depth, stencil); } diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h index b332608c..9b34e637 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h @@ -14,6 +14,7 @@ namespace MobileGL { namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value); void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 3fc71b68..da14735f 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -1164,10 +1164,6 @@ namespace MobileGL { } } - void GetTexImage_State(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { - // TODO: implement - } - void GetCompressedTexImage_State(GLenum target, GLint level, void* img) { // TODO: implement } @@ -1378,7 +1374,136 @@ namespace MobileGL { MG_State::pGLContext->SetActiveTextureUnit(texture - GL_TEXTURE0); } + void GetTexImage_Backend(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { +#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES + MG_Backend::DirectGLES::GetTexImage(target, level, format, type, pixels); +#endif + } + + // Add to GL_Texture.cpp + void GetTexImage_State(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + // ======================= Converting ================================ + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + + // ===================== Error Checking ============================== + // Validate target + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", "Invalid texture target")); + return; + } + + // Validate level + if (level < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", "Level must be non-negative")); + return; + } + + // Validate format + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", "Invalid format")); + return; + } + + // Validate type + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", "Invalid pixel data type")); + return; + } + + // Get texture object + SharedPtr textureObject = nullptr; + if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) { + textureObject = TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget); + } else { + auto activeUnit = + MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + textureObject = bindingSlot.GetBoundObject(); + } + + if (!TextureImpl::ValidateTextureObject(textureObject)) { + MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", + "No valid texture bound to target")); + return; + } + + // Check texture completeness + if (!textureObject->IsComplete()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", "Texture is incomplete")); + return; + } + + // Check PBO state + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + + if (pixelPackBufferObject) { + // Check if PBO is mapped + if (pixelPackBufferObject->IsMapped()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", + "Pixel pack buffer is currently mapped")); + return; + } + + // Check alignment + const SizeT typeSize = MG_Util::GetTexturePixelDataTypeSize(texturePixelDataType); + if (reinterpret_cast(pixels) % typeSize != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", + "Pixel data not aligned for pixel pack buffer")); + return; + } + } + + // Special case for depth/stencil + if (textureInputFormat == TextureInputFormat::StencilIndex) { + if (textureObject->GetFormat() != TextureInternalFormat::DepthStencil && + textureObject->GetFormat() != TextureInternalFormat::Depth24Stencil8 && + textureObject->GetFormat() != TextureInternalFormat::Depth32FStencil8) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", + "No stencil buffer for stencil index format")); + return; + } + } + + // Check for multisampling + if (textureObject->GetStorageType() == TextureStorageType::Mipmap) { + auto mipmapObject = static_cast(textureObject.get()); + if (mipmapObject->GetMipmapLevelCount() > 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", "GetTexImage_State", + "Multisampled textures not supported for GetTexImage")); + return; + } + } + } + /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + GetTexImage_State(target, level, format, type, pixels); + GetTexImage_Backend(target, level, format, type, pixels); + } + 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) { TexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); @@ -1475,10 +1600,6 @@ namespace MobileGL { GetTexLevelParameterfv_State(target, level, pname, params); } - void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { - GetTexImage_State(target, level, format, type, pixels); - } - void GetCompressedTexImage(GLenum target, GLint level, void* img) { GetCompressedTexImage_State(target, level, img); } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h index 97b4047d..2bd725bc 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h @@ -12,6 +12,7 @@ namespace MobileGL { namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); 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, @@ -43,7 +44,6 @@ namespace MobileGL { void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params); void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params); void GetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat* params); - void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); void GetCompressedTexImage(GLenum target, GLint level, void* img); void GenTextures(GLsizei n, GLuint* textures); void DeleteTextures(GLsizei n, const GLuint* textures); diff --git a/MobileGL/MG_Util/Metrics/TextureMetrics.cpp b/MobileGL/MG_Util/Metrics/TextureMetrics.cpp index 5c33fec9..daaf5929 100644 --- a/MobileGL/MG_Util/Metrics/TextureMetrics.cpp +++ b/MobileGL/MG_Util/Metrics/TextureMetrics.cpp @@ -239,6 +239,12 @@ namespace MobileGL { } } + SizeT GetTexturePixelDataTypeSize(TexturePixelDataType type) { + SizeT sizedPixelFormatSize = GetSizedTexturePixelDataTypeSize(type); + if (sizedPixelFormatSize > 0) return sizedPixelFormatSize; + return GetBaseTexturePixelDataTypeSize(type); + } + SizeT GetInternalBytesPerPixel(TextureInternalFormat internalformat, TexturePixelDataType type) { SizeT sizedTextureFormatSize = GetSizedInternalFormatSizeInBytes(internalformat); if (sizedTextureFormatSize > 0) return sizedTextureFormatSize; diff --git a/MobileGL/MG_Util/Metrics/TextureMetrics.h b/MobileGL/MG_Util/Metrics/TextureMetrics.h index 85c23d55..cd99ee0d 100644 --- a/MobileGL/MG_Util/Metrics/TextureMetrics.h +++ b/MobileGL/MG_Util/Metrics/TextureMetrics.h @@ -16,6 +16,7 @@ namespace MobileGL { SizeT GetBaseInternalFormatComponentCount(TextureInternalFormat format); SizeT GetSizedTexturePixelDataTypeSize(TexturePixelDataType type); SizeT GetBaseTexturePixelDataTypeSize(TexturePixelDataType type); + SizeT GetTexturePixelDataTypeSize(TexturePixelDataType type); // This should respect internal format more SizeT GetInternalBytesPerPixel(TextureInternalFormat internalformat, TexturePixelDataType type); // This should respect type more, representing data passed in From 2ad7e2da4019a272e27ce54ab8edd2f50bd50824 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 20:21:10 +0800 Subject: [PATCH 72/74] [Misc] (MG_Backend/DirectGLES): Handle GL_BGRA as GL_RGBA in ReadPixels and GetTexImage. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 7a5d8386..38d80894 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -1244,6 +1244,8 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ReadPixels: x=%d y=%d w=%d h=%d format=%s type=%s pixels=%p", x, y, width, height, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); + if (format == GL_BGRA) format = GL_RGBA; // FIXME: convert RGBA to BGRA + MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", MG_Util::ConvertGLEnumToString(format).c_str()); @@ -1326,6 +1328,8 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(target).c_str(), level, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); + if (format == GL_BGRA) format = GL_RGBA; // FIXME: convert RGBA to BGRA + MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", MG_Util::ConvertGLEnumToString(format).c_str()); From eb3621cb3cb284b1c4cf3b759ad43c91c0d3bd39 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 20:48:01 +0800 Subject: [PATCH 73/74] Revert "[Misc] (MG_Backend/DirectGLES): Handle GL_BGRA as GL_RGBA in ReadPixels and GetTexImage." This reverts commit 2ad7e2da4019a272e27ce54ab8edd2f50bd50824. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 38d80894..7a5d8386 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -1244,8 +1244,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ReadPixels: x=%d y=%d w=%d h=%d format=%s type=%s pixels=%p", x, y, width, height, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); - if (format == GL_BGRA) format = GL_RGBA; // FIXME: convert RGBA to BGRA - MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", MG_Util::ConvertGLEnumToString(format).c_str()); @@ -1328,8 +1326,6 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(target).c_str(), level, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); - if (format == GL_BGRA) format = GL_RGBA; // FIXME: convert RGBA to BGRA - MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER, "Only GL_RGBA and GL_RGBA_INTEGER are supported currently, while requested %s.", MG_Util::ConvertGLEnumToString(format).c_str()); From d4d0407a1cf0f845902e817788654d6a771c4682 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 6 Feb 2026 21:42:07 +0800 Subject: [PATCH 74/74] [Fix] (MG_Backend/DirectGLES): Fix GetTexImage. --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 7a5d8386..6878eac9 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -915,8 +915,8 @@ namespace MobileGL::MG_Backend::DirectGLES { static GLuint s_prevReadFBO = 0; void BindTempFBO(Bool isRead) { MGLOG_D("%s: Binding temporary FBO for operations like CopyTexImage2D that require framebuffer binding, " - "previous draw FBO=%u", - __func__, s_prevDrawFBO); + "previous draw FBO=%u, read FBO=%u", + __func__, s_prevDrawFBO, s_prevReadFBO); static GLuint tempFBO = 0; if (!tempFBO) { MG_External::GLES::glGenFramebuffers(1, &tempFBO); @@ -1366,6 +1366,8 @@ namespace MobileGL::MG_Backend::DirectGLES { TempFBOBinder tempFBOBinder(true); MGLOG_D("GetTexImage: glFramebufferTexture2D(level=%d)", level); + MG_External::GLES::glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, backendTexId, + level); MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)"); MG_External::GLES::glReadBuffer(GL_COLOR_ATTACHMENT0);