From bb459061896b1e938984749e8275a88434ce4626 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 13:33:29 +0800 Subject: [PATCH 01/21] [Feat] implement glBlitFramebuffer, making things start to render --- CMakeLists.txt | 2 +- .../Implementations/GL/Drawing/GL_Drawing.cpp | 128 +++++++++++++++++- .../GL/Framebuffer/GL_Framebuffer.h | 1 + .../GLFuncsDefinitions/GLFuncsDefinitions.cpp | 3 +- 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index accb47a3..6482a160 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") set(CMAKE_ANDROID_STL_TYPE c++_static) -set(CMAKE_BUILD_TYPE Release) +#set(CMAKE_BUILD_TYPE Release) set(PROFILING OFF) diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index 8ab473e6..f4152177 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -215,7 +215,7 @@ namespace MG_GL::GL { static GLuint lastBoundVAO = 0; static GLuint lastBoundProgram = 0; - static GLuint lastBoundFBO = 0; + static GLuint lastBoundFBO[2] = {0}; static std::array lastBoundTextures; void DrawElementsSHITTILY(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { CommonState* commonState = MG_State_T::commonState; @@ -579,7 +579,7 @@ namespace MG_GL::GL { // Framebuffer GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; - if (currentFBO != lastBoundFBO) { + if (currentFBO != lastBoundFBO[0]) { GLuint glFBO = 0; if (currentFBO == 0) { @@ -665,7 +665,7 @@ namespace MG_GL::GL { } } - lastBoundFBO = currentFBO; + lastBoundFBO[0] = currentFBO; } @@ -679,12 +679,130 @@ namespace MG_GL::GL { } } + void RealizeFBOState(GLenum fbtype) { + FramebufferState* fbState = MG_State_T::framebufferState; + + GLuint fb = fbState->currentBindings_[fbtype]; + if (fb != lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1]) { + GLuint glFBO = 0; + + if (fb == 0) { + CallAndCheck(::GLES::glBindFramebuffer(fbtype, 0);) + glFBO = 0; + } else { + bool isNewGlesFBO = false; + + if (s_framebufferMap.find(fb) == s_framebufferMap.end()) { + CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) + s_framebufferMap[fb] = glFBO; + CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) + MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, fb); + isNewGlesFBO = true; + } else { + glFBO = s_framebufferMap[fb]; + CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) + MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, fb); + } + + if (glFBO != 0) { + FramebufferObject* mgFBO = fbState->GetCurrentFBO(fbtype); + if (mgFBO) { + MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, fb); + + for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { + + if (mgAtt.type != GL_TEXTURE_2D) { + MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, fb); + continue; + } + + GLuint expectedGLTexId = 0; + if (mgAtt.handle != 0) { + if (s_textureMap.count(mgAtt.handle)) { + expectedGLTexId = s_textureMap[mgAtt.handle]; + } else { + MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, fb, mgAttachmentPoint); + for (auto const& [key, val] : s_textureMap) + MG_Util::Debug::LogW(" key: %d, val: %d", key, val); + continue; + } + } + + GLint glesAttachedType = 0; + GLint glesAttachedName = 0; + + CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) + + if (glesAttachedType == GL_TEXTURE) { + CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) + } else if (glesAttachedType != GL_NONE) { + MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); + } + + if ((GLuint)glesAttachedName != expectedGLTexId) { + MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", + fb, mgAttachmentPoint, expectedGLTexId, glesAttachedName); + + CallAndCheck(::GLES::glFramebufferTexture2D( + fbtype, + mgAttachmentPoint, + GL_TEXTURE_2D, + expectedGLTexId, + mgAtt.mipLevel + );) + } + } + + GLenum status = ::GLES::glCheckFramebufferStatus(fbtype); + if (status != GL_FRAMEBUFFER_COMPLETE) { + MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", + fb, glFBO, status, MG_Util::Debug::GLEnumToString(status)); + } + + } else { + MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", fb); + } + } + } + + lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1] = fb; + } + } + + void BlitFramebuffer(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) { + MG_Util::Debug::LogD("BlitFramebuffer, srcX0=%d, srcY0=%d, srcX1=%d, srcY1=%d, dstX0=%d, dstY0=%d, dstX1=%d, dstY1=%d, mask=0x%x, filter=%s", + srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, MG_Util::Debug::GLEnumToString(filter)); + + // Realize FBO states + RealizeFBOState(GL_DRAW_FRAMEBUFFER); + RealizeFBOState(GL_READ_FRAMEBUFFER); + ::GLES::glBlitFramebuffer(srcX0, + srcY0, + srcX1, + srcY1, + dstX0, + dstY0, + dstX1, + dstY1, + mask, + filter); + } + void ClearSHITTILY(GLbitfield mask) { CommonState* commonState = MG_State_T::commonState; FramebufferState* fbState = MG_State_T::framebufferState; GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; - if (currentFBO != lastBoundFBO) { + if (currentFBO != lastBoundFBO[0]) { GLuint glFBO = 0; if (currentFBO == 0) { @@ -766,7 +884,7 @@ namespace MG_GL::GL { } } - lastBoundFBO = currentFBO; + lastBoundFBO[0] = currentFBO; } diff --git a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h index 712b3289..1ecf9a43 100644 --- a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h +++ b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.h @@ -13,6 +13,7 @@ namespace MG_GL::GL { void BindFramebuffer(GLenum target, GLuint framebuffer); void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLenum CheckFramebufferStatus(GLenum target); + void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); } #endif //MOBILEGL_GL_FRAMEBUFFER_H diff --git a/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp b/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp index 8c0af8fb..034f111a 100644 --- a/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp +++ b/MG/MG_GL/Implementations/GL/GLFuncsDefinitions/GLFuncsDefinitions.cpp @@ -197,7 +197,8 @@ DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix2x4fv, GLint location, GLsizei count DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix4x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformMatrix4x2fv, location,count,transpose,value) DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix3x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformMatrix3x4fv, location,count,transpose,value) DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix4x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformMatrix4x3fv, location,count,transpose,value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlitFramebuffer, srcX0,srcY0,srcX1,srcY1,dstX0,dstY0,dstX1,dstY1,mask,filter) +DECLARE_GL_FUNCTION_HEAD(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlitFramebuffer, srcX0,srcY0,srcX1,srcY1,dstX0,dstY0,dstX1,dstY1,mask,filter) +//DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlitFramebuffer, srcX0,srcY0,srcX1,srcY1,dstX0,dstY0,dstX1,dstY1,mask,filter) DECLARE_GL_FUNCTION_STUB_HEAD(void, RenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, RenderbufferStorageMultisample, target,samples,internalformat,width,height) DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferTextureLayer, target,attachment,texture,level,layer) DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedBufferRange, target,offset,length) From 6c1d1e0e7a86e56a030d3f6ab290c40935e724b6 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 15:38:30 +0800 Subject: [PATCH 02/21] [Feat] implement renaming for buffer --- .../Implementations/GL/Buffer/GL_Buffer.cpp | 15 +- .../Implementations/GL/Drawing/GL_Drawing.cpp | 588 +++++++++--------- MG/MG_GL/State/Buffer/BufferState.cpp | 124 ++-- MG/MG_GL/State/Buffer/BufferState.h | 14 +- MG/MG_GL/State/Core/GLState.cpp | 18 +- MG/MG_GL/State/Core/GLState.h | 8 +- 6 files changed, 412 insertions(+), 355 deletions(-) diff --git a/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp b/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp index f6d567d9..70c43f1b 100644 --- a/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp +++ b/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp @@ -39,7 +39,14 @@ namespace MG_GL::GL { void BindBuffer(GLenum target, GLuint buffer) { MG_Util::Debug::LogD("glBindBuffer, target: %d, buffer: %d", target, buffer); - if (buffer != 0 && !MG_State::ValidateBufferHandle(buffer)) { + if (buffer != 0 && + MG_State::ValidateGeneratedName(buffer) && + !MG_State::ValidateAllocatedBufferHandle(buffer)) { + MG_Util::Debug::LogE("Actually creating buffer: %u", buffer); + GLenum result = MG_State::CreateBuffer(buffer); + } + + if (buffer != 0 && !MG_State::ValidateAllocatedBufferHandle(buffer)) { MG_State::SetError(GL_INVALID_VALUE); MG_Util::Debug::LogE("Invalid buffer handle: %u", buffer); return; @@ -80,9 +87,9 @@ namespace MG_GL::GL { return; } - GLenum result = MG_State::CreateBuffers(n, buffers); + GLenum result = MG_State::GenBufferNames(n, buffers); if (result == GL_NO_ERROR) { - MG_Util::Debug::LogD("Generated buffers:"); + MG_Util::Debug::LogD("Generated buffer names:"); for (GLsizei i = 0; i < n; ++i) { MG_Util::Debug::LogD(" Buffer[%d] = %u", i, buffers[i]); } @@ -100,7 +107,7 @@ namespace MG_GL::GL { return GL_FALSE; } - bool isValid = MG_State::ValidateBufferHandle(buffer); + bool isValid = MG_State::ValidateAllocatedBufferHandle(buffer); // Should we report gl error here or in MG_State? MG_Util::Debug::LogD("Buffer %u is %s", buffer, isValid ? "valid" : "invalid"); return isValid ? GL_TRUE : GL_FALSE; diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index f4152177..240a2630 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -180,43 +180,138 @@ namespace MG_GL::GL { } - static std::unordered_map s_bufferDirtyFlags_bufferObj; +// static std::unordered_map s_bufferDirtyFlags_bufferObj; void SyncAllBuffersToGLES(BufferState* bufferState) { - GLint currentVBO = 0, currentEBO = 0; - CallAndCheck(::GLES::glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤tVBO);) - CallAndCheck(::GLES::glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤tEBO);) - for (auto& [mgBufferId, bufferObj] : bufferState->buffers_) { - if (!bufferObj.generated) continue; - - // TODO: Check if the buffer changes rather than always update it. - if (s_bufferMap.find(mgBufferId) == s_bufferMap.end() || true) { - if (s_bufferMap.find(mgBufferId) == s_bufferMap.end()) { - GLuint glBuffer; - CallAndCheck(::GLES::glGenBuffers(1, &glBuffer);) - s_bufferMap[mgBufferId] = glBuffer; - } - CallAndCheck(::GLES::glBindBuffer(bufferObj.target, s_bufferMap[mgBufferId]);) - CallAndCheck(::GLES::glBufferData( - bufferObj.target, - bufferObj.data.size(), - bufferObj.data.data(), - bufferObj.usage - );) - s_bufferDirtyFlags_bufferObj[mgBufferId] = bufferObj.data.data(); + GLint prev_vbo = 0; + CallAndCheck(::GLES::glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev_vbo);) + + for (auto& [mgname, obj] : bufferState->buffers_) { + if (!obj.generated) + continue; + + // Gen real buffers at ES + if (s_bufferMap.find(mgname) == s_bufferMap.end()) { + GLuint glname; + CallAndCheck(::GLES::glGenBuffers(1, &glname);) + s_bufferMap[mgname] = glname; } + + GLuint glname = s_bufferMap[mgname]; + + // Populate data to ES + CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, glname);) + CallAndCheck(::GLES::glBufferData( + GL_ARRAY_BUFFER, + obj.data.size(), + obj.data.data(), + obj.usage);) + +// s_bufferDirtyFlags_bufferObj[mgname] = obj.data.data(); } - CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, currentVBO);) - CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, currentEBO);) + CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, prev_vbo);) } - void DrawArraysSHITTILY(GLenum mode, GLint first, GLsizei count) { - - } static GLuint lastBoundVAO = 0; static GLuint lastBoundProgram = 0; static GLuint lastBoundFBO[2] = {0}; static std::array lastBoundTextures; + + void RealizeFBOState(GLenum fbtype) { + FramebufferState* fbState = MG_State_T::framebufferState; + + GLuint fb = fbState->currentBindings_[fbtype]; + if (fb != lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1]) { + GLuint glFBO = 0; + + if (fb == 0) { + CallAndCheck(::GLES::glBindFramebuffer(fbtype, 0);) + glFBO = 0; + } else { + bool isNewGlesFBO = false; + + if (s_framebufferMap.find(fb) == s_framebufferMap.end()) { + CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) + s_framebufferMap[fb] = glFBO; + CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) + MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, fb); + isNewGlesFBO = true; + } else { + glFBO = s_framebufferMap[fb]; + CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) + MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, fb); + } + + if (glFBO != 0) { + FramebufferObject* mgFBO = fbState->GetCurrentFBO(fbtype); + if (mgFBO) { + MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, fb); + + for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { + + if (mgAtt.type != GL_TEXTURE_2D) { + MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, fb); + continue; + } + + GLuint expectedGLTexId = 0; + if (mgAtt.handle != 0) { + if (s_textureMap.count(mgAtt.handle)) { + expectedGLTexId = s_textureMap[mgAtt.handle]; + } else { + MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, fb, mgAttachmentPoint); + for (auto const& [key, val] : s_textureMap) + MG_Util::Debug::LogW(" key: %d, val: %d", key, val); + continue; + } + } + + GLint glesAttachedType = 0; + GLint glesAttachedName = 0; + + CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) + + if (glesAttachedType == GL_TEXTURE) { + CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) + } else if (glesAttachedType != GL_NONE) { + MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); + } + + if ((GLuint)glesAttachedName != expectedGLTexId) { + MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", + fb, mgAttachmentPoint, expectedGLTexId, glesAttachedName); + + CallAndCheck(::GLES::glFramebufferTexture2D( + fbtype, + mgAttachmentPoint, + GL_TEXTURE_2D, + expectedGLTexId, + mgAtt.mipLevel + );) + } + } + + GLenum status = ::GLES::glCheckFramebufferStatus(fbtype); + if (status != GL_FRAMEBUFFER_COMPLETE) { + MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", + fb, glFBO, status, MG_Util::Debug::GLEnumToString(status)); + } + + } else { + MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", fb); + } + } + } + + lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1] = fb; + } + } + + + void DrawArraysSHITTILY(GLenum mode, GLint first, GLsizei count) { + + } + void DrawElementsSHITTILY(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { CommonState* commonState = MG_State_T::commonState; TextureState* textureState = MG_State_T::textureState; @@ -578,95 +673,97 @@ namespace MG_GL::GL { } // Framebuffer - GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; - if (currentFBO != lastBoundFBO[0]) { - GLuint glFBO = 0; + RealizeFBOState(GL_DRAW_FRAMEBUFFER); +// GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; +// if (currentFBO != lastBoundFBO[0]) { +// GLuint glFBO = 0; +// +// if (currentFBO == 0) { +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, 0);) +// glFBO = 0; +// } else { +// bool isNewGlesFBO = false; +// +// if (s_framebufferMap.find(currentFBO) == s_framebufferMap.end()) { +// CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) +// s_framebufferMap[currentFBO] = glFBO; +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) +// MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); +// GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); +// if (status != GL_FRAMEBUFFER_COMPLETE) { +// MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after creation! Status: 0x%X (%s)", +// currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); +// } +// isNewGlesFBO = true; +// } else { +// glFBO = s_framebufferMap[currentFBO]; +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) +// MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); +// } +// +// if (glFBO != 0) { +// FramebufferObject* mgFBO = fbState->GetCurrentFBO(GL_DRAW_FRAMEBUFFER); +// if (mgFBO) { +// MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, currentFBO); +// +// for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { +// +// if (mgAtt.type != GL_TEXTURE_2D) { +// MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, currentFBO); +// continue; +// } +// +// GLuint expectedGLTexId = 0; +// if (mgAtt.handle != 0) { +// if (s_textureMap.count(mgAtt.handle)) { +// expectedGLTexId = s_textureMap[mgAtt.handle]; +// } else { +// MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, currentFBO, mgAttachmentPoint); +// for (auto const& [key, val] : s_textureMap) +// MG_Util::Debug::LogW(" key: %d, val: %d", key, val); +// continue; +// } +// } +// +// GLint glesAttachedType = 0; +// GLint glesAttachedName = 0; +// +// CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) +// +// if (glesAttachedType == GL_TEXTURE) { +// CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) +// } else if (glesAttachedType != GL_NONE) { +// MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); +// } +// +// if ((GLuint)glesAttachedName != expectedGLTexId) { +// MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", +// currentFBO, mgAttachmentPoint, expectedGLTexId, glesAttachedName); +// +// CallAndCheck(::GLES::glFramebufferTexture2D( +// GL_FRAMEBUFFER, +// mgAttachmentPoint, +// GL_TEXTURE_2D, +// expectedGLTexId, +// mgAtt.mipLevel +// );) +// } +// } +// GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); +// if (status != GL_FRAMEBUFFER_COMPLETE) { +// MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", +// currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); +// } +// +// } else { +// MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", currentFBO); +// } +// } +// } +// +// lastBoundFBO[0] = currentFBO; +// } - if (currentFBO == 0) { - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, 0);) - glFBO = 0; - } else { - bool isNewGlesFBO = false; - - if (s_framebufferMap.find(currentFBO) == s_framebufferMap.end()) { - CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) - s_framebufferMap[currentFBO] = glFBO; - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) - MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); - GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after creation! Status: 0x%X (%s)", - currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); - } - isNewGlesFBO = true; - } else { - glFBO = s_framebufferMap[currentFBO]; - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) - MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); - } - - if (glFBO != 0) { - FramebufferObject* mgFBO = fbState->GetCurrentFBO(GL_DRAW_FRAMEBUFFER); - if (mgFBO) { - MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, currentFBO); - - for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { - - if (mgAtt.type != GL_TEXTURE_2D) { - MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, currentFBO); - continue; - } - - GLuint expectedGLTexId = 0; - if (mgAtt.handle != 0) { - if (s_textureMap.count(mgAtt.handle)) { - expectedGLTexId = s_textureMap[mgAtt.handle]; - } else { - MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, currentFBO, mgAttachmentPoint); - for (auto const& [key, val] : s_textureMap) - MG_Util::Debug::LogW(" key: %d, val: %d", key, val); - continue; - } - } - - GLint glesAttachedType = 0; - GLint glesAttachedName = 0; - - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) - - if (glesAttachedType == GL_TEXTURE) { - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) - } else if (glesAttachedType != GL_NONE) { - MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); - } - - if ((GLuint)glesAttachedName != expectedGLTexId) { - MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", - currentFBO, mgAttachmentPoint, expectedGLTexId, glesAttachedName); - - CallAndCheck(::GLES::glFramebufferTexture2D( - GL_FRAMEBUFFER, - mgAttachmentPoint, - GL_TEXTURE_2D, - expectedGLTexId, - mgAtt.mipLevel - );) - } - } - GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", - currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); - } - - } else { - MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", currentFBO); - } - } - } - - lastBoundFBO[0] = currentFBO; - } if (vao->elementBuffer != 0 || indices != nullptr) { @@ -679,96 +776,6 @@ namespace MG_GL::GL { } } - void RealizeFBOState(GLenum fbtype) { - FramebufferState* fbState = MG_State_T::framebufferState; - - GLuint fb = fbState->currentBindings_[fbtype]; - if (fb != lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1]) { - GLuint glFBO = 0; - - if (fb == 0) { - CallAndCheck(::GLES::glBindFramebuffer(fbtype, 0);) - glFBO = 0; - } else { - bool isNewGlesFBO = false; - - if (s_framebufferMap.find(fb) == s_framebufferMap.end()) { - CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) - s_framebufferMap[fb] = glFBO; - CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) - MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, fb); - isNewGlesFBO = true; - } else { - glFBO = s_framebufferMap[fb]; - CallAndCheck(::GLES::glBindFramebuffer(fbtype, glFBO);) - MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, fb); - } - - if (glFBO != 0) { - FramebufferObject* mgFBO = fbState->GetCurrentFBO(fbtype); - if (mgFBO) { - MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, fb); - - for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { - - if (mgAtt.type != GL_TEXTURE_2D) { - MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, fb); - continue; - } - - GLuint expectedGLTexId = 0; - if (mgAtt.handle != 0) { - if (s_textureMap.count(mgAtt.handle)) { - expectedGLTexId = s_textureMap[mgAtt.handle]; - } else { - MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, fb, mgAttachmentPoint); - for (auto const& [key, val] : s_textureMap) - MG_Util::Debug::LogW(" key: %d, val: %d", key, val); - continue; - } - } - - GLint glesAttachedType = 0; - GLint glesAttachedName = 0; - - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) - - if (glesAttachedType == GL_TEXTURE) { - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(fbtype, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) - } else if (glesAttachedType != GL_NONE) { - MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); - } - - if ((GLuint)glesAttachedName != expectedGLTexId) { - MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", - fb, mgAttachmentPoint, expectedGLTexId, glesAttachedName); - - CallAndCheck(::GLES::glFramebufferTexture2D( - fbtype, - mgAttachmentPoint, - GL_TEXTURE_2D, - expectedGLTexId, - mgAtt.mipLevel - );) - } - } - - GLenum status = ::GLES::glCheckFramebufferStatus(fbtype); - if (status != GL_FRAMEBUFFER_COMPLETE) { - MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", - fb, glFBO, status, MG_Util::Debug::GLEnumToString(status)); - } - - } else { - MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", fb); - } - } - } - - lastBoundFBO[(fbtype == GL_DRAW_FRAMEBUFFER) ? 0 : 1] = fb; - } - } - void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, @@ -801,92 +808,93 @@ namespace MG_GL::GL { CommonState* commonState = MG_State_T::commonState; FramebufferState* fbState = MG_State_T::framebufferState; - GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; - if (currentFBO != lastBoundFBO[0]) { - GLuint glFBO = 0; - - if (currentFBO == 0) { - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, 0);) - glFBO = 0; - } else { - bool isNewGlesFBO = false; - - if (s_framebufferMap.find(currentFBO) == s_framebufferMap.end()) { - CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) - s_framebufferMap[currentFBO] = glFBO; - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) - MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); - isNewGlesFBO = true; - } else { - glFBO = s_framebufferMap[currentFBO]; - CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) - MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); - } - - if (glFBO != 0) { - FramebufferObject* mgFBO = fbState->GetCurrentFBO(GL_DRAW_FRAMEBUFFER); - if (mgFBO) { - MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, currentFBO); - - for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { - - if (mgAtt.type != GL_TEXTURE_2D) { - MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, currentFBO); - continue; - } - - GLuint expectedGLTexId = 0; - if (mgAtt.handle != 0) { - if (s_textureMap.count(mgAtt.handle)) { - expectedGLTexId = s_textureMap[mgAtt.handle]; - } else { - MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, currentFBO, mgAttachmentPoint); - for (auto const& [key, val] : s_textureMap) - MG_Util::Debug::LogW(" key: %d, val: %d", key, val); - continue; - } - } - - GLint glesAttachedType = 0; - GLint glesAttachedName = 0; - - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) - - if (glesAttachedType == GL_TEXTURE) { - CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) - } else if (glesAttachedType != GL_NONE) { - MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); - } - - if ((GLuint)glesAttachedName != expectedGLTexId) { - MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", - currentFBO, mgAttachmentPoint, expectedGLTexId, glesAttachedName); - - CallAndCheck(::GLES::glFramebufferTexture2D( - GL_FRAMEBUFFER, - mgAttachmentPoint, - GL_TEXTURE_2D, - expectedGLTexId, - mgAtt.mipLevel - );) - } - } - - GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", - currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); - } - - } else { - MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", currentFBO); - } - } - } - - lastBoundFBO[0] = currentFBO; - } +// GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; +// if (currentFBO != lastBoundFBO[0]) { +// GLuint glFBO = 0; +// +// if (currentFBO == 0) { +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, 0);) +// glFBO = 0; +// } else { +// bool isNewGlesFBO = false; +// +// if (s_framebufferMap.find(currentFBO) == s_framebufferMap.end()) { +// CallAndCheck(::GLES::glGenFramebuffers(1, &glFBO);) +// s_framebufferMap[currentFBO] = glFBO; +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) +// MG_Util::Debug::LogD("Generated and bound new GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); +// isNewGlesFBO = true; +// } else { +// glFBO = s_framebufferMap[currentFBO]; +// CallAndCheck(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, glFBO);) +// MG_Util::Debug::LogD("Bound existing GLES FBO %u for MobileGL FBO %u", glFBO, currentFBO); +// } +// +// if (glFBO != 0) { +// FramebufferObject* mgFBO = fbState->GetCurrentFBO(GL_DRAW_FRAMEBUFFER); +// if (mgFBO) { +// MG_Util::Debug::LogD("Checking/Syncing attachments for GLES FBO %u (MobileGL FBO %u)", glFBO, currentFBO); +// +// for (auto const& [mgAttachmentPoint, mgAtt] : mgFBO->attachments) { +// +// if (mgAtt.type != GL_TEXTURE_2D) { +// MG_Util::Debug::LogW("Skipping non-TEXTURE_2D attachment 0x%X for FBO %u", mgAttachmentPoint, currentFBO); +// continue; +// } +// +// GLuint expectedGLTexId = 0; +// if (mgAtt.handle != 0) { +// if (s_textureMap.count(mgAtt.handle)) { +// expectedGLTexId = s_textureMap[mgAtt.handle]; +// } else { +// MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, currentFBO, mgAttachmentPoint); +// for (auto const& [key, val] : s_textureMap) +// MG_Util::Debug::LogW(" key: %d, val: %d", key, val); +// continue; +// } +// } +// +// GLint glesAttachedType = 0; +// GLint glesAttachedName = 0; +// +// CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) +// +// if (glesAttachedType == GL_TEXTURE) { +// CallAndCheck(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &glesAttachedName);) +// } else if (glesAttachedType != GL_NONE) { +// MG_Util::Debug::LogW("GLES FBO %u attachment 0x%X has non-texture type 0x%X (expected GL_TEXTURE or GL_NONE)", glFBO, mgAttachmentPoint, glesAttachedType); +// } +// +// if ((GLuint)glesAttachedName != expectedGLTexId) { +// MG_Util::Debug::LogD("Syncing FBO %u attachment 0x%X: Expected GLES TexID %u, Found GLES ObjName %d. Attaching/Detaching...", +// currentFBO, mgAttachmentPoint, expectedGLTexId, glesAttachedName); +// +// CallAndCheck(::GLES::glFramebufferTexture2D( +// GL_FRAMEBUFFER, +// mgAttachmentPoint, +// GL_TEXTURE_2D, +// expectedGLTexId, +// mgAtt.mipLevel +// );) +// } +// } +// +// GLenum status = ::GLES::glCheckFramebufferStatus(GL_FRAMEBUFFER); +// if (status != GL_FRAMEBUFFER_COMPLETE) { +// MG_Util::Debug::LogE("Framebuffer %u (GLES FBO %u) is not complete after sync! Status: 0x%X (%s)", +// currentFBO, glFBO, status, MG_Util::Debug::GLEnumToString(status)); +// } +// +// } else { +// MG_Util::Debug::LogW("Could not get MobileGL FBO object for ID %u during attachment sync.", currentFBO); +// } +// } +// } +// +// lastBoundFBO[0] = currentFBO; +// } + RealizeFBOState(GL_DRAW_FRAMEBUFFER); static GLfloat lastClearColor[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; if (memcmp(lastClearColor, commonState->clearColor, sizeof(lastClearColor)) != 0) { diff --git a/MG/MG_GL/State/Buffer/BufferState.cpp b/MG/MG_GL/State/Buffer/BufferState.cpp index 4dde0d53..6b3a997b 100644 --- a/MG/MG_GL/State/Buffer/BufferState.cpp +++ b/MG/MG_GL/State/Buffer/BufferState.cpp @@ -6,56 +6,79 @@ #include "BufferState.h" -GLenum BufferState::Create(GLuint* buffer) { - MG_Util::Debug::LogD("MG_State: Buffer: Create called"); - if (!buffer) return GL_INVALID_VALUE; +GLenum BufferState::GenName(GLuint *buffer) { + MG_Util::Debug::LogD("MG_State: Buffer: GenName"); + if (!buffer) + return GL_INVALID_VALUE; GLuint id = 0; - if (!freeIds_.empty()) { - id = *freeIds_.begin(); - freeIds_.erase(freeIds_.begin()); + if (freeId_.empty()) { + id = lastId_++; } else { - id = ++lastId_; + id = freeId_.back(); + freeId_.pop_back(); } + *buffer = id; - BufferObject& obj = buffers_[id]; - MG_Util::Debug::LogD("MG_State: Buffer: Create created buffer %d", id); + MG_Util::Debug::LogD("MG_State: Buffer: Gen new name %d", id); + + return GL_NO_ERROR; +} + +GLenum BufferState::GenNameN(GLsizei n, GLuint* buffers) { + MG_Util::Debug::LogD("MG_State: Buffer: GenNameN called with n=%d", n); + if (n < 0) return GL_INVALID_VALUE; + + for (GLsizei i = 0; i < n; ++i) { + GLenum result = GenName(&buffers[i]); + if (result != GL_NO_ERROR) { + MG_Util::Debug::LogE("MG_State: Buffer: GenNameN failed with error 0x%x", result); + return result; + } + } + MG_Util::Debug::LogD("MG_State: Buffer: GenNameN created buffers successfully"); + return GL_NO_ERROR; +} + +GLenum BufferState::Create(GLuint buffer) { + MG_Util::Debug::LogD("MG_State: Buffer: Create called"); + if (!buffer) + return GL_INVALID_VALUE; + + if (ValidateAllocatedHandle(buffer)) + return GL_INVALID_VALUE; + + BufferObject& obj = buffers_[buffer]; + MG_Util::Debug::LogD("MG_State: Buffer: Create created buffer %d", buffer); obj.generated = true; return GL_NO_ERROR; } -GLenum BufferState::CreateN(GLsizei n, GLuint* buffers) { - MG_Util::Debug::LogD("MG_State: Buffer: CreateN called with n=%d", n); - if (n < 0) return GL_INVALID_VALUE; - - for (GLsizei i = 0; i < n; ++i) { - GLenum result = Create(&buffers[i]); - if (result != GL_NO_ERROR) { - MG_Util::Debug::LogE("MG_State: Buffer: CreateN create buffer failed with error 0x%x", result); - return result; - } - } - MG_Util::Debug::LogD("MG_State: Buffer: CreateN created buffers successfully"); - return GL_NO_ERROR; -} +//GLenum BufferState::CreateN(GLsizei n, GLuint* buffers) { +// MG_Util::Debug::LogD("MG_State: Buffer: CreateN called with n=%d", n); +// if (n < 0) return GL_INVALID_VALUE; +// +// for (GLsizei i = 0; i < n; ++i) { +// GLenum result = Create(&buffers[i]); +// if (result != GL_NO_ERROR) { +// MG_Util::Debug::LogE("MG_State: Buffer: CreateN create buffer failed with error 0x%x", result); +// return result; +// } +// } +// MG_Util::Debug::LogD("MG_State: Buffer: CreateN created buffers successfully"); +// return GL_NO_ERROR; +//} GLenum BufferState::Bind(GLenum target, GLuint buffer) { - if (!IsValidTarget_(target)) return GL_INVALID_ENUM; - MG_Util::Debug::LogD("MG_State: Buffer: Bind called with target=0x%x, buffer=%u", target, buffer); + // We don't handle unallocated buffer names here, just plain bind - if (buffer != 0) { - auto it = buffers_.find(buffer); - if (it == buffers_.end()) { - buffers_[buffer]; - } else { - BufferObject& obj = it->second; - if (obj.target != 0 && obj.target != target) { - MG_Util::Debug::LogE("MG_State: Buffer: Bind can not bind a buffer to different target 0x%x, current bind target is 0x%x", target, obj.target); - return GL_INVALID_OPERATION; - } - } - buffers_[buffer].target = target; + if (!IsValidTarget_(target)) return GL_INVALID_ENUM; + MG_Util::Debug::LogD("MG_State: Buffer: Bind called with target=%s, buffer=%u", MG_Util::Debug::GLEnumToString(target), buffer); + + if (buffer != 0 && !ValidateAllocatedHandle(buffer)) { + MG_Util::Debug::LogE("MG_State: Buffer: Binding invalid buffer %d to %s", buffer, MG_Util::Debug::GLEnumToString(target)); + return GL_INVALID_OPERATION; } currentBindings_[target] = buffer; @@ -119,18 +142,25 @@ GLenum BufferState::ReleaseBufferMemory(GLenum target) { return GL_NO_ERROR; } -bool BufferState::ValidateHandle(GLuint buffer) { - bool isvalid = buffers_.count(buffer) > 0; - MG_Util::Debug::LogD("MG_State: Buffer: ValidateHandle called on buffer %u returns %d", buffer, isvalid); - return isvalid; +bool BufferState::ValidateAllocatedHandle(GLuint buffer) { + bool isValid = buffers_.find(buffer) != buffers_.end(); + MG_Util::Debug::LogD("MG_State: Buffer: ValidateAllocatedHandle called on buffer %u returns %d", buffer, isValid); + return isValid; +} + +bool BufferState::ValidateGeneratedName(GLuint buffer) { + bool inFreeList = std::find(freeId_.begin(), freeId_.end(), buffer) != freeId_.end(); + bool lessThanLast = buffer < lastId_; // lastId_ is not generated yet + MG_Util::Debug::LogD("MG_State: Buffer: ValidateAllocatedHandle called on buffer %u returns %d", buffer, lessThanLast && !inFreeList); + return lessThanLast && !inFreeList; } void BufferState::Delete(GLuint buffer) { - if (buffers_.erase(buffer)) { - freeIds_.insert(buffer); - for (auto& [target, id] : currentBindings_) { - if (id == buffer) id = 0; - } + buffers_.erase(buffer); + if (ValidateGeneratedName(buffer)) + freeId_.emplace_back(buffer); + for (auto& [target, id] : currentBindings_) { + if (id == buffer) id = 0; } MG_Util::Debug::LogD("MG_State: Buffer: Delete buffer %u", buffer); } @@ -179,7 +209,7 @@ GLenum BufferState::QueryPropertyIntVector(GLenum target, GLenum pname, GLint* p case GL_BUFFER_USAGE: *params = static_cast(buffer.usage); break; - MG_Util::Debug::LogD("MG_State: Buffer: QueryPropertyIntVector Query info about buffer %u succeed",buffer.target); + MG_Util::Debug::LogD("MG_State: Buffer: QueryPropertyIntVector Query info about buffer %u succeed", target); default: return GL_INVALID_ENUM; } diff --git a/MG/MG_GL/State/Buffer/BufferState.h b/MG/MG_GL/State/Buffer/BufferState.h index 948953f3..4e8c54ac 100644 --- a/MG/MG_GL/State/Buffer/BufferState.h +++ b/MG/MG_GL/State/Buffer/BufferState.h @@ -11,7 +11,6 @@ class BufferState { public: struct BufferObject { - GLenum target = 0; GLenum usage = GL_STATIC_DRAW; std::vector data; bool isMapped = false; @@ -20,23 +19,26 @@ public: }; // Return: the validity of the operation, according to OpenGL 3 standard - GLenum Create(GLuint* buffer); - GLenum CreateN(GLsizei n, GLuint* buffers); + GLenum GenName(GLuint* buffer); + GLenum GenNameN(GLsizei n, GLuint* buffers); + GLenum Create(GLuint buffer); +// GLenum CreateN(GLsizei n, GLuint* buffers); GLenum Bind(GLenum target, GLuint buffer); GLenum CommitStorage(GLenum target, GLsizeiptr size, const void* data, GLenum usage); GLenum AcquireBufferMemory(GLenum target, GLenum access, void** mappedPointer); GLenum ReleaseBufferMemory(GLenum target); GLenum QueryPropertyIntVector(GLenum target, GLenum pname, GLint* params) const; - bool ValidateHandle(GLuint buffer); + bool ValidateAllocatedHandle(GLuint buffer); + bool ValidateGeneratedName(GLuint buffer); void Delete(GLuint buffer); GLuint GetCurrentBinding(GLenum target) const; std::unordered_map currentBindings_; std::unordered_map buffers_; private: - std::set freeIds_; - GLuint lastId_ = 0; + std::vector freeId_; + GLuint lastId_ = 1; static bool IsValidTarget_(GLenum target); }; diff --git a/MG/MG_GL/State/Core/GLState.cpp b/MG/MG_GL/State/Core/GLState.cpp index f7f5f290..8638d52b 100644 --- a/MG/MG_GL/State/Core/GLState.cpp +++ b/MG/MG_GL/State/Core/GLState.cpp @@ -160,12 +160,16 @@ namespace MG_State { return MG_State_T::bufferState->ReleaseBufferMemory(target); } - GLenum CreateBuffer(GLuint* buffer) { + GLenum CreateBuffer(GLuint buffer) { return MG_State_T::bufferState->Create(buffer); } - GLenum CreateBuffers(GLsizei n, GLuint* buffers) { - return MG_State_T::bufferState->CreateN(n, buffers); +// GLenum CreateBuffers(GLsizei n, GLuint* buffers) { +// return MG_State_T::bufferState->CreateN(n, buffers); +// } + + GLenum GenBufferNames(GLsizei n, GLuint* buffers) { + return MG_State_T::bufferState->GenNameN(n, buffers); } GLenum BindBuffer(GLenum target, GLuint buffer) { @@ -181,8 +185,12 @@ namespace MG_State { return MG_State_T::bufferState->CommitStorage(target, size, data, usage); } - bool ValidateBufferHandle(GLuint buffer) { - return MG_State_T::bufferState->ValidateHandle(buffer); + bool ValidateAllocatedBufferHandle(GLuint buffer) { + return MG_State_T::bufferState->ValidateAllocatedHandle(buffer); + } + + bool ValidateGeneratedName(GLuint buffer) { + return MG_State_T::bufferState->ValidateGeneratedName(buffer); } void DeleteBuffer(GLuint buffer) { diff --git a/MG/MG_GL/State/Core/GLState.h b/MG/MG_GL/State/Core/GLState.h index e86b040b..5b667071 100644 --- a/MG/MG_GL/State/Core/GLState.h +++ b/MG/MG_GL/State/Core/GLState.h @@ -58,11 +58,13 @@ namespace MG_State { // Buffer GLenum AcquireBufferMemory(GLenum target, GLenum access, void** mappedPtr); GLenum ReleaseBufferMemory(GLenum target); - GLenum CreateBuffer(GLuint* buffer); - GLenum CreateBuffers(GLsizei n, GLuint* buffers); + GLenum CreateBuffer(GLuint buffer); +// GLenum CreateBuffers(GLsizei n, GLuint* buffers); + GLenum GenBufferNames(GLsizei n, GLuint* buffers); GLenum BindBuffer(GLenum target, GLuint buffer); GLenum CommitBufferStorage(GLenum target, GLsizeiptr size, const void* data, GLenum usage); - bool ValidateBufferHandle(GLuint buffer); + bool ValidateAllocatedBufferHandle(GLuint buffer); + bool ValidateGeneratedName(GLuint buffer); void DeleteBuffer(GLuint buffer); GLenum QueryBufferPropertyIntVector(GLenum target, GLenum pname, GLint* params); From a17497522a6f49e2df43ba66b279c270e353f2b9 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 16:22:08 +0800 Subject: [PATCH 03/21] [Misc] more GLenum to string --- MG/Global.h | 2 +- MG/MG_UTIL/Debug/Debug.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MG/Global.h b/MG/Global.h index 61adf95d..2fb584b4 100644 --- a/MG/Global.h +++ b/MG/Global.h @@ -27,7 +27,7 @@ namespace MG_Global { } namespace Common { - inline const int LogLevel = MG_Constants::Common::LOG_LEVEL_INFO; + inline const int LogLevel = MG_Constants::Common::LOG_LEVEL_DEBUG; #ifdef __ANDROID__ inline const char* LOG_FILE_PATH = "/sdcard/MG/latest.log"; diff --git a/MG/MG_UTIL/Debug/Debug.cpp b/MG/MG_UTIL/Debug/Debug.cpp index ddaac4e2..7352c2ea 100644 --- a/MG/MG_UTIL/Debug/Debug.cpp +++ b/MG/MG_UTIL/Debug/Debug.cpp @@ -1206,6 +1206,12 @@ void Log##name(const char* format, ...) { \ CASE(GL_DOT3_RGBA) /* texture_border_clamp */ CASE(GL_CLAMP_TO_BORDER) +/* framebuffer_status */ + CASE(GL_FRAMEBUFFER_COMPLETE) + CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) + CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) + CASE(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) + CASE(GL_FRAMEBUFFER_UNSUPPORTED) /* * Miscellaneous */ From d1eb0296713f261dba86eb20e5d4d9067976ff3d Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 17:15:50 +0800 Subject: [PATCH 04/21] [Fix] some format quirk --- .../Implementations/GL/Drawing/GL_Drawing.cpp | 34 ++++++++++++++----- .../GL/Framebuffer/GL_Framebuffer.cpp | 1 + MG/MG_UTIL/Debug/Debug.cpp | 3 ++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index 240a2630..d232454d 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -123,15 +123,22 @@ namespace MG_GL::GL { for (auto& [level, mip] : texObj.params.mipmapData) { const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr; switch (target) { - case GL_TEXTURE_2D: - CallAndCheck(::GLES::glTexImage2D( - target, level, mip.internalFormat, - mip.width, mip.height, 0, - mip.format, mip.type, data - );) + case GL_TEXTURE_2D: { + GLenum type = mip.type; + // Mali cannot use GL_FLOAT as depth format + if (mip.internalFormat == GL_DEPTH_COMPONENT) { + type = GL_UNSIGNED_INT; + } + CallAndCheck(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, type, data + );) s_textureLevelUploaded[mgTexId][level] = true; - MG_Util::Debug::LogD("Initial upload texture %u level %d (size=%zu)", mgTexId, level, mip.pixelData.size()); + MG_Util::Debug::LogD("Initial upload texture %u level %d (size=%zu)", + mgTexId, level, mip.pixelData.size()); break; + } default: MG_Util::Debug::LogE("Unsupported target: %s", MG_Util::Debug::GLEnumToString(target)); } @@ -256,10 +263,10 @@ namespace MG_GL::GL { GLuint expectedGLTexId = 0; if (mgAtt.handle != 0) { - if (s_textureMap.count(mgAtt.handle)) { + if (s_textureMap.find(mgAtt.handle) != s_textureMap.end()) { expectedGLTexId = s_textureMap[mgAtt.handle]; } else { - MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in s_textureMap during FBO sync!", mgAtt.handle, fb, mgAttachmentPoint); + MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment %s not found in s_textureMap during FBO sync!", mgAtt.handle, fb, MG_Util::Debug::GLEnumToString(mgAttachmentPoint)); for (auto const& [key, val] : s_textureMap) MG_Util::Debug::LogW(" key: %d, val: %d", key, val); continue; @@ -788,6 +795,10 @@ namespace MG_GL::GL { GLenum filter) { MG_Util::Debug::LogD("BlitFramebuffer, srcX0=%d, srcY0=%d, srcX1=%d, srcY1=%d, dstX0=%d, dstY0=%d, dstX1=%d, dstY1=%d, mask=0x%x, filter=%s", srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, MG_Util::Debug::GLEnumToString(filter)); + TextureState* textureState = MG_State_T::textureState; + + // Texture + SyncAllTexturesToGLES(textureState); // Realize FBO states RealizeFBOState(GL_DRAW_FRAMEBUFFER); @@ -894,6 +905,11 @@ namespace MG_GL::GL { // lastBoundFBO[0] = currentFBO; // } + TextureState* textureState = MG_State_T::textureState; + + // Texture + SyncAllTexturesToGLES(textureState); + RealizeFBOState(GL_DRAW_FRAMEBUFFER); static GLfloat lastClearColor[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; diff --git a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp index 7a4945bc..562c03cc 100644 --- a/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp +++ b/MG/MG_GL/Implementations/GL/Framebuffer/GL_Framebuffer.cpp @@ -45,6 +45,7 @@ namespace MG_GL::GL { GLenum result = MG_State::AttachTexture2DToFramebuffer( target, attachment, textarget, texture, level ); + if (result == GL_NO_ERROR) return; MG_State::SetError(result); MG_Util::Debug::LogE("Texture attachment failed: %s", MG_Util::Debug::GLEnumToString(result)); diff --git a/MG/MG_UTIL/Debug/Debug.cpp b/MG/MG_UTIL/Debug/Debug.cpp index 7352c2ea..5add9b7e 100644 --- a/MG/MG_UTIL/Debug/Debug.cpp +++ b/MG/MG_UTIL/Debug/Debug.cpp @@ -1212,6 +1212,9 @@ void Log##name(const char* format, ...) { \ CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) CASE(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) CASE(GL_FRAMEBUFFER_UNSUPPORTED) + CASE(GL_COLOR_ATTACHMENT0) + CASE(GL_DEPTH_ATTACHMENT) + CASE(GL_STENCIL_ATTACHMENT) /* * Miscellaneous */ From fc0396705f983bbf30651eaa5631864c33420881 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 17:44:37 +0800 Subject: [PATCH 05/21] [Feat] NormalizePixelFormat --- .../Implementations/GL/Drawing/GL_Drawing.cpp | 307 +++++++++++++++++- 1 file changed, 300 insertions(+), 7 deletions(-) diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index d232454d..ad133455 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -8,6 +8,296 @@ #include "../../../../Includes.h" namespace MG_GL::GL { + void NormalizePixelFormat(GLenum internalFormat, GLenum type, GLenum format, GLenum* outInternalFormat, GLenum* outType, GLenum* outFormat) { +// if (format && *format == GL_BGRA) +// *format = GL_RGBA; + switch (internalFormat) { + case GL_DEPTH_COMPONENT16: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_SHORT; + if (outFormat) + *outFormat = format; + break; + + case GL_DEPTH_COMPONENT24: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT; + if (outFormat) + *outFormat = format; + break; + + case GL_DEPTH_COMPONENT32: + if (outInternalFormat) + *outInternalFormat = GL_DEPTH_COMPONENT32F; + if (outType) + *outType = GL_UNSIGNED_INT; + if (outFormat) + *outFormat = format; + break; + + case GL_DEPTH_COMPONENT32F: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_FLOAT; + if (outFormat) + *outFormat = format; + break; + + case GL_DEPTH_COMPONENT: + // TODO: Add enableCompatibleMode option + MG_Util::Debug::LogD("Find GL_DEPTH_COMPONENT: internalFormat: %s, format: %s, type: %s", + MG_Util::Debug::GLEnumToString(internalFormat), MG_Util::Debug::GLEnumToString(format), MG_Util::Debug::GLEnumToString(type)); + + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT; + if (outFormat) + *outFormat = format; + break; + + case GL_DEPTH_STENCIL: + if (outInternalFormat) + *outInternalFormat = GL_DEPTH32F_STENCIL8; + if (outType) + *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + if (outFormat) + *outFormat = format; + break; + + case GL_RGB10_A2: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT_2_10_10_10_REV; + if (outFormat) + *outFormat = format; + break; + + case GL_RGB5_A1: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_SHORT_5_5_5_1; + if (outFormat) + *outFormat = format; + break; + + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_RG_RGTC2: + MG_Util::Debug::LogD("GL_COMPRESSED_RED_RGTC1 or GL_COMPRESSED_RG_RGTC2 is not supported!"); + break; + + case GL_SRGB8: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = format; + break; + + case GL_RGBA32F: + case GL_RGB32F: + case GL_RG32F: + case GL_R32F: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_FLOAT; + if (outFormat) + *outFormat = format; + break; + + case GL_RGB9_E5: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT_5_9_9_9_REV; + if (outFormat) + *outFormat = format; + break; + + case GL_R11F_G11F_B10F: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT_10F_11F_11F_REV; + if (outFormat) + *outFormat = GL_RGB; + break; + + case GL_RGBA32UI: + case GL_RGB32UI: + case GL_RG32UI: + case GL_R32UI: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_INT; + if (outFormat) + *outFormat = format; + break; + + case GL_RGBA32I: + case GL_RGB32I: + case GL_RG32I: + case GL_R32I: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_INT; + if (outFormat) + *outFormat = format; + break; + + case GL_RGBA16: { +// if (!checked_rgba16) { +// support_rgba16 = check_rgba16(); +// checked_rgba16 = true; +// } +// if (support_rgba16) { +// if(type) +// *type = GL_UNSIGNED_SHORT; +// } else { +// *internal_format = GL_RGBA16F; +// if(type) +// *type = GL_FLOAT; +// } + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_FLOAT; + if (outFormat) + *outFormat = format; + break; + } + case GL_RGBA8: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = GL_RGBA; + break; + + case GL_RGBA: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = GL_RGBA; + break; + + case GL_RGBA16F: + case GL_R16F: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_HALF_FLOAT; + if (outFormat) + *outFormat = format; + break; + + case GL_R16: + if (outInternalFormat) + *outInternalFormat = GL_R16F; + if (outType) + *outType = GL_FLOAT; + if (outFormat) + *outFormat = format; + break; + + case GL_RGB16: + if (outInternalFormat) + *outInternalFormat = GL_RGB16F; + if (outType) + *outType = GL_HALF_FLOAT; + if (outFormat) + *outFormat = GL_RGB; + break; + + case GL_RGB16F: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_HALF_FLOAT; + if (outFormat) + *outFormat = GL_RGB; + break; + + case GL_RG16: + case GL_RG16F: + if (outInternalFormat) + *outInternalFormat = GL_RG16F; + if (outType) + *outType = GL_HALF_FLOAT; + if (outFormat) + *outFormat = GL_RG; + break; + + case GL_R8: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = GL_RED; + break; + case GL_R8UI: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = GL_RED_INTEGER; + break; + + case GL_RGB8_SNORM: + case GL_RGBA8_SNORM: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_BYTE; + if (outFormat) + *outFormat = format; + break; + case GL_RGB8: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType && type != GL_UNSIGNED_BYTE) + *outType = GL_UNSIGNED_BYTE; + if (outFormat) + *outFormat = GL_RGB; + break; + case GL_RGBA16_SNORM: + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = GL_SHORT; + if (outFormat) + *outFormat = format; + break; + default: + MG_Util::Debug::LogD("NormalizePixelFormat: no conversion"); + + if (outInternalFormat) + *outInternalFormat = internalFormat; + if (outType) + *outType = type; + if (outFormat) + *outFormat = format; + break; + } + } + + std::string processOutColorLocations(const std::string& glslCode) { const static std::regex pattern(R"(\n(out highp vec4 outColor)(\d+);)"); const std::string replacement = "\nlayout(location=$2) $1$2;"; @@ -124,15 +414,18 @@ namespace MG_GL::GL { const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr; switch (target) { case GL_TEXTURE_2D: { - GLenum type = mip.type; - // Mali cannot use GL_FLOAT as depth format - if (mip.internalFormat == GL_DEPTH_COMPONENT) { - type = GL_UNSIGNED_INT; - } + GLenum internalFormat = 0, type = 0, format = 0; + NormalizePixelFormat(mip.internalFormat, mip.type, mip.format, &internalFormat, &type, &format); +// +// GLenum type = mip.type; +// // Mali cannot use GL_FLOAT as depth format +// if (mip.internalFormat == GL_DEPTH_COMPONENT) { +// type = GL_UNSIGNED_INT; +// } CallAndCheck(::GLES::glTexImage2D( - target, level, mip.internalFormat, + target, level, internalFormat, mip.width, mip.height, 0, - mip.format, type, data + format, type, data );) s_textureLevelUploaded[mgTexId][level] = true; MG_Util::Debug::LogD("Initial upload texture %u level %d (size=%zu)", From a50856dea057e4e46730dc120c00c4cf2b6a2601 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 20:38:43 +0800 Subject: [PATCH 06/21] [buildsystem] (build.gradle): add gradle file --- build.gradle | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 build.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..5b838b95 --- /dev/null +++ b/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.library' + +android { + namespace 'top.mobilegl.mobilegl' + compileSdk 34 + + defaultConfig { + minSdk 26 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + ndkVersion '27.2.12479018' + } + + buildTypes { + release { + minifyEnabled false + } + proguard { + minifyEnabled true + initWith debug + } + fordebug { + debuggable true + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + version "3.22.1" + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + ndkVersion '26.1.10909125' +} From f98f877e075da663238fc32d900ba56d452af2d7 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 23:25:46 +0800 Subject: [PATCH 07/21] [Fix] (GL_Drawing.cpp): realize glBindAttribLocation, and other misc fixes --- CMakeLists.txt | 4 +- MG/Includes.h | 1 + .../Implementations/GL/Buffer/GL_Buffer.cpp | 2 +- .../Implementations/GL/Drawing/GL_Drawing.cpp | 88 ++++++++++++------- build.gradle | 3 +- 5 files changed, 62 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6482a160..0d718f8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project("MobileGL") enable_language(CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -118,4 +118,4 @@ if (PROFILING) add_library(perfetto STATIC ${CMAKE_SOURCE_DIR}/3rdparty/perfetto/sdk/perfetto.cc) target_link_libraries(MobileGL perfetto ${CMAKE_THREAD_LIBS_INIT}) target_compile_definitions(MobileGL PUBLIC PROFILING=1) -endif() \ No newline at end of file +endif() diff --git a/MG/Includes.h b/MG/Includes.h index a353ec19..96cae5ee 100644 --- a/MG/Includes.h +++ b/MG/Includes.h @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include diff --git a/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp b/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp index 70c43f1b..a691494d 100644 --- a/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp +++ b/MG/MG_GL/Implementations/GL/Buffer/GL_Buffer.cpp @@ -42,7 +42,7 @@ namespace MG_GL::GL { if (buffer != 0 && MG_State::ValidateGeneratedName(buffer) && !MG_State::ValidateAllocatedBufferHandle(buffer)) { - MG_Util::Debug::LogE("Actually creating buffer: %u", buffer); + MG_Util::Debug::LogD("Actually creating buffer: %u", buffer); GLenum result = MG_State::CreateBuffer(buffer); } diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index ad133455..79e183dd 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -702,31 +702,43 @@ namespace MG_GL::GL { // Buffer SyncAllBuffersToGLES(bufferState); + + GLuint vbo = MG_State_T::bufferState->GetCurrentBinding(GL_ARRAY_BUFFER); + CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, s_bufferMap[vbo]);) + // VAO - GLuint mgVAO = vaState->currentVao_; - VertexArrayObject* vao = &vaState->vaos_[vaState->currentVao_]; - for (auto& [mgVAOId, mgVAO] : vaState->vaos_) { - if (!mgVAO.generated) continue; +// GLuint mgVAOId = vaState->currentVao_; +// VertexArrayObject* vao = &vaState->vaos_[mgVAOId]; + for (auto& [mgid, vao] : vaState->vaos_) { + if (!vao.generated) + continue; + + MG_Util::Debug::LogD("Creating MG VAO: %d", mgid); // TODO: Check is the VAO changes rather than always update it. //if (!s_vaoMap.count(mgVAOId)) { - GLuint glVAO; - if (!s_vaoMap.count(mgVAOId)) { + GLuint glVAO; + if (s_vaoMap.find(mgid) == s_vaoMap.end()) { CallAndCheck(::GLES::glGenVertexArrays(1, &glVAO);) - s_vaoMap[mgVAOId] = glVAO; - + s_vaoMap[mgid] = glVAO; } else { - glVAO = s_vaoMap[mgVAOId]; + glVAO = s_vaoMap[mgid]; } CallAndCheck(::GLES::glBindVertexArray(glVAO);) - - if (mgVAO.elementBuffer != 0 && s_bufferMap.count(mgVAO.elementBuffer)) { - CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_bufferMap[mgVAO.elementBuffer]);) - } + MG_Util::Debug::LogD("Bind VAO (MG -> ES): %d -> %d", mgid, glVAO); - for (auto& [index, attrib] : mgVAO.attribs) { - if (attrib.buffer != 0 && s_bufferMap.count(attrib.buffer)) { - CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, s_bufferMap[attrib.buffer]);) + std::string name = std::format("MG VAO {}", mgid); + ::GLES::glObjectLabel(GL_VERTEX_ARRAY, mgid, name.length(), name.c_str()); + + if (vao.elementBuffer != 0 && s_bufferMap.find(vao.elementBuffer) != s_bufferMap.end()) { + CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_bufferMap[vao.elementBuffer]);) + } + MG_Util::Debug::LogD("VAO has %d attributes:", vao.attribs.size()); + for (auto& [index, attrib] : vao.attribs) { +// if (attrib.buffer != 0 && s_bufferMap.find(attrib.buffer) != s_bufferMap.end()) { + MG_Util::Debug::LogD("attrib #%d: size=%d, type=%s, stride=%d, pointer=%d, %s, isInt=%s", + index, attrib.size, MG_Util::Debug::GLEnumToString(attrib.type), attrib.stride, attrib.pointer, + (attrib.enabled ? "enabled" : "disabled"), (attrib.isInteger ? "true" : "false")); if (attrib.isInteger) { CallAndCheck(::GLES::glVertexAttribIPointer( @@ -746,31 +758,34 @@ namespace MG_GL::GL { } else { CallAndCheck(::GLES::glDisableVertexAttribArray(index);) } - } +// } } CallAndCheck(::GLES::glBindVertexArray(0);) //} } GLuint currentMgVAO = vaState->currentVao_; - if (s_vaoMap.count(currentMgVAO)) { + MG_Util::Debug::LogD("Now binding to VAO %d...", currentMgVAO); + if (s_vaoMap.find(currentMgVAO) != s_vaoMap.end()) { CallAndCheck(::GLES::glBindVertexArray(s_vaoMap[currentMgVAO]);) - VertexArrayObject& currentVAO = vaState->vaos_[currentMgVAO]; - for (auto& [index, attrib] : currentVAO.attribs) { - if (attrib.enabled) { - CallAndCheck(::GLES::glEnableVertexAttribArray(index);) - } else { - CallAndCheck(::GLES::glDisableVertexAttribArray(index);) - } - } +// VertexArrayObject& currentVAO = vaState->vaos_[currentMgVAO]; +// for (auto& [index, attrib] : currentVAO.attribs) { +// if (attrib.enabled) { +// CallAndCheck(::GLES::glEnableVertexAttribArray(index);) +// } else { +// CallAndCheck(::GLES::glDisableVertexAttribArray(index);) +// } +// } } else { CallAndCheck(::GLES::glBindVertexArray(0);) } // EBO - if (vao->elementBuffer != 0) { - if (s_bufferMap.count(vao->elementBuffer)) { - CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_bufferMap[vao->elementBuffer]);) + GLuint curVaoId = vaState->currentVao_; + VertexArrayObject* curvao = &vaState->vaos_[curVaoId]; + if (curvao->elementBuffer != 0) { + if (s_bufferMap.find(curvao->elementBuffer) != s_bufferMap.end()) { + CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_bufferMap[curvao->elementBuffer]);) } } else if (indices != nullptr) { static GLuint dynamicIBO = 0; @@ -796,11 +811,22 @@ namespace MG_GL::GL { if (s_programMap.find(currentProgram) == s_programMap.end()) { GLuint glProgram = ::GLES::glCreateProgram(); ProgramObject& mgProgram = programState->programs_[currentProgram]; - + + std::string name = std::format("MG Program {}", currentProgram); + ::GLES::glObjectLabel(GL_PROGRAM, glProgram, name.length(), name.c_str()); + + // Attribute Names + // before vertex shader attach + for (auto& [name, idx]: mgProgram.attribLocations) { + MG_Util::Debug::LogD("%s: location = %d", name.c_str(), idx); + CallAndCheck(::GLES::glBindAttribLocation(glProgram, idx, name.c_str());) + } + // Shader for (GLuint shaderId : mgProgram.attachedShaders) { ShaderObject& mgShader = programState->shaders_[shaderId]; GLuint glShader = ::GLES::glCreateShader(mgShader.type); + std::string source = MG_Util::Program::CompileSPIRVToGLSL(mgShader.compiledSpirv, 320, true); // Post-processing ESSL source = removeLayoutBinding(source); @@ -1066,7 +1092,7 @@ namespace MG_GL::GL { - if (vao->elementBuffer != 0 || indices != nullptr) { + if (curvao->elementBuffer != 0 || indices != nullptr) { CallAndCheck(::GLES::glDrawElements( mode, count, diff --git a/build.gradle b/build.gradle index 5b838b95..534b45db 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,6 @@ android { minSdk 26 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - ndkVersion '27.2.12479018' } buildTypes { @@ -33,5 +32,5 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - ndkVersion '26.1.10909125' + ndkVersion '27.2.12479018' } From 3edb48d5fc072513c8d48d1b4c566ece5b12319f Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 8 May 2025 09:49:09 +0800 Subject: [PATCH 08/21] [Misc]: add ankerl/unordered_dense --- MG/Includes.h | 1 + include/ankerl/unordered_dense.h | 2101 ++++++++++++++++++++++++++++++ 2 files changed, 2102 insertions(+) create mode 100644 include/ankerl/unordered_dense.h diff --git a/MG/Includes.h b/MG/Includes.h index 96cae5ee..7b4471c0 100644 --- a/MG/Includes.h +++ b/MG/Includes.h @@ -111,6 +111,7 @@ #include #include #include +#include #include "GLES/gl32.h" #include "MG_Include/UncertainBool.hpp" diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h new file mode 100644 index 00000000..13484a98 --- /dev/null +++ b/include/ankerl/unordered_dense.h @@ -0,0 +1,2101 @@ +///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// + +// A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. +// Version 4.5.0 +// https://github.com/martinus/unordered_dense +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2022-2024 Martin Leitner-Ankerl +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ANKERL_UNORDERED_DENSE_H +#define ANKERL_UNORDERED_DENSE_H + +// see https://semver.org/spec/v2.0.0.html +#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 5 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes + +// API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) +#define ANKERL_UNORDERED_DENSE_NAMESPACE \ + ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ + ANKERL_UNORDERED_DENSE_VERSION_MAJOR, ANKERL_UNORDERED_DENSE_VERSION_MINOR, ANKERL_UNORDERED_DENSE_VERSION_PATCH) + +#if defined(_MSVC_LANG) +# define ANKERL_UNORDERED_DENSE_CPP_VERSION _MSVC_LANG +#else +# define ANKERL_UNORDERED_DENSE_CPP_VERSION __cplusplus +#endif + +#if defined(__GNUC__) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) decl __attribute__((__packed__)) +#elif defined(_MSC_VER) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) __pragma(pack(push, 1)) decl __pragma(pack(pop)) +#endif + +// exceptions +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 // NOLINT(cppcoreguidelines-macro-usage) +#else +# define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 // NOLINT(cppcoreguidelines-macro-usage) +#endif +#ifdef _MSC_VER +# define ANKERL_UNORDERED_DENSE_NOINLINE __declspec(noinline) +#else +# define ANKERL_UNORDERED_DENSE_NOINLINE __attribute__((noinline)) +#endif + +// defined in unordered_dense.cpp +#if !defined(ANKERL_UNORDERED_DENSE_EXPORT) +# define ANKERL_UNORDERED_DENSE_EXPORT +#endif + +#if ANKERL_UNORDERED_DENSE_CPP_VERSION < 201703L +# error ankerl::unordered_dense requires C++17 or higher +#else +# include // for array +# include // for uint64_t, uint32_t, uint8_t, UINT64_C +# include // for size_t, memcpy, memset +# include // for equal_to, hash +# include // for initializer_list +# include // for pair, distance +# include // for numeric_limits +# include // for allocator, allocator_traits, shared_ptr +# include // for optional +# include // for out_of_range +# include // for basic_string +# include // for basic_string_view, hash +# include // for forward_as_tuple +# include // for enable_if_t, declval, conditional_t, ena... +# include // for forward, exchange, pair, as_const, piece... +# include // for vector +# if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() == 0 +# include // for abort +# endif + +# if defined(__has_include) && !defined(ANKERL_UNORDERED_DENSE_DISABLE_PMR) +# if __has_include() +# define ANKERL_UNORDERED_DENSE_PMR std::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include // for polymorphic_allocator +# elif __has_include() +# define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr // NOLINT(cppcoreguidelines-macro-usage) +# include // for polymorphic_allocator +# endif +# endif + +# if defined(_MSC_VER) && defined(_M_X64) +# include +# pragma intrinsic(_umul128) +# endif + +# if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +# define ANKERL_UNORDERED_DENSE_LIKELY(x) __builtin_expect(x, 1) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) __builtin_expect(x, 0) // NOLINT(cppcoreguidelines-macro-usage) +# else +# define ANKERL_UNORDERED_DENSE_LIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# endif + +namespace ankerl::unordered_dense { +inline namespace ANKERL_UNORDERED_DENSE_NAMESPACE { + +namespace detail { + +# if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() + +// make sure this is not inlined as it is slow and dramatically enlarges code, thus making other +// inlinings more difficult. Throws are also generally the slow path. +[[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_key_not_found() { + throw std::out_of_range("ankerl::unordered_dense::map::at(): key not found"); +} +[[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_bucket_overflow() { + throw std::overflow_error("ankerl::unordered_dense: reached max bucket size, cannot increase size"); +} +[[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_too_many_elements() { + throw std::out_of_range("ankerl::unordered_dense::map::replace(): too many elements"); +} + +# else + +[[noreturn]] inline void on_error_key_not_found() { + abort(); +} +[[noreturn]] inline void on_error_bucket_overflow() { + abort(); +} +[[noreturn]] inline void on_error_too_many_elements() { + abort(); +} + +# endif + +} // namespace detail + +// hash /////////////////////////////////////////////////////////////////////// + +// This is a stripped-down implementation of wyhash: https://github.com/wangyi-fudan/wyhash +// No big-endian support (because different values on different machines don't matter), +// hardcodes seed and the secret, reformats the code, and clang-tidy fixes. +namespace detail::wyhash { + +inline void mum(uint64_t* a, uint64_t* b) { +# if defined(__SIZEOF_INT128__) + __uint128_t r = *a; + r *= *b; + *a = static_cast(r); + *b = static_cast(r >> 64U); +# elif defined(_MSC_VER) && defined(_M_X64) + *a = _umul128(*a, *b, b); +# else + uint64_t ha = *a >> 32U; + uint64_t hb = *b >> 32U; + uint64_t la = static_cast(*a); + uint64_t lb = static_cast(*b); + uint64_t hi{}; + uint64_t lo{}; + uint64_t rh = ha * hb; + uint64_t rm0 = ha * lb; + uint64_t rm1 = hb * la; + uint64_t rl = la * lb; + uint64_t t = rl + (rm0 << 32U); + auto c = static_cast(t < rl); + lo = t + (rm1 << 32U); + c += static_cast(lo < t); + hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c; + *a = lo; + *b = hi; +# endif +} + +// multiply and xor mix function, aka MUM +[[nodiscard]] inline auto mix(uint64_t a, uint64_t b) -> uint64_t { + mum(&a, &b); + return a ^ b; +} + +// read functions. WARNING: we don't care about endianness, so results are different on big endian! +[[nodiscard]] inline auto r8(const uint8_t* p) -> uint64_t { + uint64_t v{}; + std::memcpy(&v, p, 8U); + return v; +} + +[[nodiscard]] inline auto r4(const uint8_t* p) -> uint64_t { + uint32_t v{}; + std::memcpy(&v, p, 4); + return v; +} + +// reads 1, 2, or 3 bytes +[[nodiscard]] inline auto r3(const uint8_t* p, size_t k) -> uint64_t { + return (static_cast(p[0]) << 16U) | (static_cast(p[k >> 1U]) << 8U) | p[k - 1]; +} + +[[maybe_unused]] [[nodiscard]] inline auto hash(void const* key, size_t len) -> uint64_t { + static constexpr auto secret = std::array{UINT64_C(0xa0761d6478bd642f), + UINT64_C(0xe7037ed1a0b428db), + UINT64_C(0x8ebc6af09c88c6e3), + UINT64_C(0x589965cc75374cc3)}; + + auto const* p = static_cast(key); + uint64_t seed = secret[0]; + uint64_t a{}; + uint64_t b{}; + if (ANKERL_UNORDERED_DENSE_LIKELY(len <= 16)) { + if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 4)) { + a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); + b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); + } else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) { + a = r3(p, len); + b = 0; + } else { + a = 0; + b = 0; + } + } else { + size_t i = len; + if (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 48)) { + uint64_t see1 = seed; + uint64_t see2 = seed; + do { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); + see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); + p += 48; + i -= 48; + } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48)); + seed ^= see1 ^ see2; + } + while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + i -= 16; + p += 16; + } + a = r8(p + i - 16); + b = r8(p + i - 8); + } + + return mix(secret[1] ^ len, mix(a ^ secret[1], b ^ seed)); +} + +[[nodiscard]] inline auto hash(uint64_t x) -> uint64_t { + return detail::wyhash::mix(x, UINT64_C(0x9E3779B97F4A7C15)); +} + +} // namespace detail::wyhash + +ANKERL_UNORDERED_DENSE_EXPORT template +struct hash { + auto operator()(T const& obj) const noexcept(noexcept(std::declval>().operator()(std::declval()))) + -> uint64_t { + return std::hash{}(obj); + } +}; + +template +struct hash::is_avalanching> { + using is_avalanching = void; + auto operator()(T const& obj) const noexcept(noexcept(std::declval>().operator()(std::declval()))) + -> uint64_t { + return std::hash{}(obj); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::basic_string const& str) const noexcept -> uint64_t { + return detail::wyhash::hash(str.data(), sizeof(CharT) * str.size()); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::basic_string_view const& sv) const noexcept -> uint64_t { + return detail::wyhash::hash(sv.data(), sizeof(CharT) * sv.size()); + } +}; + +template +struct hash { + using is_avalanching = void; + auto operator()(T* ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr)); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::unique_ptr const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr.get())); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::shared_ptr const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr.get())); + } +}; + +template +struct hash::value>::type> { + using is_avalanching = void; + auto operator()(Enum e) const noexcept -> uint64_t { + using underlying = typename std::underlying_type_t; + return detail::wyhash::hash(static_cast(e)); + } +}; + +template +struct tuple_hash_helper { + // Converts the value into 64bit. If it is an integral type, just cast it. Mixing is doing the rest. + // If it isn't an integral we need to hash it. + template + [[nodiscard]] constexpr static auto to64(Arg const& arg) -> uint64_t { + if constexpr (std::is_integral_v || std::is_enum_v) { + return static_cast(arg); + } else { + return hash{}(arg); + } + } + + [[nodiscard]] static auto mix64(uint64_t state, uint64_t v) -> uint64_t { + return detail::wyhash::mix(state + v, uint64_t{0x9ddfea08eb382d69}); + } + + // Creates a buffer that holds all the data from each element of the tuple. If possible we memcpy the data directly. If + // not, we hash the object and use this for the array. Size of the array is known at compile time, and memcpy is optimized + // away, so filling the buffer is highly efficient. Finally, call wyhash with this buffer. + template + [[nodiscard]] static auto calc_hash(T const& t, std::index_sequence) noexcept -> uint64_t { + auto h = uint64_t{}; + ((h = mix64(h, to64(std::get(t)))), ...); + return h; + } +}; + +template +struct hash> : tuple_hash_helper { + using is_avalanching = void; + auto operator()(std::tuple const& t) const noexcept -> uint64_t { + return tuple_hash_helper::calc_hash(t, std::index_sequence_for{}); + } +}; + +template +struct hash> : tuple_hash_helper { + using is_avalanching = void; + auto operator()(std::pair const& t) const noexcept -> uint64_t { + return tuple_hash_helper::calc_hash(t, std::index_sequence_for{}); + } +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_HASH_STATICCAST(T) \ + template <> \ + struct hash { \ + using is_avalanching = void; \ + auto operator()(T const& obj) const noexcept -> uint64_t { \ + return detail::wyhash::hash(static_cast(obj)); \ + } \ + } + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +# endif +// see https://en.cppreference.com/w/cpp/utility/hash +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(bool); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(signed char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned char); +# if ANKERL_UNORDERED_DENSE_CPP_VERSION >= 202002L && defined(__cpp_char8_t) +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char8_t); +# endif +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char16_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char32_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(wchar_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long long); + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +# endif + +// bucket_type ////////////////////////////////////////////////////////// + +namespace bucket_type { + +struct standard { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + uint32_t m_value_idx; // index into the m_values vector. +}; + +ANKERL_UNORDERED_DENSE_PACK(struct big { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + size_t m_value_idx; // index into the m_values vector. +}); + +} // namespace bucket_type + +namespace detail { + +struct nonesuch {}; +struct default_container_t {}; + +template class Op, class... Args> +struct detector { + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; +}; + +template