diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f2baa2..4c07a400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,10 +13,32 @@ 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_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) + 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") @@ -103,7 +125,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 @@ -256,33 +280,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) @@ -296,11 +322,6 @@ if (ANDROID) log vulkan ) - target_link_libraries(${CMAKE_PROJECT_NAME}_s PUBLIC - android - log - vulkan - ) endif() if (NOT ANDROID) diff --git a/MobileGL/Config.h b/MobileGL/Config.h index a5f22734..aa047ec3 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 diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 26c3d087..6878eac9 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -7,6 +7,12 @@ // End of Source File Header #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 @@ -86,6 +92,20 @@ 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,7 +114,9 @@ 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."); @@ -104,35 +126,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) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, bufferObject) == end) { - buffersToSync.push_back(bufferObject); - } + CreateAndSyncBufferObject(bufferObject); } } // IBO if (includeIBO) { - const auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); + auto possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); if (possibleIBO) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, possibleIBO) == end) { - 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) { - const auto& end = buffersToSync.end(); - if (std::find(buffersToSync.begin(), end, possibleIndirectBuffer) == end) { - buffersToSync.push_back(possibleIndirectBuffer); - } + CreateAndSyncBufferObject(possibleIndirectBuffer); } } @@ -142,30 +155,14 @@ 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); - } + CreateAndSyncBufferObject(obj); } } - - // Do real sync - for (auto& bufferObject : buffersToSync) { - 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 namespace VertexArrayImpl { - void SyncCurrentVAO(Bool needDivisor) { + void SyncCurrentVAO() { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -183,7 +180,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendVAOObject = backendVAOIt->second; } - backendVAOObject->SyncToBackend(currentVAOObject, needDivisor); + backendVAOObject->SyncToBackend(currentVAOObject); } } // namespace VertexArrayImpl @@ -201,7 +198,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendTextureObject = backendTextureIt->second; } - backendTextureObject->SyncToBackend(textureObject); + backendTextureObject->SyncTextureParamsToBackend(textureObject); + backendTextureObject->SyncBuiltinSamplerToBackend(textureObject); + backendTextureObject->SyncMipmapsToBackend(textureObject); return backendTextureObject; } @@ -213,21 +212,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); } } } @@ -235,34 +226,14 @@ 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; - 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 @@ -276,7 +247,11 @@ 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."); @@ -303,73 +278,187 @@ namespace MobileGL::MG_Backend::DirectGLES { backendFBOObject->SyncToBackend(currentFBO, target); } - backendFBOObject->Bind(target); - lastUpdatedFBO = currentFBO.get(); } } } // 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(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); + { // 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 - 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); + { // Depth state + 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 @@ -402,7 +491,10 @@ 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()) { @@ -423,7 +515,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(); @@ -454,8 +546,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; @@ -471,18 +561,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); } } } @@ -586,7 +676,7 @@ namespace MobileGL::MG_Backend::DirectGLES { } backendSamplerObject->SyncToBackend(samplerObject); } else { - MG_External::GLES::glBindSampler(unit, 0); + SamplerImpl::UnbindSampler(unit); } } } @@ -786,18 +876,17 @@ 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.", - MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); + MGLOG_E(" Texture target %s is not supported, skipping.", + MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); return false; } @@ -817,11 +906,48 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { backendTextureObject = backendTextureIt->second; } - backendTextureObject->Bind(target); + backendTextureObject->Bind(target, unit); } return true; } + static GLuint s_prevDrawFBO = 0; + 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, read FBO=%u", + __func__, s_prevDrawFBO, s_prevReadFBO); + static GLuint tempFBO = 0; + if (!tempFBO) { + MG_External::GLES::glGenFramebuffers(1, &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 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(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, GLsizei height, GLint border) { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG @@ -841,30 +967,36 @@ 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()); }); - 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); + // 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 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); - bool isDepthFormat = + Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)); - bool isStencilFormat = + Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat)); if (!isDepthFormat) { @@ -879,30 +1011,18 @@ 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); + 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()); }); GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + 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) { MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE"); - - // Protector will automatically revert to previous fbo states return; } @@ -912,7 +1032,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 } } @@ -939,7 +1058,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()); }); @@ -948,10 +1080,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); @@ -960,29 +1092,18 @@ 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); + 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()); }); GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + 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()); }); 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; } @@ -992,7 +1113,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 } } @@ -1006,8 +1126,7 @@ namespace MobileGL::MG_Backend::DirectGLES { auto texture = slot.GetBoundObject(); auto backendTexture = TextureImpl::SyncTextureObjectToBackend(texture); - TextureImpl::BackendTextureBindingProtector protector(target); - backendTexture->Bind(target); + backendTexture->Bind(target, unitIndex); MG_External::GLES::glGenerateMipmap(target); } @@ -1031,105 +1150,16 @@ 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 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) { @@ -1138,51 +1168,293 @@ 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 + 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); + 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); + + 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 f57f56ab..4c5241cc 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); \ @@ -51,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_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 186f9b66..f639ab63 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -7,24 +7,29 @@ // End of Source File Header #include "Managers.h" -#include "MG_Backend/Backends.h" -#include "MG_Util/Debug/Log.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" -#include "MG_State/GLState/TextureState/TextureObjectBuffer.h" #include #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 @@ -39,7 +44,6 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - const GLenum TempBufferTarget = GL_ARRAY_BUFFER; void BackendBufferObject::SyncToBackend(SharedPtr& stateBufferObject) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -58,8 +62,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 +73,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; } @@ -92,7 +109,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); @@ -100,10 +116,8 @@ 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); - - stateBufferObject->ClearDirty(); } void BackendBufferObject::SyncToBackend_glBufferSubData( @@ -111,66 +125,74 @@ 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); 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) { + Bind(); + 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 - 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); - 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); - void* mappedData = - MG_External::GLES::glMapBufferRange(TempBufferTarget, range.start, range.end - range.start, - (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : 0) | GL_MAP_WRITE_BIT); + SizeT minStart = ranges.GetOverallMinStart(); + SizeT maxEnd = ranges.GetOverallMaxEnd(); + Bind(); + 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); } } - void BackendBufferObject::Bind() { -#ifdef TRACY_ENABLE - ZoneScopedC(TRACY_ZONECOLOR_BACKEND); -#endif - MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId); - } - void BackendBufferObject::Bind(GLenum target) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif + if (target == GL_ARRAY_BUFFER) { + if (g_boundVertexBufferObject == this) { + return; + } + g_boundVertexBufferObject = this; + } MG_External::GLES::glBindBuffer(target, m_backendBufferId); } UnorderedMap, SharedPtr> g_backendBufferObjects; + BackendBufferObject* g_boundVertexBufferObject = nullptr; } // namespace BufferImpl namespace VertexArrayImpl { @@ -194,8 +216,25 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_External::GLES::glBindVertexArray(m_backendVAOId); } - void BackendVertexArrayObject::SyncToBackend(SharedPtr& stateVAOObject, - Bool needDivisor) { + void BackendVertexArrayObject::BindAttributeBuffer(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::SyncToBackend(SharedPtr& stateVAOObject) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif @@ -207,36 +246,30 @@ 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()) { - 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) { + 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); + } } - const auto& bufferObject = attrib.Buffer; - if (!bufferObject) { - MGLOG_W("Attribute has no bound buffer, skipping."); - continue; - } + Bool needsSyncFormat = allAttributeVersions[attribIndex].FormatVersion != + m_syncedAttributeVersions[attribIndex].FormatVersion; + Bool needsSyncBuffer = allAttributeVersions[attribIndex].BufferVersion != + m_syncedAttributeVersions[attribIndex].BufferVersion; + if (!needsSyncFormat && !needsSyncBuffer) continue; - 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; + BindAttributeBuffer(attribIndex, attrib); - backendBufferObject->Bind(GL_ARRAY_BUFFER); if (!attrib.IsInteger) { MG_External::GLES::glVertexAttribPointer( attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type), @@ -247,23 +280,27 @@ namespace MobileGL::MG_Backend::DirectGLES { attrib.Stride, (const void*)attrib.Offset); } - if (needDivisor) { + if (needsSyncFormat) { MG_External::GLES::glVertexAttribDivisor(attribIndex, attrib.Divisor); } } - 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; } - stateVAOObject->ClearDirtyAttributes(); + m_syncedAttributeVersions = allAttributeVersions; } UnorderedMap, SharedPtr> @@ -284,11 +321,19 @@ 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 + if (g_activeTextureUnit != unit) { + ActivateTextureUnit(unit); + } + + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); + if (this == g_boundTexturesCache[unit][targetN]) return; + MG_External::GLES::glBindTexture(target, m_backendTextureId); + g_boundTexturesCache[unit][targetN] = this; } Uint BackendTextureObject::GetBackendTextureId() { @@ -298,17 +343,19 @@ 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, +#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()); @@ -333,7 +380,6 @@ 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()); @@ -507,9 +553,54 @@ namespace MobileGL::MG_Backend::DirectGLES { 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; + } + + 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; + } + + m_syncedSamplerVersion = currentSamplerVersion; + + 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 = samplerObject->GetAllSamplerParameters(); #define SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \ if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \ @@ -523,102 +614,153 @@ 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); + 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; - const auto& levelRange = stateTextureObject->GetLevelRange(); + MGLOG_D("Syncing texture params with backend ID %u to backend for state ID %u", m_backendTextureId, + stateTextureObject->GetExternalIndex()); - 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()); - }); + 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; + } - const auto& swizzleParams = stateTextureObject->GetAllSwizzleParams(); - if (swizzleParams != m_cacheSwizzleParams) { + 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 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()); + }); + } } + 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 + if (unit != g_activeTextureUnit) { + ActivateTextureUnit(unit); + } + + auto targetN = static_cast(MG_Util::ConvertGLEnumToTextureTarget(target)); + if (g_boundTexturesCache[unit][targetN] == nullptr) return; + + 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 @@ -647,6 +789,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("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__); + 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 @@ -659,129 +837,148 @@ 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 - const auto& attachments = stateFBOObject->GetAllAttachments(); - for (SizeT i = 0; i < attachments.size(); ++i) { - const auto& attachment = attachments[i]; - if (!attachment.IsValid() || attachment.IsEmpty()) { - 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; - } - - backendRenderbufferObject->SyncToBackend(renderbufferObject); - backendRenderbufferObject->Bind(); - MG_External::GLES::glFramebufferRenderbuffer(glFBOTarget, glAttachment, GL_RENDERBUFFER, - backendRenderbufferObject->GetBackendRenderbufferId()); - } + // -------------------- Connect attachments (set buffers) ----------------------- + // 1. Remap draw buffers + auto& stateDrawBuffers = stateFBOObject->GetDrawBuffers(); + Bool drawBufferClean = false; + if (memcmp(m_frontendDrawBuffers, stateDrawBuffers.data(), + FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType)) == 0) { + drawBufferClean = true; } - // 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; + + 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) { + auto frontendBuf = stateDrawBuffers[i]; + if (frontendBuf == FramebufferAttachmentType::None) { + m_backendDrawBuffers[i] = GL_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); - 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; - } - 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; + 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 { - backendRenderbufferObject = backendRenderbufferIt->second; + m_backendDrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; } - - backendRenderbufferObject->SyncToBackend(renderbufferObject); - backendRenderbufferObject->Bind(); - MG_External::GLES::glFramebufferRenderbuffer(glFBOTarget, glAttachment, GL_RENDERBUFFER, - backendRenderbufferObject->GetBackendRenderbufferId()); + nEffectiveBuffers = i + 1; } - MG_External::GLES::glReadBuffer(glAttachment); + 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 ----------------------- + const auto& attachments = stateFBOObject->GetAllAttachmentObjects(); + const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); + for (SizeT i = 0; i < attachments.size(); ++i) { + const auto& attachmentObject = attachments[i]; + 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]) { + 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), " + "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]); + 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 (%d) and backend texture object " + "(%d), frontend texture object ID=%d.", + objectName, backendTexId, textureObject->GetExternalIndex()); + + 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 } } - 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> g_backendFramebufferObjects; + Array g_fboBindVersions = {0}; } // namespace FramebufferImpl namespace PrgramImpl { @@ -902,16 +1099,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); @@ -921,24 +1109,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); @@ -1025,6 +1195,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()); @@ -1073,7 +1252,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() { @@ -1083,6 +1265,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 927f7ae8..b08aee3a 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -16,25 +16,26 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { + const GLenum TempBufferTarget = GL_ARRAY_BUFFER; class BackendBufferObject { public: BackendBufferObject(); void SyncToBackend(SharedPtr& stateBufferObject); Uint GetBackendBufferId() { return m_backendBufferId; } - void Bind(); - void Bind(GLenum target); + void Bind(GLenum target = TempBufferTarget); private: 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; Bool m_isInitialized = false; }; + extern BackendBufferObject* g_boundVertexBufferObject; extern UnorderedMap, SharedPtr> g_backendBufferObjects; } // namespace BufferImpl @@ -43,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 BindAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib); + Uint m_backendVAOId = 0; Bool m_isInitialized = false; + Uint16 m_syncedIndexBufferVersion = 0; + Array + m_syncedAttributeVersions; }; extern UnorderedMap, SharedPtr> @@ -82,11 +88,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 SyncToBackend(SharedPtr& stateTextureObject); - void Bind(GLenum target); + void SyncMipmapsToBackend(SharedPtr& stateTextureObject); + void SyncBuiltinSamplerToBackend(SharedPtr& stateTextureObject); + void SyncTextureParamsToBackend(SharedPtr& stateTextureObject); + void Bind(GLenum target, Uint unit = TempTextureUnit); Uint GetBackendTextureId(); private: @@ -98,10 +107,18 @@ 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; + 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 { @@ -112,7 +129,11 @@ namespace MobileGL::MG_Backend::DirectGLES { FramebufferTarget asTarget); Uint GetBackendFramebufferId() { return m_backendFBOId; } void Bind(FramebufferTarget target); - FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); + bool SyncAttachmentObject(GLenum glFBOTarget, + const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, + GLenum glBackendAttachment); + // FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); + GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const; private: Uint m_backendFBOId = 0; @@ -125,25 +146,22 @@ 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}; FramebufferAttachmentType m_frontendReadBuffer = FramebufferAttachmentType::Color0; + GLenum m_backendReadBuffer = GL_COLOR_ATTACHMENT0; + + using FramebufferObject = MG_State::GLState::FramebufferObject; + FramebufferObject::FramebufferAttachmentVersionArray m_syncedFrontendAttachmentVersions = {0}; }; extern UnorderedMap, SharedPtr> g_backendFramebufferObjects; + extern Array g_fboBindVersions; } // namespace FramebufferImpl namespace PrgramImpl { @@ -178,8 +196,13 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint m_backendSamplerId = 0; Bool m_isInitialized = false; SamplerParameters m_cacheSamplerParameters; + Uint16 m_syncedSamplerVersion = 0; }; + void UnbindSampler(Uint unit); + + extern Array + g_boundSamplersCache; extern UnorderedMap, SharedPtr> g_backendSamplerObjects; } // namespace SamplerImpl 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); diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index cfb3e9ad..a6c92001 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -574,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/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 4847a903..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) @@ -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/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index d590ba4f..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 @@ -304,6 +305,25 @@ namespace MobileGL { } } + 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) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -488,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); } @@ -575,6 +730,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..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); @@ -46,6 +47,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/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_Impl/GLImpl/RenderState/GL_RenderState.cpp b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp index 540fd0a6..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) { @@ -241,10 +260,6 @@ namespace MobileGL { // TODO: implement } - void ReadBuffer_State(GLenum src) { - // TODO: implement - } - void ClearStencil_State(GLint s) { // TODO: implement } @@ -257,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); } @@ -390,10 +465,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..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); @@ -45,7 +48,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); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 911fca93..da14735f 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 { @@ -714,9 +715,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 +738,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, @@ -1163,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 } @@ -1228,12 +1225,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); } @@ -1332,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); @@ -1429,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_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/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/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 206edfc5..93008e65 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); } @@ -236,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); @@ -246,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 a0f258b2..3ac2eeac 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -86,13 +86,21 @@ 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); 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/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index 899e2676..c89d064c 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,56 +71,55 @@ 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_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)] = FramebufferAttachment(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)] = FramebufferAttachment(renderbuffer); - m_drawBuffersDirty = true; + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(renderbuffer); + BumpAttachmentVersion(type); } void FramebufferObject::Detach(FramebufferAttachmentType type) { - m_attachments[static_cast(type)] = FramebufferAttachment(false); - m_drawBuffersDirty = true; + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(false); + BumpAttachmentVersion(type); } - const FramebufferAttachment& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { - return m_attachments[static_cast(type)]; + const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const { + return m_attachmentObjects[static_cast(type)]; } - const Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>& - 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(); @@ -143,26 +142,22 @@ namespace MobileGL { void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { if (m_drawBuffers[index] == buffer) return; - m_drawBuffersDirty = true; m_drawBuffers[index] = buffer; + BumpAttachmentVersion(buffer); } - // void FramebufferObject::SetDrawBuffers(const Vector& buffers) { - // m_drawBuffers = buffers; - // m_drawBuffersDirty = true; - // } - // void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) { - // - // } - - const Array& FramebufferObject:: - GetDrawBuffers() const { + const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const { return m_drawBuffers; } 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 f8356cad..c0061971 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -69,11 +69,12 @@ 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 +95,51 @@ 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; + using FramebufferAttachmentVersionArray = + Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; + 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& 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 Array& GetDrawBuffers() const; + 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: + void BumpAttachmentVersion(FramebufferAttachmentType type); + const Uint m_externalIndex = 0; - Array(FramebufferAttachmentType::FramebufferAttachmentTypeCount)> - m_attachments; - Bool m_drawBuffersDirty = false; - Array m_drawBuffers; - FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; + 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 diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index d38ddb34..b23f5c34 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -7,168 +7,246 @@ // End of Source File Header #include "RenderState.h" +#include "MG_Util/Types.h" namespace MobileGL { namespace MG_State { 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) { - m_viewport = viewport; + if (m_parameters.Viewport == viewport) return; + + m_parameters.Viewport = viewport; + ++m_version; } const IntVec4& RenderState::GetViewport() const { - return m_viewport; + return m_parameters.Viewport; } // -------------------- 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_blendEnabled = enabled; - break; - case CapabilityInput::DepthTest: - m_depthTestEnabled = enabled; - break; - case CapabilityInput::CullFace: - m_cullFaceEnabled = enabled; - break; - case CapabilityInput::ScissorTest: - m_scissorTestEnabled = 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; } +#undef SET_CAPABILITY } Bool RenderState::IsCapabilityEnabled(CapabilityInput cap) const { +#define RETURN_CAPABILITY(capability) \ + case CapabilityInput::capability: \ + return m_parameters.capability##Enabled; switch (cap) { + RETURN_CAPABILITY(DepthTest); + RETURN_CAPABILITY(CullFace); + RETURN_CAPABILITY(ScissorTest); case CapabilityInput::Blend: - return m_blendEnabled; - case CapabilityInput::DepthTest: - return m_depthTestEnabled; - case CapabilityInput::CullFace: - return m_cullFaceEnabled; - case CapabilityInput::ScissorTest: - return m_scissorTestEnabled; + 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) { - m_srcFactorRGB = srcRGB; - m_dstFactorRGB = dstRGB; - m_srcFactorAlpha = srcAlpha; - m_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_srcFactorRGB; - dstRGB = m_dstFactorRGB; - srcAlpha = m_srcFactorAlpha; - dstAlpha = m_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 -------------------- void RenderState::SetDepthFunc(DepthTestFunc func) { - m_depthFunc = func; + if (m_parameters.DepthFunc == func) return; + + m_parameters.DepthFunc = func; + ++m_version; } DepthTestFunc RenderState::GetDepthFunc() const { - return m_depthFunc; + return m_parameters.DepthFunc; } void RenderState::SetDepthMask(Bool flag) { - m_depthMask = flag; + if (m_parameters.DepthMask == flag) return; + + m_parameters.DepthMask = flag; + ++m_version; } Bool RenderState::GetDepthMask() const { - return m_depthMask; + return m_parameters.DepthMask; } // -------------------- Color Mask -------------------- void RenderState::SetColorMask(BoolVec4 mask) { - m_colorMask = mask; + if (m_parameters.ColorMask == mask) return; + + m_parameters.ColorMask = mask; + ++m_version; } const BoolVec4 RenderState::GetColorMask() const { - return m_colorMask; + return m_parameters.ColorMask; } // -------------------- Clear State -------------------- void RenderState::SetClearColor(FloatVec4 color) { - m_clearColor = color; + if (m_parameters.ClearColor == color) return; + + m_parameters.ClearColor = color; + ++m_version; } const FloatVec4& RenderState::GetClearColor() const { - return m_clearColor; + return m_parameters.ClearColor; } void RenderState::SetClearDepth(Float depth) { - m_clearDepth = depth; + if (m_parameters.ClearDepth == depth) return; + + m_parameters.ClearDepth = depth; + ++m_version; } Float RenderState::GetClearDepth() const { - return m_clearDepth; + return m_parameters.ClearDepth; } // -------------------- 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_packParameters.Alignment = value; - break; - case PixelStoreParam::PackRowLength: - m_packParameters.RowLength = value; - break; - case PixelStoreParam::PackImageHeight: - m_packParameters.ImageHeight = value; - break; - case PixelStoreParam::PackSkipPixels: - m_packParameters.SkipPixels = value; - break; - case PixelStoreParam::PackSkipRows: - m_packParameters.SkipRows = value; - break; - case PixelStoreParam::PackSkipImages: - m_packParameters.SkipImages = value; - break; - case PixelStoreParam::PackSwapBytes: - m_packParameters.SwapBytes = value != 0; - break; - case PixelStoreParam::PackLsbFirst: - m_packParameters.LSBFirst = value != 0; - break; - case PixelStoreParam::UnpackAlignment: - m_unpackParameters.Alignment = value; - break; - case PixelStoreParam::UnpackRowLength: - m_unpackParameters.RowLength = value; - break; - case PixelStoreParam::UnpackImageHeight: - m_unpackParameters.ImageHeight = value; - break; - case PixelStoreParam::UnpackSkipPixels: - m_unpackParameters.SkipPixels = value; - break; - case PixelStoreParam::UnpackSkipRows: - m_unpackParameters.SkipRows = value; - break; - case PixelStoreParam::UnpackSkipImages: - m_unpackParameters.SkipImages = value; - break; - case PixelStoreParam::UnpackSwapBytes: - m_unpackParameters.SwapBytes = value != 0; - MGLOG_D("%s: SwapBytes = %s", __func__, value ? "true" : "false"); - break; - case PixelStoreParam::UnpackLsbFirst: - m_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 +254,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_packParameters.Alignment; - case PixelStoreParam::PackRowLength: - return m_packParameters.RowLength; - case PixelStoreParam::PackImageHeight: - return m_packParameters.ImageHeight; - case PixelStoreParam::PackSkipPixels: - return m_packParameters.SkipPixels; - case PixelStoreParam::PackSkipRows: - return m_packParameters.SkipRows; - case PixelStoreParam::PackSkipImages: - return m_packParameters.SkipImages; - case PixelStoreParam::PackSwapBytes: - return m_packParameters.SwapBytes ? 1 : 0; - case PixelStoreParam::PackLsbFirst: - return m_packParameters.LSBFirst ? 1 : 0; - case PixelStoreParam::UnpackAlignment: - return m_unpackParameters.Alignment; - case PixelStoreParam::UnpackRowLength: - return m_unpackParameters.RowLength; - case PixelStoreParam::UnpackImageHeight: - return m_unpackParameters.ImageHeight; - case PixelStoreParam::UnpackSkipPixels: - return m_unpackParameters.SkipPixels; - case PixelStoreParam::UnpackSkipRows: - return m_unpackParameters.SkipRows; - case PixelStoreParam::UnpackSkipImages: - return m_unpackParameters.SkipImages; - case PixelStoreParam::UnpackSwapBytes: - return m_unpackParameters.SwapBytes ? 1 : 0; - case PixelStoreParam::UnpackLsbFirst: - return m_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,25 +281,31 @@ namespace MobileGL { } PixelStoreParameters RenderState::GetPixelStoreParameters(Bool isUnpack) const { - return isUnpack ? m_unpackParameters : m_packParameters; + return isUnpack ? m_pixelStoreUnpackParameters : m_pixelStorePackParameters; } // -------------------- Cull Face -------------------- void RenderState::SetCullFaceMode(CullFaceMode mode) { - m_cullFaceMode = mode; + if (m_parameters.CullFaceModeSetting == mode) return; + + m_parameters.CullFaceModeSetting = mode; + ++m_version; } CullFaceMode RenderState::GetCullFaceMode() const { - return m_cullFaceMode; + return m_parameters.CullFaceModeSetting; } // --------------------- Scissor --------------------- void RenderState::SetScissorBox(IntVec4 box) { - m_scissorBox = box; + if (m_parameters.ScissorBox == box) return; + + m_parameters.ScissorBox = box; + ++m_version; } 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..0b1a3214 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -7,9 +7,9 @@ // End of Source File Header #pragma once -#include "MG_Util/Math/VectorTypes.h" -#include "MG_Util/Types.h" #include +#include +#include namespace MobileGL { enum class BlendFactor { @@ -53,7 +53,7 @@ namespace MobileGL { PackSkipPixels, PackSkipImages, PackSwapBytes, - PackLsbFirst, + PackLSBFirst, // Unpack Parameters UnpackAlignment, @@ -63,7 +63,7 @@ namespace MobileGL { UnpackSkipPixels, UnpackSkipImages, UnpackSwapBytes, - UnpackLsbFirst, + UnpackLSBFirst, PixelStoreParamCount, Unknown = -1 @@ -128,12 +128,51 @@ 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 + Array BlendStates; + + // 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; + + // 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 { 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 @@ -141,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); @@ -177,39 +222,12 @@ 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; + Uint16 m_version = 0; + RenderStateParameters m_parameters; // 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 + PixelStoreParameters m_pixelStorePackParameters; + PixelStoreParameters m_pixelStoreUnpackParameters; }; } // namespace GLState } // namespace MG_State diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp index 2e24802b..ddf60478 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp @@ -14,47 +14,77 @@ 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"); } m_samplerParameters.minLod = minLod; m_samplerParameters.maxLod = maxLod; + ++m_version; } 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; } SamplerWrapMode SamplerObject::GetWrapS() const { @@ -108,6 +138,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 diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index 2fbf712f..aaac2c01 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 { @@ -43,7 +44,10 @@ namespace MobileGL { } void TextureObjectBase::SetInternalFormat(TextureInternalFormat format) { + if (format == m_internalFormat) return; + m_internalFormat = format; + ++m_textureParamsVersion; } Uint TextureObjectBase::GetExternalIndex() const { @@ -55,7 +59,10 @@ namespace MobileGL { } void TextureObjectBase::SetBorderColor(const FloatVec4& color) { + if (color == m_borderColor) return; + m_borderColor = color; + ++m_textureParamsVersion; } TextureSwizzleParam TextureObjectBase::GetSwizzleParam(TextureSwizzleParam param) const { @@ -80,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; @@ -98,9 +107,14 @@ namespace MobileGL { static_cast(param)); break; } + ++m_textureParamsVersion; } + void TextureObjectBase::SetSwizzleParamRGBA(const Vec4& values) { + if (values == m_swizzleParams) return; + m_swizzleParams = values; + ++m_textureParamsVersion; } const UintVec2& TextureObjectBase::GetLevelRange() const { @@ -108,11 +122,21 @@ 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; + } + + Uint16 TextureObjectBase::GetTextureParamsVersion() const { + return m_textureParamsVersion; } Uint TextureObjectWithOneMipmap::GetMipmapLevelCount() const { @@ -144,11 +168,11 @@ namespace MobileGL { } void TextureObjectWithOneMipmap::MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, - bool dirty) { + Bool dirty) { m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty); } - bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { + Bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const { return m_textureStorage.IsDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel); } @@ -170,7 +194,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 dab1b41c..cef2bde3 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -41,6 +41,7 @@ namespace MobileGL { virtual const UintVec2& GetLevelRange() const = 0; virtual void SetBaseLevel(Uint baseLevel) = 0; virtual void SetMaxLevel(Uint maxLevel) = 0; + virtual Uint16 GetTextureParamsVersion() const = 0; protected: virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0; @@ -67,7 +68,7 @@ namespace MobileGL { const UintVec2& GetLevelRange() const override; void SetBaseLevel(Uint baseLevel) override; void SetMaxLevel(Uint maxLevel) override; - + Uint16 GetTextureParamsVersion() const override; protected: const Uint m_externalIndex; const TextureTarget m_target = TextureTarget::Unknown; @@ -77,6 +78,7 @@ namespace MobileGL { Vec4 m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green, TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; UintVec2 m_levelRange = {0, 1000}; + Uint16 m_textureParamsVersion = 0; }; class TextureObjectMipmap : public TextureObjectBase { @@ -92,8 +94,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) = 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 { @@ -108,7 +111,7 @@ 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; + void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) override; bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override; IntVec3 GetBaseSize() const override; diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index a5b91cb3..d6e8c8c7 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -22,20 +22,26 @@ namespace MobileGL { attr.Offset = 0; attr.Buffer = nullptr; - MarkAttributeDirty(index); + BumpAttributeFormatVersion(index); } } void VertexArrayObject::EnableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; + + if (m_attributes[index].Enabled) return; + m_attributes[index].Enabled = true; - MarkAttributeDirty(index); + BumpAttributeSwitchVersion(index); } void VertexArrayObject::DisableAttribute(Uint index) { if (index >= MAX_VERTEX_ATTRIBS) return; + + if (!m_attributes[index].Enabled) return; + m_attributes[index].Enabled = false; - MarkAttributeDirty(index); + BumpAttributeSwitchVersion(index); } Bool VertexArrayObject::IsAttributeEnabled(Uint index) const { @@ -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; } @@ -59,13 +71,16 @@ 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; + + if (m_attributes[index].Buffer == buffer) return; + m_attributes[index].Buffer = buffer; - MarkAttributeDirty(index); + BumpAttributeBufferVersion(index); } BindingSlot& VertexArrayObject::GetIndexBufferBindingSlot() { @@ -83,22 +98,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 +106,39 @@ 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; + } + + 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; + 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..93f6aa47 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,12 @@ namespace MobileGL { SharedPtr Buffer; }; + struct VertexAttributeVersion { + Uint16 FormatVersion = 0; + Uint16 BufferVersion = 0; + Uint16 SwitchVersion = 0; + }; + class VertexArrayObject { public: static constexpr int MAX_VERTEX_ATTRIBS = 16; @@ -45,19 +52,22 @@ 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); + void BumpAttributeSwitchVersion(Uint index); const Uint m_externalIndex = 0; Array m_attributes; - Vector m_dirtyAttributes; + Array m_attributeVersions; BindingSlot m_indexBufferBindingSlot; }; } // namespace GLState 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)); } diff --git a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp index 5e17b568..4f094c0f 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp @@ -30,13 +30,22 @@ 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; + 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; } } 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/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; } } 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/MGToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp index 6bb12ac4..92c2b1fd 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.", @@ -193,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..936749b5 100644 --- a/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h @@ -15,5 +15,7 @@ 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 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"; } 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/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 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) { diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h index 6027fd46..17dd533e 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.h +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.h @@ -9,11 +9,13 @@ #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 diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index c4748b5c..725be14d 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -145,17 +145,20 @@ 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) { + if (m_boundObject == object) return; - void Bind(SharedPtr object) { m_boundObject = 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; }; diff --git a/README.md b/README.md index 6274c8bd..5babaa85 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ 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 | 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" // } // } }