From c08ac7db72dd16bc8ae6b7c6238ff2757f218f07 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 22 Jun 2026 21:08:49 +0800 Subject: [PATCH] [Fix]: fix Sundial Lite - detach source texture from synced GLES framebuffers before mipmap generation - bind a complete scratch framebuffer while calling glGenerateMipmap - force ANGLE norm16 texture fallback and convert 16-bit normalized uploads - raise Sundial Lite retrace tolerance for software DirectGLES validation --- .github/workflows/apk.yml | 2 +- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 138 ++++++++++- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 234 +++++++++++++----- MobileGL/MG_Backend/DirectGLES/Managers.h | 2 + MobileGL/MG_Backend/DirectGLES/Utils.cpp | 6 +- .../Texture/TextureFormatProcessor.cpp | 29 ++- 6 files changed, 343 insertions(+), 68 deletions(-) diff --git a/.github/workflows/apk.yml b/.github/workflows/apk.yml index bdbe32d3..c2f47cb5 100644 --- a/.github/workflows/apk.yml +++ b/.github/workflows/apk.yml @@ -270,7 +270,7 @@ jobs: target_call: 150023 width: 854 height: 480 - tolerance: 4100 + tolerance: 8000 crop_x: 0 crop_y: 0 crop_width: 0 diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 931da306..02a62184 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1605,27 +1606,147 @@ namespace MobileGL::MG_Backend::DirectGLES { while (g_GLESFuncs.glGetError() != GL_NO_ERROR) {} } - class ScopedDefaultFramebufferBinding { + class ScopedCompleteFramebufferBinding { public: - ScopedDefaultFramebufferBinding() { + ScopedCompleteFramebufferBinding() { + GLint prevReadFBO = 0; + GLint prevDrawFBO = 0; + g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &prevReadFBO); + g_GLESFuncs.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prevDrawFBO); + g_GLESFuncs.glGetIntegerv(GL_RENDERBUFFER_BINDING, &m_prevRenderbuffer); + m_prevReadFBO = static_cast(prevReadFBO); + m_prevDrawFBO = static_cast(prevDrawFBO); + + EnsureScratchFBO(); + g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, s_scratchFBO); + g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_scratchFBO); + } + + ~ScopedCompleteFramebufferBinding() { + g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_prevReadFBO); + g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_prevDrawFBO); + g_GLESFuncs.glBindRenderbuffer(GL_RENDERBUFFER, static_cast(m_prevRenderbuffer)); + } + + private: + static void EnsureScratchFBO() { + if (s_scratchFBO != 0) { + return; + } + + g_GLESFuncs.glGenFramebuffers(1, &s_scratchFBO); + g_GLESFuncs.glGenRenderbuffers(1, &s_scratchRBO); + g_GLESFuncs.glBindFramebuffer(GL_FRAMEBUFFER, s_scratchFBO); + g_GLESFuncs.glBindRenderbuffer(GL_RENDERBUFFER, s_scratchRBO); + g_GLESFuncs.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1); + g_GLESFuncs.glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, s_scratchRBO); + const GLenum drawBuffer = GL_COLOR_ATTACHMENT0; + g_GLESFuncs.glDrawBuffers(1, &drawBuffer); + g_GLESFuncs.glReadBuffer(GL_COLOR_ATTACHMENT0); + MOBILEGL_ASSERT(g_GLESFuncs.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, + "GenerateMipmap scratch framebuffer is incomplete."); + } + + GLuint m_prevReadFBO = 0; + GLuint m_prevDrawFBO = 0; + GLint m_prevRenderbuffer = 0; + static GLuint s_scratchFBO; + static GLuint s_scratchRBO; + }; + + GLuint ScopedCompleteFramebufferBinding::s_scratchFBO = 0; + GLuint ScopedCompleteFramebufferBinding::s_scratchRBO = 0; + + class ScopedDetachedTextureFramebufferAttachments { + public: + explicit ScopedDetachedTextureFramebufferAttachments( + const SharedPtr& texture) { + if (texture == nullptr) { + return; + } + GLint prevReadFBO = 0; GLint prevDrawFBO = 0; g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &prevReadFBO); g_GLESFuncs.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prevDrawFBO); m_prevReadFBO = static_cast(prevReadFBO); m_prevDrawFBO = static_cast(prevDrawFBO); - g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + const auto backendTextureIt = TextureImpl::g_backendTextureObjects.find(texture.get()); + if (backendTextureIt == TextureImpl::g_backendTextureObjects.end() || !backendTextureIt->second) { + return; + } + const GLuint backendTextureId = backendTextureIt->second->GetBackendTextureId(); + + for (auto it = FramebufferImpl::g_backendFramebufferObjects.begin(); + it != FramebufferImpl::g_backendFramebufferObjects.end(); ++it) { + auto* stateFBO = it->first; + const auto& backendFBO = it->second; + if (stateFBO == nullptr || !backendFBO || stateFBO->IsDefaultFramebuffer()) { + continue; + } + + const auto& attachments = stateFBO->GetAllAttachmentObjects(); + for (SizeT i = 0; i < attachments.size(); ++i) { + const auto& attachmentObject = attachments[i]; + if (!attachmentObject.IsTexture() || attachmentObject.GetTexture().get() != texture.get()) { + continue; + } + + const auto frontendType = static_cast(i); + GLenum backendAttachment = GL_NONE; + if (frontendType >= FramebufferAttachmentType::Color0 && + frontendType <= FramebufferAttachmentType::Color31) { + backendAttachment = backendFBO->GetBackendAttachmentType(frontendType); + } else { + backendAttachment = MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(frontendType); + } + if (backendAttachment == GL_NONE || backendAttachment == GL_UNKNOWN_MGL) { + continue; + } + + GLenum textureTarget = + MG_Util::ConvertTextureUploadTargetToGLEnum(attachmentObject.GetTextureUploadTarget()); + if (textureTarget == GL_UNKNOWN_MGL) { + textureTarget = MG_Util::ConvertTextureTargetToGLEnum(texture->GetTarget()); + } + + const GLuint backendFBOId = backendFBO->GetBackendFramebufferId(); + g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backendFBOId); + g_GLESFuncs.glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, backendAttachment, textureTarget, 0, 0); + ClearGLErrors(); + m_detachedAttachments.push_back( + {backendFBOId, backendAttachment, textureTarget, backendTextureId, + static_cast(attachmentObject.GetTextureLevel())}); + } + } } - ~ScopedDefaultFramebufferBinding() { + ~ScopedDetachedTextureFramebufferAttachments() { + for (const auto& attachment : m_detachedAttachments) { + g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, attachment.framebuffer); + g_GLESFuncs.glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, attachment.attachment, attachment.textureTarget, + attachment.texture, attachment.level); + } g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_prevReadFBO); g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_prevDrawFBO); } private: + struct DetachedAttachment { + GLuint framebuffer = 0; + GLenum attachment = GL_NONE; + GLenum textureTarget = GL_TEXTURE_2D; + GLuint texture = 0; + GLint level = 0; + }; + GLuint m_prevReadFBO = 0; GLuint m_prevDrawFBO = 0; + Vector m_detachedAttachments; }; class ScopedDepthBlitState { @@ -2077,8 +2198,11 @@ namespace MobileGL::MG_Backend::DirectGLES { backendTexture->Bind(target, unitIndex); DebugImpl::ErrorLopper::Clear(); // ANGLE/Mesa may validate the currently bound FBO while generating mipmaps. - // Avoid a feedback-loop style failure when the source texture is attached there. - ScopedDefaultFramebufferBinding defaultFramebuffer; + // Also detach the source texture from synced FBO objects for ANGLE's validation. + ScopedDetachedTextureFramebufferAttachments detachedAttachments(texture); + DebugImpl::ErrorLopper::Clear(); + // Bind a complete internal FBO that does not reference the source texture. + ScopedCompleteFramebufferBinding completeFramebuffer; g_GLESFuncs.glGenerateMipmap(target); AssertNoGLError("glGenerateMipmap"); } diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index d20fa0b9..7c1b199f 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace MobileGL::MG_Backend::DirectGLES { @@ -497,6 +498,110 @@ namespace MobileGL::MG_Backend::DirectGLES { return m_backendTextureId; } + class ScopedDefaultUnpackState { + public: + ScopedDefaultUnpackState() { + g_GLESFuncs.glGetIntegerv(GL_UNPACK_ALIGNMENT, &m_prevAlignment); + g_GLESFuncs.glGetIntegerv(GL_UNPACK_ROW_LENGTH, &m_prevRowLength); + g_GLESFuncs.glGetIntegerv(GL_UNPACK_SKIP_ROWS, &m_prevSkipRows); + g_GLESFuncs.glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &m_prevSkipPixels); + g_GLESFuncs.glGetIntegerv(GL_UNPACK_IMAGE_HEIGHT, &m_prevImageHeight); + g_GLESFuncs.glGetIntegerv(GL_UNPACK_SKIP_IMAGES, &m_prevSkipImages); + g_GLESFuncs.glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0); + } + + ~ScopedDefaultUnpackState() { + g_GLESFuncs.glPixelStorei(GL_UNPACK_ALIGNMENT, m_prevAlignment); + g_GLESFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, m_prevRowLength); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_ROWS, m_prevSkipRows); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_PIXELS, m_prevSkipPixels); + g_GLESFuncs.glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_prevImageHeight); + g_GLESFuncs.glPixelStorei(GL_UNPACK_SKIP_IMAGES, m_prevSkipImages); + } + + private: + GLint m_prevAlignment = 4; + GLint m_prevRowLength = 0; + GLint m_prevSkipRows = 0; + GLint m_prevSkipPixels = 0; + GLint m_prevImageHeight = 0; + GLint m_prevSkipImages = 0; + }; + + static Uint GetNorm16ComponentCount(TextureInternalFormat format) { + switch (format) { + case TextureInternalFormat::R16: + case TextureInternalFormat::R16Snorm: + return 1; + case TextureInternalFormat::RG16: + case TextureInternalFormat::RG16Snorm: + return 2; + case TextureInternalFormat::RGB16: + case TextureInternalFormat::RGB16Snorm: + return 3; + case TextureInternalFormat::RGBA16: + case TextureInternalFormat::RGBA16Snorm: + return 4; + default: + return 0; + } + } + + static Bool IsSnorm16Format(TextureInternalFormat format) { + switch (format) { + case TextureInternalFormat::R16Snorm: + case TextureInternalFormat::RG16Snorm: + case TextureInternalFormat::RGB16Snorm: + case TextureInternalFormat::RGBA16Snorm: + return true; + default: + return false; + } + } + + static const void* PrepareNorm16FloatFallbackUpload(TextureInternalFormat format, + const IntVec3& texelSize, + const void* data, + SizeT byteSize, + GLenum uploadType, + Vector& convertedData) { + const Uint componentCount = GetNorm16ComponentCount(format); + if (componentCount == 0 || uploadType != GL_FLOAT || data == nullptr || byteSize == 0) { + return data; + } + + const SizeT texelCount = static_cast(std::max(texelSize.x(), 0)) * + static_cast(std::max(texelSize.y(), 0)) * + static_cast(std::max(texelSize.z(), 0)); + const SizeT componentTotal = texelCount * static_cast(componentCount); + const SizeT sourceComponentTotal = byteSize / sizeof(Uint16); + if (componentTotal == 0 || sourceComponentTotal == 0) { + return nullptr; + } + + convertedData.assign(componentTotal, 0.0f); + const SizeT copyComponentTotal = std::min(componentTotal, sourceComponentTotal); + if (IsSnorm16Format(format)) { + const Int16* src = static_cast(data); + constexpr Float invMaxSnorm16 = 1.0f / 32767.0f; + for (SizeT i = 0; i < copyComponentTotal; ++i) { + convertedData[i] = std::max(static_cast(src[i]) * invMaxSnorm16, -1.0f); + } + } else { + const Uint16* src = static_cast(data); + constexpr Float invMaxUnorm16 = 1.0f / 65535.0f; + for (SizeT i = 0; i < copyComponentTotal; ++i) { + convertedData[i] = static_cast(src[i]) * invMaxUnorm16; + } + } + return convertedData.data(); + } + void BackendTextureObject::SyncMipmapsToBackend( const SharedPtr& stateTextureObject) { if (!stateTextureObject) { @@ -579,6 +684,7 @@ namespace MobileGL::MG_Backend::DirectGLES { &glFormat, &glType); const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + ScopedDefaultUnpackState unpackState; for (auto& uploadTarget : uploadTargets) { for (SizeT level = m_prevTextureInfo.mipmapLevels; level < mipmapCount; ++level) { auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); @@ -588,6 +694,10 @@ namespace MobileGL::MG_Backend::DirectGLES { auto* pData = (levelDirty && levelByteSize != 0) ? textureMipmapObject->MapMipmapData(uploadTarget, level) : nullptr; + Vector convertedUploadData; + const void* uploadData = PrepareNorm16FloatFallbackUpload( + textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType, + convertedUploadData); DebugImpl::ErrorLopper::Clear(); g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); @@ -597,13 +707,13 @@ namespace MobileGL::MG_Backend::DirectGLES { g_GLESFuncs.glTexImage2D( glUploadTarget, static_cast(level), (GLint)glInternalFormat, static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - 0, glFormat, glType, pData); + 0, glFormat, glType, uploadData); break; case TextureTarget::Texture3D: g_GLESFuncs.glTexImage3D( glUploadTarget, static_cast(level), (GLint)glInternalFormat, static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - static_cast(levelTexelSize.z()), 0, glFormat, glType, pData); + static_cast(levelTexelSize.z()), 0, glFormat, glType, uploadData); break; default: MGLOG_E("Unhandled texture target %s", @@ -664,62 +774,69 @@ namespace MobileGL::MG_Backend::DirectGLES { } } } else { - 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"); + ScopedDefaultUnpackState unpackState; + 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; + Vector convertedUploadData; + const void* uploadData = PrepareNorm16FloatFallbackUpload( + textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType, + convertedUploadData); + 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"); - DebugImpl::ErrorLopper::Clear(); - g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - auto textureTarget = stateTextureObject->GetTarget(); - // TODO: handle more texture types - switch (textureTarget) { - case TextureTarget::Texture2D: - case TextureTarget::TextureCubeMap: { - g_GLESFuncs.glTexImage2D( - glUploadTarget, static_cast(level), (GLint)glInternalFormat, - static_cast(levelTexelSize.x()), static_cast(levelTexelSize.y()), - 0, glFormat, glType, pData); - break; + DebugImpl::ErrorLopper::Clear(); + g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + auto textureTarget = stateTextureObject->GetTarget(); + // TODO: handle more texture types + switch (textureTarget) { + case TextureTarget::Texture2D: + case TextureTarget::TextureCubeMap: { + g_GLESFuncs.glTexImage2D( + glUploadTarget, static_cast(level), (GLint)glInternalFormat, + static_cast(levelTexelSize.x()), + static_cast(levelTexelSize.y()), 0, glFormat, glType, uploadData); + break; + } + case TextureTarget::Texture3D: { + g_GLESFuncs.glTexImage3D( + glUploadTarget, static_cast(level), (GLint)glInternalFormat, + static_cast(levelTexelSize.x()), + static_cast(levelTexelSize.y()), + static_cast(levelTexelSize.z()), 0, glFormat, glType, uploadData); + break; + } + default: { + MGLOG_E("Unhandled texture target %s", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + } + } + DebugImpl::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); } - case TextureTarget::Texture3D: { - g_GLESFuncs.glTexImage3D( - glUploadTarget, static_cast(level), (GLint)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()); - } - } - DebugImpl::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; } @@ -742,6 +859,7 @@ namespace MobileGL::MG_Backend::DirectGLES { TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, &glFormat, &glType); const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + ScopedDefaultUnpackState unpackState; for (auto& uploadTarget : uploadTargets) { for (SizeT level = 0; level < mipmapCount; ++level) { if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { @@ -770,20 +888,24 @@ namespace MobileGL::MG_Backend::DirectGLES { }); auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); const void* mipData = textureMipmapObject->MapMipmapData(uploadTarget, level); + Vector convertedUploadData; + const void* uploadData = PrepareNorm16FloatFallbackUpload( + textureMipmapObject->GetFormat(), texelSize, mipData, byteSize, glType, + convertedUploadData); switch (stateTextureObject->GetTarget()) { case TextureTarget::Texture2D: case TextureTarget::TextureCubeMap: g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, static_cast(texelSize.x()), static_cast(texelSize.y()), glFormat, glType, - mipData); + uploadData); break; case TextureTarget::Texture3D: g_GLESFuncs.glTexSubImage3D(glUploadTarget, static_cast(level), 0, 0, 0, static_cast(texelSize.x()), static_cast(texelSize.y()), static_cast(texelSize.z()), glFormat, glType, - mipData); + uploadData); break; default: MGLOG_E("Unhandled texture target %s", diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 942a048c..06ab7aa1 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -52,6 +52,8 @@ namespace MobileGL::MG_Backend::DirectGLES { return const_cast(this)->find(stateObj); } + iterator begin() { return m_backendObjects.begin(); } + const_iterator begin() const { return m_backendObjects.begin(); } iterator end() { return m_backendObjects.end(); } const_iterator end() const { return m_backendObjects.end(); } diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index 622a276d..8cdfcf73 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -26,8 +26,10 @@ namespace MobileGL::MG_Backend::DirectGLES { ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif using namespace MobileGL::MG_Util::TextureFormatProcessor; - auto options = (g_GLESCapabilities.SupportsNorm16Texture) ? PixelFormatNormalizeOptionBit::None - : PixelFormatNormalizeOptionBit::NoNorm16; + const Bool useNorm16Texture = g_GLESCapabilities.SupportsNorm16Texture && + g_GLESCapabilities.GLESRendererString.find("ANGLE") == String::npos; + auto options = + useNorm16Texture ? PixelFormatNormalizeOptionBit::None : PixelFormatNormalizeOptionBit::NoNorm16; NormalizePixelFormat(MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat), options, outInternalFormat, outFormat, outType); } diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp index 0a94bef0..dc7fd949 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp @@ -41,6 +41,26 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { *outInternalFormat = GL_R32F; break; } + case GL_RGBA16_SNORM: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RGBA16F; + break; + } + case GL_RGB16_SNORM: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RGB16F; + break; + } + case GL_RG16_SNORM: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_RG16F; + break; + } + case GL_R16_SNORM: + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outInternalFormat = GL_R16F; + break; + } default: *outInternalFormat = internalFormat; break; @@ -235,8 +255,13 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { case GL_RGB16_SNORM: case GL_RG16_SNORM: case GL_R16_SNORM: - *outType = GL_SHORT; - break; + if (options & PixelFormatNormalizeOptionBit::NoNorm16) { + *outType = GL_FLOAT; + break; + } else { + *outType = GL_SHORT; + break; + } case GL_RGBA8_SNORM: case GL_RGB8_SNORM: case GL_RG8_SNORM: