From 3fa6aa660c93271946dcef9d01525ff684a09526 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 5 May 2025 09:56:36 +0800 Subject: [PATCH 01/34] [Feat] (GL_Drawing): Add DrawElementsSHITTILY. Add SPIRV->GLSL ES conversion. --- MG/Global.h | 2 +- .../Implementations/GL/Common/GL_Common.cpp | 8 - .../Implementations/GL/Drawing/GL_Drawing.cpp | 850 +++++++++++++++++- .../Implementations/GL/Drawing/GL_Drawing.h | 2 - MG/MG_GL/State/Buffer/BufferState.h | 4 +- MG/MG_GL/State/Common/CommonState.cpp | 1 + MG/MG_GL/State/Common/CommonState.h | 21 +- .../State/Framebuffer/FramebufferState.cpp | 3 +- MG/MG_GL/State/Framebuffer/FramebufferState.h | 3 +- MG/MG_GL/State/Program/ProgramState.h | 3 +- MG/MG_GL/State/Texture/TextureState.h | 2 +- MG/MG_GL/State/VertexArray/VertexArrayState.h | 6 +- MG/MG_UTIL/Program/GLSLTool.cpp | 35 + MG/MG_UTIL/Program/GLSLTool.h | 1 + 14 files changed, 903 insertions(+), 38 deletions(-) diff --git a/MG/Global.h b/MG/Global.h index 2fb584b4..61adf95d 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_DEBUG; + inline const int LogLevel = MG_Constants::Common::LOG_LEVEL_INFO; #ifdef __ANDROID__ inline const char* LOG_FILE_PATH = "/sdcard/MG/latest.log"; diff --git a/MG/MG_GL/Implementations/GL/Common/GL_Common.cpp b/MG/MG_GL/Implementations/GL/Common/GL_Common.cpp index b2389138..abd7f00d 100644 --- a/MG/MG_GL/Implementations/GL/Common/GL_Common.cpp +++ b/MG/MG_GL/Implementations/GL/Common/GL_Common.cpp @@ -43,14 +43,6 @@ namespace MG_GL::GL { MG_Util::Debug::LogE("Error setting separate blend func: %s", MG_Util::Debug::GLEnumToString(result)); } - void Clear(GLbitfield mask) { - MG_Util::Debug::LogD("glClear, mask: 0x%X", mask); - GLenum result = MG_State::glClear(mask); - if (result == GL_NO_ERROR) return; - MG_State::SetError(result); - MG_Util::Debug::LogE("Error clearing buffers: %s", MG_Util::Debug::GLEnumToString(result)); - } - void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { MG_Util::Debug::LogD("glClearColor, rgba: [%.2f, %.2f, %.2f, %.2f]", red, green, blue, alpha); GLenum result = MG_State::glClearColor(red, green, blue, alpha); diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index 72ea7cbb..8ab473e6 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -4,14 +4,850 @@ #include "GL_Drawing.h" +#undef MOBILEGL_GLSLTOOL_H +#include "../../../../Includes.h" + namespace MG_GL::GL { - // This is a test function that directly draws textures. + 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;"; + return std::regex_replace(glslCode, pattern, replacement); + } + + std::string forceSupporterOutput(const std::string& glslCode) { + bool hasPrecisionFloat = glslCode.find("precision ") != std::string::npos && + glslCode.find("float;") != std::string::npos; + bool hasPrecisionInt = glslCode.find("precision ") != std::string::npos && + glslCode.find("int;") != std::string::npos; + + std::string result = glslCode; + std::string precisionFloat; + std::string precisionInt; + + if (hasPrecisionFloat && hasPrecisionInt) { + std::istringstream iss(result); + std::vector lines; + std::string line; + while (std::getline(iss, line)) { + bool isPrecisionLine = (line.find("precision ") != std::string::npos) && + (line.find("float;") != std::string::npos || line.find("int;") != std::string::npos); + if (!isPrecisionLine) { + lines.push_back(line); + } + } + result.clear(); + for (size_t i = 0; i < lines.size(); ++i) { + if (i != 0) result += '\n'; + result += lines[i]; + } + precisionFloat = "precision highp float;\n"; + precisionInt = "precision highp int;\n"; + } else { + precisionFloat = hasPrecisionFloat ? "" : "precision highp float;\n"; + precisionInt = hasPrecisionInt ? "" : "precision highp int;\n"; + } + + size_t lastExtensionPos = result.rfind("#extension"); + size_t insertionPos = 0; + + if (lastExtensionPos != std::string::npos) { + size_t nextNewline = result.find('\n', lastExtensionPos); + if (nextNewline != std::string::npos) { + insertionPos = nextNewline + 1; + } else { + insertionPos = result.length(); + } + } else { + size_t firstNewline = result.find('\n'); + if (firstNewline != std::string::npos) { + insertionPos = firstNewline + 1; + } else { + result = precisionFloat + precisionInt + result; + return result; + } + } + + result.insert(insertionPos, precisionFloat + precisionInt); + return result; + } + + std::string removeLayoutBinding(const std::string& glslCode) { + static std::regex bindingRegex(R"(layout\s*\(\s*binding\s*=\s*\d+\s*\)\s*)"); + std::string result = std::regex_replace(glslCode, bindingRegex, ""); + static std::regex bindingRegex2(R"(layout\s*\(\s*binding\s*=\s*\d+\s*,)"); + result = std::regex_replace(result, bindingRegex2, "layout("); + return result; + } + + static std::unordered_map s_textureMap; + static std::unordered_map s_vaoMap; + static std::unordered_map s_bufferMap; + static std::unordered_map s_programMap; + static std::unordered_map s_framebufferMap; + struct MipLevelInfo { + GLenum internalFormat; + GLsizei width; + GLsizei height; + GLenum format; + GLenum type; + }; + + void CheckGLESError() { + while (GLenum err = ::GLES::glGetError() != GL_NO_ERROR) { + MG_Util::Debug::LogE("-> GLES Error: %s", MG_Util::Debug::GLEnumToString(err)); + } + } + +#define CallAndCheck(operation) MG_Util::Debug::LogD("GLES call: %s", #operation); operation CheckGLESError(); + static std::unordered_map> s_textureLevelUploaded; + + void SyncAllTexturesToGLES(TextureState* textureState) { + MG_Util::Debug::LogD("Syncing all textures to GLES..."); + for (auto& [mgTexId, texObj] : textureState->textures) { + if (!texObj.generated) continue; + + if (s_textureMap.find(mgTexId) == s_textureMap.end()) { + GLuint glTexId; + CallAndCheck(::GLES::glGenTextures(1, &glTexId);) + s_textureMap[mgTexId] = glTexId; + GLenum target = texObj.target; + CallAndCheck(::GLES::glBindTexture(target, glTexId);) + + for (auto& [pname, param] : texObj.params.texPropertiesInt) { + CallAndCheck(::GLES::glTexParameteri(target, pname, param);) + } + for (auto& [pname, param] : texObj.params.texPropertiesFloat) { + CallAndCheck(::GLES::glTexParameterf(target, pname, param);) + } + + 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 + );) + s_textureLevelUploaded[mgTexId][level] = true; + 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)); + } + } + MG_Util::Debug::LogD("Created and synced new GLES texture %u (MobileGL ID)", mgTexId); + } else { + GLuint glTexId = s_textureMap[mgTexId]; + GLenum target = texObj.target; + CallAndCheck(::GLES::glBindTexture(target, glTexId);) + + for (auto& [level, mip] : texObj.params.mipmapData) { + if (!s_textureLevelUploaded[mgTexId][level]) { + bool levelInitialized = s_textureLevelUploaded[mgTexId].count(level); + const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr; + + switch (target) { + case GL_TEXTURE_2D: + if (levelInitialized) { + CallAndCheck(::GLES::glTexSubImage2D( + target, level, + 0, 0, + mip.width, mip.height, + mip.format, mip.type, data + );) + s_textureLevelUploaded[mgTexId][level] = true; + MG_Util::Debug::LogD("Updated texture %u level %d with glTexSubImage2D", mgTexId, level); + } else { + CallAndCheck(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, mip.type, data + );) + s_textureLevelUploaded[mgTexId][level] = true; + MG_Util::Debug::LogD("Initialized texture %u level %d with glTexImage2D", mgTexId, level); + } + break; + default: + MG_Util::Debug::LogE("Unsupported target: %s", MG_Util::Debug::GLEnumToString(target)); + } + } + } + MG_Util::Debug::LogD("Updated existing GLES texture %u (MobileGL ID)", mgTexId); + } + } + CallAndCheck(::GLES::glBindTexture(GL_TEXTURE_2D, 0);) + } + + + 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(); + } + } + CallAndCheck(::GLES::glBindBuffer(GL_ARRAY_BUFFER, currentVBO);) + CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, currentEBO);) + } + + void DrawArraysSHITTILY(GLenum mode, GLint first, GLsizei count) { + + } + + static GLuint lastBoundVAO = 0; + static GLuint lastBoundProgram = 0; + static GLuint lastBoundFBO = 0; + static std::array lastBoundTextures; + void DrawElementsSHITTILY(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { + CommonState* commonState = MG_State_T::commonState; + TextureState* textureState = MG_State_T::textureState; + BufferState* bufferState = MG_State_T::bufferState; + VertexArrayState* vaState = MG_State_T::vertexArrayState; + ProgramState* programState = MG_State_T::programState; + FramebufferState* fbState = MG_State_T::framebufferState; + + // Common + const GLint* viewport = commonState->viewport; + if (viewport[2] > 0 && viewport[3] > 0) { + CallAndCheck(::GLES::glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);) + } + + if (commonState->capabilities[GL_BLEND]) { + CallAndCheck(::GLES::glEnable(GL_BLEND);) + CallAndCheck(::GLES::glBlendFuncSeparate( + commonState->blendSrcRGB, commonState->blendDstRGB, + commonState->blendSrcAlpha, commonState->blendDstAlpha + );) + } else { + CallAndCheck(::GLES::glDisable(GL_BLEND);) + } + + if (commonState->capabilities[GL_DEPTH_TEST]) { + CallAndCheck(::GLES::glEnable(GL_DEPTH_TEST);) + CallAndCheck(::GLES::glDepthMask(commonState->depthMask);) + CallAndCheck(::GLES::glDepthFunc(commonState->depthFunc);) + } else { + CallAndCheck(::GLES::glDisable(GL_DEPTH_TEST);) + } + + if (commonState->capabilities[GL_CULL_FACE]) { + CallAndCheck(::GLES::glEnable(GL_CULL_FACE);) + } else { + CallAndCheck(::GLES::glDisable(GL_CULL_FACE);) + } + + CallAndCheck(::GLES::glColorMask( + commonState->colorMask[0], commonState->colorMask[1], + commonState->colorMask[2], commonState->colorMask[3] + );) + + // Texture + SyncAllTexturesToGLES(textureState); + for (size_t unitIdx = 0; unitIdx < textureState->textureUnits_.size(); ++unitIdx) { + TextureUnitState& mgUnit = textureState->textureUnits_[unitIdx]; + GLenum glUnit = GL_TEXTURE0 + unitIdx; + + if (lastBoundTextures[unitIdx] != mgUnit.boundTextures[GL_TEXTURE_2D]) { + CallAndCheck(::GLES::glActiveTexture(glUnit);) + for (auto& [target, texId] : mgUnit.boundTextures) { + if (texId == 0) { + CallAndCheck(::GLES::glBindTexture(target, 0);) + lastBoundTextures[unitIdx] = 0; + continue; + } + + if (s_textureMap.find(texId) == s_textureMap.end()) { + GLuint glTexId; + CallAndCheck(::GLES::glGenTextures(1, &glTexId);) + s_textureMap[texId] = glTexId; + + CallAndCheck(::GLES::glBindTexture(target, glTexId);) + TextureObject& mgTex = textureState->textures[texId]; + + for (auto& [pname, param] : mgTex.params.texPropertiesInt) { + CallAndCheck(::GLES::glTexParameteri(target, pname, param);) + } + for (auto& [pname, param] : mgTex.params.texPropertiesFloat) { + CallAndCheck(::GLES::glTexParameterf(target, pname, param);) + } + + for (auto& [level, mip] : mgTex.params.mipmapData) { + CallAndCheck(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, mip.type, mip.pixelData.data() + );) + } + } + CallAndCheck(::GLES::glBindTexture(target, s_textureMap[texId]);) + lastBoundTextures[unitIdx] = s_textureMap[texId]; + } + } + } + + // Buffer + SyncAllBuffersToGLES(bufferState); + + // VAO + GLuint mgVAO = vaState->currentVao_; + VertexArrayObject* vao = &vaState->vaos_[vaState->currentVao_]; + for (auto& [mgVAOId, mgVAO] : vaState->vaos_) { + if (!mgVAO.generated) continue; + + // TODO: Check is the VAO changes rather than always update it. + //if (!s_vaoMap.count(mgVAOId)) { + GLuint glVAO; + if (!s_vaoMap.count(mgVAOId)) { + CallAndCheck(::GLES::glGenVertexArrays(1, &glVAO);) + s_vaoMap[mgVAOId] = glVAO; + + } else { + glVAO = s_vaoMap[mgVAOId]; + } + CallAndCheck(::GLES::glBindVertexArray(glVAO);) + + if (mgVAO.elementBuffer != 0 && s_bufferMap.count(mgVAO.elementBuffer)) { + CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_bufferMap[mgVAO.elementBuffer]);) + } + + 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]);) + + if (attrib.isInteger) { + CallAndCheck(::GLES::glVertexAttribIPointer( + index, attrib.size, attrib.type, + attrib.stride, attrib.pointer + );) + } else { + CallAndCheck(::GLES::glVertexAttribPointer( + index, attrib.size, attrib.type, + attrib.normalized ? GL_TRUE : GL_FALSE, + attrib.stride, attrib.pointer + );) + } + + if (attrib.enabled) { + CallAndCheck(::GLES::glEnableVertexAttribArray(index);) + } else { + CallAndCheck(::GLES::glDisableVertexAttribArray(index);) + } + } + } + CallAndCheck(::GLES::glBindVertexArray(0);) + //} + } + GLuint currentMgVAO = vaState->currentVao_; + if (s_vaoMap.count(currentMgVAO)) { + 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);) + } + } + } 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]);) + } + } else if (indices != nullptr) { + static GLuint dynamicIBO = 0; + if (dynamicIBO == 0) { + CallAndCheck(::GLES::glGenBuffers(1, &dynamicIBO);) + } + + size_t typeSize = 0; + switch(type) { + case GL_UNSIGNED_BYTE: typeSize = sizeof(GLubyte); break; + case GL_UNSIGNED_SHORT: typeSize = sizeof(GLushort); break; + case GL_UNSIGNED_INT: typeSize = sizeof(GLuint); break; + } + size_t dataSize = count * typeSize; + + CallAndCheck(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamicIBO);) + CallAndCheck(::GLES::glBufferData(GL_ELEMENT_ARRAY_BUFFER, dataSize, indices, GL_STREAM_DRAW);) + } + + // Program + GLuint currentProgram = programState->GetCurrentProgram(); + if (currentProgram != lastBoundProgram) { + if (s_programMap.find(currentProgram) == s_programMap.end()) { + GLuint glProgram = ::GLES::glCreateProgram(); + ProgramObject& mgProgram = programState->programs_[currentProgram]; + + // 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); + source = processOutColorLocations(source); + source = forceSupporterOutput(source); + MG_Util::Debug::LogD( + "shader source:\n%s\nConverted source:\n%s\nSpirv Size:%d", + mgShader.source.c_str(),source.c_str(),mgShader.compiledSpirv.size() + ); + + const char* s[] = { source.c_str() }; + CallAndCheck(::GLES::glShaderSource(glShader, 1, s, nullptr);) + CallAndCheck(::GLES::glCompileShader(glShader);) + + GLint success; + CallAndCheck(::GLES::glGetShaderiv(glShader, GL_COMPILE_STATUS, &success);) + if (!success) { + GLchar infoLog[512]; + CallAndCheck(::GLES::glGetShaderInfoLog(glShader, 512, nullptr, infoLog);) + MG_Util::Debug::LogE("Shader compilation failed: %s", infoLog); + } + CallAndCheck(::GLES::glAttachShader(glProgram, glShader);) + } + + CallAndCheck(::GLES::glLinkProgram(glProgram);) + GLint linkStatus; + CallAndCheck(::GLES::glGetProgramiv(glProgram, GL_LINK_STATUS, &linkStatus);) + if (!linkStatus) { + GLchar infoLog[512]; + CallAndCheck(::GLES::glGetProgramInfoLog(glProgram, 512, nullptr, infoLog);) + MG_Util::Debug::LogE("Program linking failed: %s", infoLog); + } + s_programMap[currentProgram] = glProgram; + } + CallAndCheck(::GLES::glUseProgram(s_programMap[currentProgram]);) + lastBoundProgram = currentProgram; + + // Uniform + ProgramObject& mgProgram = programState->programs_[currentProgram]; + for (auto& [name, uniform] : mgProgram.uniformValues) { + GLint location = ::GLES::glGetUniformLocation(s_programMap[currentProgram], name.c_str()); + if (location == -1) { + MG_Util::Debug::LogE("Uniform location not found: %s", name.c_str()); + continue; + } + MG_Util::Debug::LogD("Uniform %s(type: %u) location: %u(GLES) -- %u(MG)", name.c_str(), uniform.type, location, mgProgram.uniformLocations[name]); + + switch (uniform.type) { + case GL_FLOAT: { + GLsizei num = static_cast(uniform.count / 1); + CallAndCheck(::GLES::glUniform1fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + CallAndCheck(::GLES::glUniform2fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + CallAndCheck(::GLES::glUniform3fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + CallAndCheck(::GLES::glUniform4fv(location, num, uniform.floatData.data());) + break; + } + + case GL_INT: + case GL_BOOL: + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: { + GLsizei num = static_cast(uniform.count / 1); + CallAndCheck(::GLES::glUniform1iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC2: + case GL_BOOL_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + CallAndCheck(::GLES::glUniform2iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC3: + case GL_BOOL_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + CallAndCheck(::GLES::glUniform3iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC4: + case GL_BOOL_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + CallAndCheck(::GLES::glUniform4iv(location, num, uniform.intData.data());) + break; + } + + case GL_UNSIGNED_INT: { + GLsizei num = static_cast(uniform.count / 1); + CallAndCheck(::GLES::glUniform1uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + CallAndCheck(::GLES::glUniform2uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + CallAndCheck(::GLES::glUniform3uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + CallAndCheck(::GLES::glUniform4uiv(location, num, uniform.uintData.data());) + break; + } + + case GL_FLOAT_MAT2: { + GLsizei num = static_cast(uniform.count / 4); + CallAndCheck(::GLES::glUniformMatrix2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3: { + GLsizei num = static_cast(uniform.count / 9); + CallAndCheck(::GLES::glUniformMatrix3fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4: { + GLsizei num = static_cast(uniform.count / 16); + CallAndCheck(::GLES::glUniformMatrix4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT2x3: { + GLsizei num = static_cast(uniform.count / 6); + CallAndCheck(::GLES::glUniformMatrix2x3fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT2x4: { + GLsizei num = static_cast(uniform.count / 8); + CallAndCheck(::GLES::glUniformMatrix2x4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3x2: { + GLsizei num = static_cast(uniform.count / 6); + CallAndCheck(::GLES::glUniformMatrix3x2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3x4: { + GLsizei num = static_cast(uniform.count / 12); + CallAndCheck(::GLES::glUniformMatrix3x4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4x2: { + GLsizei num = static_cast(uniform.count / 8); + CallAndCheck(::GLES::glUniformMatrix4x2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4x3: { + GLsizei num = static_cast(uniform.count / 12); + CallAndCheck(::GLES::glUniformMatrix4x3fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + } + + } + } + + // Framebuffer + GLuint currentFBO = fbState->currentBindings_[GL_DRAW_FRAMEBUFFER]; + if (currentFBO != lastBoundFBO) { + 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 = currentFBO; + } + + + if (vao->elementBuffer != 0 || indices != nullptr) { + CallAndCheck(::GLES::glDrawElements( + mode, + count, + type, + indices + );) + } + } + + 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) { + 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 = currentFBO; + } + + + static GLfloat lastClearColor[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; + if (memcmp(lastClearColor, commonState->clearColor, sizeof(lastClearColor)) != 0) { + CallAndCheck(::GLES::glClearColor( + commonState->clearColor[0], + commonState->clearColor[1], + commonState->clearColor[2], + commonState->clearColor[3] + );) + memcpy(lastClearColor, commonState->clearColor, sizeof(lastClearColor)); + } + + static GLfloat lastClearDepth = -1.0f; + if (lastClearDepth != commonState->clearDepth) { + CallAndCheck(::GLES::glClearDepthf(commonState->clearDepth);) + lastClearDepth = commonState->clearDepth; + } + + static GLint lastClearStencil = -1; + GLint currentStencil = 0; + if (lastClearStencil != currentStencil) { + CallAndCheck(::GLES::glClearStencil(currentStencil);) + lastClearStencil = currentStencil; + } + + const GLint* viewport = commonState->viewport; + if (viewport[2] > 0 && viewport[3] > 0) { + CallAndCheck(::GLES::glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);) + } + + if (commonState->capabilities[GL_BLEND]) { + CallAndCheck(::GLES::glEnable(GL_BLEND);) + CallAndCheck(::GLES::glBlendFuncSeparate( + commonState->blendSrcRGB, commonState->blendDstRGB, + commonState->blendSrcAlpha, commonState->blendDstAlpha + );) + } else { + CallAndCheck(::GLES::glDisable(GL_BLEND);) + } + + if (commonState->capabilities[GL_DEPTH_TEST]) { + CallAndCheck(::GLES::glEnable(GL_DEPTH_TEST);) + CallAndCheck(::GLES::glDepthMask(commonState->depthMask);) + CallAndCheck(::GLES::glDepthFunc(commonState->depthFunc);) + } else { + CallAndCheck(::GLES::glDisable(GL_DEPTH_TEST);) + } + + if (commonState->capabilities[GL_CULL_FACE]) { + CallAndCheck(::GLES::glEnable(GL_CULL_FACE);) + } else { + CallAndCheck(::GLES::glDisable(GL_CULL_FACE);) + } + + CallAndCheck(::GLES::glColorMask( + commonState->colorMask[0], commonState->colorMask[1], + commonState->colorMask[2], commonState->colorMask[3] + );) + + CallAndCheck(::GLES::glClear(mask);) + + } + + void Clear(GLbitfield mask) { + MG_Util::Debug::LogD("glClear, mask: 0x%X", mask); + GLenum result = MG_State::glClear(mask); + if (result == GL_NO_ERROR) { + ClearSHITTILY(mask); + return; + } + MG_State::SetError(result); + MG_Util::Debug::LogE("Error clearing buffers: %s", MG_Util::Debug::GLEnumToString(result)); + } + void DrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) { - ::GLES::glViewport(0,0,3200,1440); - MG_RHI::GLES::Test::DrawAllTextures(); - }; + DrawElementsSHITTILY(mode, count, type, indices); + } + void DrawArrays(GLenum mode, GLint first, GLsizei count) { - ::GLES::glViewport(0,0,3200,1440); - MG_RHI::GLES::Test::DrawAllTextures(); - }; + DrawArraysSHITTILY(mode, first, count); + } } \ No newline at end of file diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.h b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.h index 0eff0fcb..fe06ce5d 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.h +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.h @@ -4,9 +4,7 @@ #ifndef MOBILEGL_GL_DRAWING_H #define MOBILEGL_GL_DRAWING_H - #include "../../../../Includes.h" - namespace MG_GL::GL { void DrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices); void DrawArrays(GLenum mode, GLint first, GLsizei count); diff --git a/MG/MG_GL/State/Buffer/BufferState.h b/MG/MG_GL/State/Buffer/BufferState.h index 28db6c1f..948953f3 100644 --- a/MG/MG_GL/State/Buffer/BufferState.h +++ b/MG/MG_GL/State/Buffer/BufferState.h @@ -32,11 +32,11 @@ public: void Delete(GLuint buffer); GLuint GetCurrentBinding(GLenum target) const; -private: + std::unordered_map currentBindings_; std::unordered_map buffers_; +private: std::set freeIds_; GLuint lastId_ = 0; - std::unordered_map currentBindings_; static bool IsValidTarget_(GLenum target); }; diff --git a/MG/MG_GL/State/Common/CommonState.cpp b/MG/MG_GL/State/Common/CommonState.cpp index ec437424..915d57c0 100644 --- a/MG/MG_GL/State/Common/CommonState.cpp +++ b/MG/MG_GL/State/Common/CommonState.cpp @@ -107,6 +107,7 @@ GLenum CommonState::Clear(GLbitfield mask) { if ((mask & ~validBits) != 0) { return GL_INVALID_VALUE; } + clearMask = mask; return GL_NO_ERROR; } diff --git a/MG/MG_GL/State/Common/CommonState.h b/MG/MG_GL/State/Common/CommonState.h index df77e207..b19417bb 100644 --- a/MG/MG_GL/State/Common/CommonState.h +++ b/MG/MG_GL/State/Common/CommonState.h @@ -9,7 +9,13 @@ #include "../../../Includes.h" class CommonState { -private: +public: + // Viewport + GLint viewport[4] = {0, 0, 0, 0}; + + // Color mask + GLboolean colorMask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE}; + // Pixel storage parameters std::unordered_map pixelStoreParams; @@ -22,21 +28,15 @@ private: // Clear state GLfloat clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GLfloat clearDepth = 1.0f; - - // Color mask - GLboolean colorMask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE}; - + GLenum clearMask; + // Depth state GLenum depthFunc = GL_LESS; GLboolean depthMask = GL_TRUE; - - // Viewport - GLint viewport[4] = {0, 0, 0, 0}; - + // Capability enables std::unordered_map capabilities; -public: CommonState(); // Return: the validity of the operation, according to OpenGL 3 standard @@ -57,6 +57,7 @@ public: GLenum Viewport(GLint x, GLint y, GLsizei width, GLsizei height); GLint QueryPixelStoreInt(GLenum pname); + }; #endif //MOBILEGL_COMMONSTATE_H diff --git a/MG/MG_GL/State/Framebuffer/FramebufferState.cpp b/MG/MG_GL/State/Framebuffer/FramebufferState.cpp index 2c899445..479813b8 100644 --- a/MG/MG_GL/State/Framebuffer/FramebufferState.cpp +++ b/MG/MG_GL/State/Framebuffer/FramebufferState.cpp @@ -48,7 +48,6 @@ GLenum FramebufferState::Bind(GLenum target, GLuint framebuffer) { if (target == GL_FRAMEBUFFER) { currentBindings_[GL_READ_FRAMEBUFFER] = framebuffer; currentBindings_[GL_DRAW_FRAMEBUFFER] = framebuffer; - currentBindings_[GL_FRAMEBUFFER] = framebuffer; } else { currentBindings_[target] = framebuffer; } @@ -60,6 +59,7 @@ GLenum FramebufferState::glAttachTexture2D(GLenum target, GLenum attachment, if (MG_Constants::Framebuffer::VALID_ATTACHMENTS.find(attachment) == MG_Constants::Framebuffer::VALID_ATTACHMENTS.end() || MG_Constants::Texture::VALID_TARGETS.find(textarget) == MG_Constants::Texture::VALID_TARGETS.end()) return GL_INVALID_ENUM; + if (target == GL_FRAMEBUFFER) target = GL_DRAW_FRAMEBUFFER; FramebufferObject* fbo = GetCurrentFBO(target); if (!fbo) return GL_INVALID_OPERATION; if (texture != 0 && !MG_State_T::textureState->IsTexture(texture)) @@ -84,6 +84,7 @@ GLenum FramebufferState::glAttachTexture2D(GLenum target, GLenum attachment, } GLenum FramebufferState::glValidateCompleteness(GLenum target) { + if (target == GL_FRAMEBUFFER) target = GL_DRAW_FRAMEBUFFER; FramebufferObject* fbo = GetCurrentFBO(target); if (!fbo) return GL_INVALID_OPERATION; return fbo->status; diff --git a/MG/MG_GL/State/Framebuffer/FramebufferState.h b/MG/MG_GL/State/Framebuffer/FramebufferState.h index 8cf7b3df..0bbae8a2 100644 --- a/MG/MG_GL/State/Framebuffer/FramebufferState.h +++ b/MG/MG_GL/State/Framebuffer/FramebufferState.h @@ -35,9 +35,10 @@ public: GLenum glValidateCompleteness(GLenum target); FramebufferObject* GetCurrentFBO(GLenum target); + std::unordered_map currentBindings_; // READ/DRAW/FRAMEBUFFER + private: std::unordered_map framebuffers_; - std::unordered_map currentBindings_; // READ/DRAW/FRAMEBUFFER std::set freeIds_; GLuint lastId_ = 0; bool ValidateHandle(GLuint framebuffer); diff --git a/MG/MG_GL/State/Program/ProgramState.h b/MG/MG_GL/State/Program/ProgramState.h index 5ea2ce7c..bbcfe372 100644 --- a/MG/MG_GL/State/Program/ProgramState.h +++ b/MG/MG_GL/State/Program/ProgramState.h @@ -47,7 +47,7 @@ struct ProgramObject { }; class ProgramState { -private: +public: std::unordered_map shaders_; std::unordered_map programs_; std::set freeShaderIds_; @@ -56,7 +56,6 @@ private: GLuint lastProgramId_ = 0; GLuint currentProgram_ = 0; -public: ProgramState(); ~ProgramState(); diff --git a/MG/MG_GL/State/Texture/TextureState.h b/MG/MG_GL/State/Texture/TextureState.h index b192ca76..804bc3a8 100644 --- a/MG/MG_GL/State/Texture/TextureState.h +++ b/MG/MG_GL/State/Texture/TextureState.h @@ -55,7 +55,6 @@ public: class TextureState { private: - std::vector textureUnits_; GLuint activeTextureUnit_ = 0; GLuint lastUsedID_ = 0; @@ -88,6 +87,7 @@ public: GLenum DeleteN(GLsizei n, const GLuint* textures); GLenum QueryLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params); + std::vector textureUnits_; private: size_t CalculatePixelDataSize_(GLenum format, GLenum type, GLsizei width, GLsizei height); void InvalidateTextureInAllUnits_(GLuint texture); diff --git a/MG/MG_GL/State/VertexArray/VertexArrayState.h b/MG/MG_GL/State/VertexArray/VertexArrayState.h index 8b02e9b9..ac19561c 100644 --- a/MG/MG_GL/State/VertexArray/VertexArrayState.h +++ b/MG/MG_GL/State/VertexArray/VertexArrayState.h @@ -9,7 +9,7 @@ #include "../../../Includes.h" struct VertexAttribState { - bool enabled = false; + bool enabled; GLint size = 4; GLenum type = GL_FLOAT; GLboolean normalized = GL_FALSE; @@ -43,11 +43,11 @@ public: GLuint GetBoundElementBuffer(); VertexArrayObject* GetCurrentVAO(); -private: + GLuint currentVao_ = 0; std::unordered_map vaos_; +private: std::set freeIds_; GLuint lastId_ = 0; - GLuint currentVao_ = 0; }; #endif //MOBILEGL_VERTEXARRAYSTATE_H diff --git a/MG/MG_UTIL/Program/GLSLTool.cpp b/MG/MG_UTIL/Program/GLSLTool.cpp index 91a77e9d..ad6983b8 100644 --- a/MG/MG_UTIL/Program/GLSLTool.cpp +++ b/MG/MG_UTIL/Program/GLSLTool.cpp @@ -402,4 +402,39 @@ namespace MG_Util::Program { } spvc_context_destroy(context); } + + std::string CompileSPIRVToGLSL(std::vector spirv, uint glslVersion, bool isES) { + spvc_context context = nullptr; + spvc_parsed_ir ir = nullptr; + spvc_compiler compiler_glsl = nullptr; + spvc_compiler_options options = nullptr; + spvc_resources resources = nullptr; + const spvc_reflected_resource *list = nullptr; + const char *result = nullptr; + size_t count; + + const SpvId *p_spirv = spirv.data(); + size_t word_count = spirv.size(); + + spvc_context_create(&context); + spvc_context_parse_spirv(context, p_spirv, word_count, &ir); + spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl); + spvc_compiler_create_shader_resources(compiler_glsl, &resources); + spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count); + spvc_compiler_create_compiler_options(compiler_glsl, &options); + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, glslVersion); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, isES ? SPVC_TRUE : SPVC_FALSE); + spvc_compiler_install_compiler_options(compiler_glsl, options); + spvc_compiler_compile(compiler_glsl, &result); + + if (!result) { + return{}; + } + + std::string essl = result; + + spvc_context_destroy(context); + + return essl; + } } \ No newline at end of file diff --git a/MG/MG_UTIL/Program/GLSLTool.h b/MG/MG_UTIL/Program/GLSLTool.h index 3d6812e7..2488f79c 100644 --- a/MG/MG_UTIL/Program/GLSLTool.h +++ b/MG/MG_UTIL/Program/GLSLTool.h @@ -177,6 +177,7 @@ namespace MG_Util::Program { std::string CompileGLSLToTShader(GLenum shaderType, const std::string& source, glslang::TShader *&shader); std::vector> CompileMultipleShadersToSPIRV(const ProgramState& state, ProgramObject& prog, std::string& infoLog); void ReflectSPIRVUniforms(const std::vector>& allSpirv, ProgramObject& prog, std::string& infoLog); + std::string CompileSPIRVToGLSL(std::vector spirv, uint glslVersion, bool isES); } #endif //MOBILEGL_GLSLTOOL_H From 0d7b342a2ec7f655b0e5770d79ec5e832138a4e2 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Mon, 5 May 2025 10:45:19 +0800 Subject: [PATCH 02/34] [Docs] (README): Fix the wrong Minecraft version. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55429bfe..00caec3f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ --- -**Target**: **OpenGL implementation compatible with Minecraft 1.17 to 1.12.4** +**Target**: **OpenGL implementation compatible with Minecraft 1.17 to 1.21.1** | Component | Development Progress\* | |--------------------------------|------------------------| From bb459061896b1e938984749e8275a88434ce4626 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 May 2025 13:33:29 +0800 Subject: [PATCH 03/34] [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 04/34] [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 05/34] [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 06/34] [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 07/34] [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 08/34] [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 09/34] [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 10/34] [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