From c68cbfa37b3189b8b106047ba500933743c2c824 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 6 May 2025 16:20:18 +0800 Subject: [PATCH] a bunch of junk, merging 'shitty' branch --- CMakeLists.txt | 1 + .../Implementations/GL/Drawing/GL_Drawing.cpp | 37 +- MG/MG_GL/State/Buffer/BufferState.h | 2 +- MG/MG_GL/State/Common/CommonState.h | 2 +- MG/MG_GL/State/Framebuffer/FramebufferState.h | 2 +- MG/MG_GL/State/Program/ProgramState.h | 2 +- MG/MG_GL/State/Texture/TextureState.h | 2 +- .../State/VertexArray/VertexArrayState.cpp | 5 +- MG/MG_GL/State/VertexArray/VertexArrayState.h | 2 +- MG/MG_RHI/GLES/RHI.cpp | 630 ++++++++++++++++++ MG/MG_RHI/GLES/RHI.h | 50 ++ MG/MG_UTIL/Program/DebugTool.cpp | 35 + MG/MG_UTIL/Program/DebugTool.h | 1 + build.gradle | 36 + 14 files changed, 797 insertions(+), 10 deletions(-) create mode 100644 MG/MG_RHI/GLES/RHI.cpp create mode 100644 MG/MG_RHI/GLES/RHI.h create mode 100644 build.gradle diff --git a/CMakeLists.txt b/CMakeLists.txt index accb47a3..814b8908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ add_library(${CMAKE_PROJECT_NAME} SHARED # MG_RHI/GLES MG/MG_RHI/GLES/Test/TestDrawing.cpp + MG/MG_RHI/GLES/RHI.cpp ) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ./includes) diff --git a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp index 72ea7cbb..8bf1f770 100644 --- a/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp +++ b/MG/MG_GL/Implementations/GL/Drawing/GL_Drawing.cpp @@ -3,12 +3,45 @@ // #include "GL_Drawing.h" +#include "../../../../MG_RHI/GLES/RHI.h" namespace MG_GL::GL { // This is a test function that directly draws textures. void DrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) { - ::GLES::glViewport(0,0,3200,1440); - MG_RHI::GLES::Test::DrawAllTextures(); +// ::GLES::glViewport(0,0,3200,1440); +// MG_RHI::GLES::Test::DrawAllTextures(); + VertexArrayState* vaState = MG_State_T::vertexArrayState; + VertexArrayObject* vao = &vaState->vaos_[vaState->currentVao_]; + + auto& rhi = MG_RHI::GLES::RHI::GetInstance(); + rhi.TransitionTo(MG_RHI::GLES::State::DrawElements); + + if (vao->elementBuffer != 0 && indices != nullptr) { + static GLuint dynamicIBO = 0; + if (dynamicIBO == 0) { + MGL_TRY(::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; + + MGL_TRY(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamicIBO);) + MGL_TRY(::GLES::glBufferData(GL_ELEMENT_ARRAY_BUFFER, dataSize, indices, GL_STREAM_DRAW);) + } + + if (vao->elementBuffer != 0 || indices != nullptr) { + MGL_TRY(::GLES::glDrawElements( + mode, + count, + type, + indices + );) + } }; void DrawArrays(GLenum mode, GLint first, GLsizei count) { ::GLES::glViewport(0,0,3200,1440); diff --git a/MG/MG_GL/State/Buffer/BufferState.h b/MG/MG_GL/State/Buffer/BufferState.h index 28db6c1f..05e2cf75 100644 --- a/MG/MG_GL/State/Buffer/BufferState.h +++ b/MG/MG_GL/State/Buffer/BufferState.h @@ -32,7 +32,7 @@ public: void Delete(GLuint buffer); GLuint GetCurrentBinding(GLenum target) const; -private: +public: // TODO std::unordered_map buffers_; std::set freeIds_; GLuint lastId_ = 0; diff --git a/MG/MG_GL/State/Common/CommonState.h b/MG/MG_GL/State/Common/CommonState.h index df77e207..c3b80a75 100644 --- a/MG/MG_GL/State/Common/CommonState.h +++ b/MG/MG_GL/State/Common/CommonState.h @@ -9,7 +9,7 @@ #include "../../../Includes.h" class CommonState { -private: +public: // TODO // Pixel storage parameters std::unordered_map pixelStoreParams; diff --git a/MG/MG_GL/State/Framebuffer/FramebufferState.h b/MG/MG_GL/State/Framebuffer/FramebufferState.h index 8cf7b3df..e6cc1cf9 100644 --- a/MG/MG_GL/State/Framebuffer/FramebufferState.h +++ b/MG/MG_GL/State/Framebuffer/FramebufferState.h @@ -35,7 +35,7 @@ public: GLenum glValidateCompleteness(GLenum target); FramebufferObject* GetCurrentFBO(GLenum target); -private: +public: // TODO std::unordered_map framebuffers_; std::unordered_map currentBindings_; // READ/DRAW/FRAMEBUFFER std::set freeIds_; diff --git a/MG/MG_GL/State/Program/ProgramState.h b/MG/MG_GL/State/Program/ProgramState.h index 5ea2ce7c..1b220b66 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: // TODO std::unordered_map shaders_; std::unordered_map programs_; std::set freeShaderIds_; diff --git a/MG/MG_GL/State/Texture/TextureState.h b/MG/MG_GL/State/Texture/TextureState.h index b192ca76..154dc92e 100644 --- a/MG/MG_GL/State/Texture/TextureState.h +++ b/MG/MG_GL/State/Texture/TextureState.h @@ -54,7 +54,7 @@ public: }; class TextureState { -private: +public: // TODO std::vector textureUnits_; GLuint activeTextureUnit_ = 0; diff --git a/MG/MG_GL/State/VertexArray/VertexArrayState.cpp b/MG/MG_GL/State/VertexArray/VertexArrayState.cpp index f6626b75..07e60742 100644 --- a/MG/MG_GL/State/VertexArray/VertexArrayState.cpp +++ b/MG/MG_GL/State/VertexArray/VertexArrayState.cpp @@ -19,7 +19,7 @@ GLenum VertexArrayState::Create(GLuint* array) { } *array = id; - vaos_[id].generated = true; +// vaos_[id].generated = true; return GL_NO_ERROR; } @@ -41,8 +41,9 @@ GLenum VertexArrayState::CreateN(GLsizei n, GLuint* arrays) { } GLenum VertexArrayState::Bind(GLuint array) { - if (array != 0 && !vaos_.count(array)) return GL_INVALID_OPERATION; + if (array != 0 && vaos_.find(array) == vaos_.end()) return GL_INVALID_OPERATION; currentVao_ = array; + GetCurrentVAO()->generated = true; return GL_NO_ERROR; } diff --git a/MG/MG_GL/State/VertexArray/VertexArrayState.h b/MG/MG_GL/State/VertexArray/VertexArrayState.h index 8b02e9b9..5a67de7f 100644 --- a/MG/MG_GL/State/VertexArray/VertexArrayState.h +++ b/MG/MG_GL/State/VertexArray/VertexArrayState.h @@ -43,7 +43,7 @@ public: GLuint GetBoundElementBuffer(); VertexArrayObject* GetCurrentVAO(); -private: +public: // TODO std::unordered_map vaos_; std::set freeIds_; GLuint lastId_ = 0; diff --git a/MG/MG_RHI/GLES/RHI.cpp b/MG/MG_RHI/GLES/RHI.cpp new file mode 100644 index 00000000..d24b280f --- /dev/null +++ b/MG/MG_RHI/GLES/RHI.cpp @@ -0,0 +1,630 @@ +// +// Created by Swung 0x48 on 2025/5/6. +// + +#include "RHI.h" +#undef MOBILEGL_PROGRAM_DEBUGTOOL_H +#include "../../MG_UTIL/Program/DebugTool.h" + +namespace MG_RHI::GLES { + 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; + } + + void RHI::CheckError() { + while (GLenum err = ::GLES::glGetError() != GL_NO_ERROR) { + MG_Util::Debug::LogE("-> GLES Error: %s", MG_Util::Debug::GLEnumToString(err)); + } + } + + void RHI::SyncAllTexturesToGLES(TextureState* textureState) { + MG_Util::Debug::LogD("Syncing all textures to GLES..."); + for (auto& [mgTexId, texObj] : textureState->textures) { + if (!texObj.generated) continue; + + if (textureMap_.find(mgTexId) == textureMap_.end()) { + GLuint glTexId; + MGL_TRY(::GLES::glGenTextures(1, &glTexId);) + textureMap_[mgTexId] = glTexId; + GLenum target = texObj.target; + MGL_TRY(::GLES::glBindTexture(target, glTexId);) + + for (auto& [pname, param] : texObj.params.texPropertiesInt) { + MGL_TRY(::GLES::glTexParameteri(target, pname, param);) + } + for (auto& [pname, param] : texObj.params.texPropertiesFloat) { + MGL_TRY(::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: + MGL_TRY(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, mip.type, data + );) + 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 = textureMap_[mgTexId]; + GLenum target = texObj.target; + MGL_TRY(::GLES::glBindTexture(target, glTexId);) + + for (auto& [level, mip] : texObj.params.mipmapData) { + if (!textureLevelUploaded_[mgTexId][level]) { + bool levelInitialized = textureLevelUploaded_[mgTexId].count(level); + const void* data = !mip.pixelData.empty() ? mip.pixelData.data() : nullptr; + + switch (target) { + case GL_TEXTURE_2D: + if (levelInitialized) { + MGL_TRY(::GLES::glTexSubImage2D( + target, level, + 0, 0, + mip.width, mip.height, + mip.format, mip.type, data + );) + textureLevelUploaded_[mgTexId][level] = true; + MG_Util::Debug::LogD("Updated texture %u level %d with glTexSubImage2D", mgTexId, level); + } else { + MGL_TRY(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, mip.type, data + );) + 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); + } + } + MGL_TRY(::GLES::glBindTexture(GL_TEXTURE_2D, 0);) + } + + void RHI::SyncAllBuffersToGLES(BufferState* bufferState) { + GLint currentVBO = 0, currentEBO = 0; + MGL_TRY(::GLES::glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤tVBO);) + MGL_TRY(::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 (bufferMap_.find(mgBufferId) == bufferMap_.end()) { + if (bufferMap_.find(mgBufferId) == bufferMap_.end()) { + GLuint glBuffer; + MGL_TRY(::GLES::glGenBuffers(1, &glBuffer);) + bufferMap_[mgBufferId] = glBuffer; + } + MGL_TRY(::GLES::glBindBuffer(bufferObj.target, bufferMap_[mgBufferId]);) + MGL_TRY(::GLES::glBufferData( + bufferObj.target, + bufferObj.data.size(), + bufferObj.data.data(), + bufferObj.usage + );) + } + } + MGL_TRY(::GLES::glBindBuffer(GL_ARRAY_BUFFER, currentVBO);) + MGL_TRY(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, currentEBO);) + } + + void RHI::TransitionTo(State state) { + currentState_ = state; + + 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; + + const GLint* viewport = commonState->viewport; + if (viewport[2] > 0 && viewport[3] > 0) { + MGL_TRY(::GLES::glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);) + } + + if (commonState->capabilities[GL_BLEND]) { + MGL_TRY(::GLES::glEnable(GL_BLEND);) + MGL_TRY(::GLES::glBlendFuncSeparate( + commonState->blendSrcRGB, commonState->blendDstRGB, + commonState->blendSrcAlpha, commonState->blendDstAlpha + );) + } else { + MGL_TRY(::GLES::glDisable(GL_BLEND);) + } + + if (commonState->capabilities[GL_DEPTH_TEST]) { + MGL_TRY(::GLES::glEnable(GL_DEPTH_TEST);) + MGL_TRY(::GLES::glDepthMask(commonState->depthMask);) + MGL_TRY(::GLES::glDepthFunc(commonState->depthFunc);) + } else { + MGL_TRY(::GLES::glDisable(GL_DEPTH_TEST);) + } + + if (commonState->capabilities[GL_CULL_FACE]) { + MGL_TRY(::GLES::glEnable(GL_CULL_FACE);) + } else { + MGL_TRY(::GLES::glDisable(GL_CULL_FACE);) + } + + MGL_TRY(::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]) { + MGL_TRY(::GLES::glActiveTexture(glUnit);) + for (auto& [target, texId] : mgUnit.boundTextures) { + if (texId == 0) { + MGL_TRY(::GLES::glBindTexture(target, 0);) + lastBoundTextures[unitIdx] = 0; + continue; + } + + if (textureMap_.find(texId) == textureMap_.end()) { + GLuint glTexId; + MGL_TRY(::GLES::glGenTextures(1, &glTexId);) + textureMap_[texId] = glTexId; + + MGL_TRY(::GLES::glBindTexture(target, glTexId);) + TextureObject& mgTex = textureState->textures[texId]; + + for (auto& [pname, param] : mgTex.params.texPropertiesInt) { + MGL_TRY(::GLES::glTexParameteri(target, pname, param);) + } + for (auto& [pname, param] : mgTex.params.texPropertiesFloat) { + MGL_TRY(::GLES::glTexParameterf(target, pname, param);) + } + + for (auto& [level, mip] : mgTex.params.mipmapData) { + MGL_TRY(::GLES::glTexImage2D( + target, level, mip.internalFormat, + mip.width, mip.height, 0, + mip.format, mip.type, mip.pixelData.data() + );) + } + } + MGL_TRY(::GLES::glBindTexture(target, textureMap_[texId]);) + lastBoundTextures[unitIdx] = textureMap_[texId]; + } + } + } + + // Buffer + SyncAllBuffersToGLES(bufferState); + + // VAO + GLuint mgVAO = vaState->currentVao_; + VertexArrayObject* vao = &vaState->vaos_[vaState->currentVao_]; + for (auto& [mgVAOId, mgVAO] : vaState->vaos_) { + MG_Util::Debug::LogD("Uploading VAO: %d", mgVAOId); + if (!mgVAO.generated) continue; + + // TODO: Check is the VAO changes rather than always update it. + GLuint glVAO; + if (vaoMap_.find(mgVAOId) == vaoMap_.end()) { + MGL_TRY(::GLES::glGenVertexArrays(1, &glVAO);) + vaoMap_[mgVAOId] = glVAO; + } else { + glVAO = vaoMap_[mgVAOId]; + } + MGL_TRY(::GLES::glBindVertexArray(glVAO);) + + if (mgVAO.elementBuffer != 0 && bufferMap_.count(mgVAO.elementBuffer)) { + MGL_TRY(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferMap_[mgVAO.elementBuffer]);) + } + + for (auto& [index, attrib] : mgVAO.attribs) { + MG_Util::Debug::LogD("attrib #%s, type = %s, size = %d, stride = %d, pointer = %p", + index, MG_Util::Debug::GLEnumToString(attrib.type), attrib.size, attrib.stride, attrib.pointer); + if (attrib.buffer != 0 && bufferMap_.count(attrib.buffer)) { + MGL_TRY(::GLES::glBindBuffer(GL_ARRAY_BUFFER, bufferMap_[attrib.buffer]);) + + if (attrib.isInteger) { + MGL_TRY(::GLES::glVertexAttribIPointer( + index, attrib.size, attrib.type, + attrib.stride, attrib.pointer + );) + } else { + MGL_TRY(::GLES::glVertexAttribPointer( + index, attrib.size, attrib.type, + attrib.normalized ? GL_TRUE : GL_FALSE, + attrib.stride, attrib.pointer + );) + } + + if (attrib.enabled) { + MGL_TRY(::GLES::glEnableVertexAttribArray(index);) + } else { + MGL_TRY(::GLES::glDisableVertexAttribArray(index);) + } + } + } + MGL_TRY(::GLES::glBindVertexArray(0);) + //} + } + GLuint currentMgVAO = vaState->currentVao_; + if (vaoMap_.count(currentMgVAO)) { + MGL_TRY(::GLES::glBindVertexArray(vaoMap_[currentMgVAO]);) + + VertexArrayObject& currentVAO = vaState->vaos_[currentMgVAO]; + for (auto& [index, attrib] : currentVAO.attribs) { + if (attrib.enabled) { + MGL_TRY(::GLES::glEnableVertexAttribArray(index);) + } else { + MGL_TRY(::GLES::glDisableVertexAttribArray(index);) + } + } + } else { + MGL_TRY(::GLES::glBindVertexArray(0);) + } + + + // EBO + if (vao->elementBuffer != 0) { + if (bufferMap_.count(vao->elementBuffer)) { + MGL_TRY(::GLES::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferMap_[vao->elementBuffer]);) + } + } + + // Program + GLuint currentProgram = programState->GetCurrentProgram(); + if (currentProgram != lastBoundProgram) { + if (programMap_.find(currentProgram) == 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() }; + MGL_TRY(::GLES::glShaderSource(glShader, 1, s, nullptr);) + MGL_TRY(::GLES::glCompileShader(glShader);) + + GLint success; + MGL_TRY(::GLES::glGetShaderiv(glShader, GL_COMPILE_STATUS, &success);) + if (!success) { + GLchar infoLog[512]; + MGL_TRY(::GLES::glGetShaderInfoLog(glShader, 512, nullptr, infoLog);) + MG_Util::Debug::LogE("Shader compilation failed: %s", infoLog); + } + MGL_TRY(::GLES::glAttachShader(glProgram, glShader);) + } + + MGL_TRY(::GLES::glLinkProgram(glProgram);) + GLint linkStatus; + MGL_TRY(::GLES::glGetProgramiv(glProgram, GL_LINK_STATUS, &linkStatus);) + if (!linkStatus) { + GLchar infoLog[512]; + MGL_TRY(::GLES::glGetProgramInfoLog(glProgram, 512, nullptr, infoLog);) + MG_Util::Debug::LogE("Program linking failed: %s", infoLog); + } + programMap_[currentProgram] = glProgram; + } + MGL_TRY(::GLES::glUseProgram(programMap_[currentProgram]);) + lastBoundProgram = currentProgram; + + // Uniform + ProgramObject& mgProgram = programState->programs_[currentProgram]; + for (auto& [name, uniform] : mgProgram.uniformValues) { + GLint location = ::GLES::glGetUniformLocation(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); + MGL_TRY(::GLES::glUniform1fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + MGL_TRY(::GLES::glUniform2fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + MGL_TRY(::GLES::glUniform3fv(location, num, uniform.floatData.data());) + break; + } + case GL_FLOAT_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + MGL_TRY(::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); + MGL_TRY(::GLES::glUniform1iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC2: + case GL_BOOL_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + MGL_TRY(::GLES::glUniform2iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC3: + case GL_BOOL_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + MGL_TRY(::GLES::glUniform3iv(location, num, uniform.intData.data());) + break; + } + case GL_INT_VEC4: + case GL_BOOL_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + MGL_TRY(::GLES::glUniform4iv(location, num, uniform.intData.data());) + break; + } + + case GL_UNSIGNED_INT: { + GLsizei num = static_cast(uniform.count / 1); + MGL_TRY(::GLES::glUniform1uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC2: { + GLsizei num = static_cast(uniform.count / 2); + MGL_TRY(::GLES::glUniform2uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC3: { + GLsizei num = static_cast(uniform.count / 3); + MGL_TRY(::GLES::glUniform3uiv(location, num, uniform.uintData.data());) + break; + } + case GL_UNSIGNED_INT_VEC4: { + GLsizei num = static_cast(uniform.count / 4); + MGL_TRY(::GLES::glUniform4uiv(location, num, uniform.uintData.data());) + break; + } + + case GL_FLOAT_MAT2: { + GLsizei num = static_cast(uniform.count / 4); + MGL_TRY(::GLES::glUniformMatrix2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3: { + GLsizei num = static_cast(uniform.count / 9); + MGL_TRY(::GLES::glUniformMatrix3fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4: { + GLsizei num = static_cast(uniform.count / 16); + MGL_TRY(::GLES::glUniformMatrix4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT2x3: { + GLsizei num = static_cast(uniform.count / 6); + MGL_TRY(::GLES::glUniformMatrix2x3fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT2x4: { + GLsizei num = static_cast(uniform.count / 8); + MGL_TRY(::GLES::glUniformMatrix2x4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3x2: { + GLsizei num = static_cast(uniform.count / 6); + MGL_TRY(::GLES::glUniformMatrix3x2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT3x4: { + GLsizei num = static_cast(uniform.count / 12); + MGL_TRY(::GLES::glUniformMatrix3x4fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4x2: { + GLsizei num = static_cast(uniform.count / 8); + MGL_TRY(::GLES::glUniformMatrix4x2fv(location, num, GL_FALSE, uniform.floatData.data());) + break; + } + case GL_FLOAT_MAT4x3: { + GLsizei num = static_cast(uniform.count / 12); + MGL_TRY(::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) { + MGL_TRY(::GLES::glBindFramebuffer(GL_FRAMEBUFFER, 0);) + glFBO = 0; + } else { + bool isNewGlesFBO = false; + + if (framebufferMap_.find(currentFBO) == framebufferMap_.end()) { + MGL_TRY(::GLES::glGenFramebuffers(1, &glFBO);) + framebufferMap_[currentFBO] = glFBO; + MGL_TRY(::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 = framebufferMap_[currentFBO]; + MGL_TRY(::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 (textureMap_.count(mgAtt.handle)) { + expectedGLTexId = textureMap_[mgAtt.handle]; + } else { + MG_Util::Debug::LogE("MobileGL Texture %u for FBO %u attachment 0x%X not found in textureMap_ during FBO sync!", mgAtt.handle, currentFBO, mgAttachmentPoint); + for (auto const& [key, val] : textureMap_) + MG_Util::Debug::LogW(" key: %d, val: %d", key, val); + continue; + } + } + + GLint glesAttachedType = 0; + GLint glesAttachedName = 0; + + MGL_TRY(::GLES::glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, mgAttachmentPoint, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &glesAttachedType);) + + if (glesAttachedType == GL_TEXTURE) { + MGL_TRY(::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); + + MGL_TRY(::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; + } + + + } + } +} \ No newline at end of file diff --git a/MG/MG_RHI/GLES/RHI.h b/MG/MG_RHI/GLES/RHI.h new file mode 100644 index 00000000..1f69d525 --- /dev/null +++ b/MG/MG_RHI/GLES/RHI.h @@ -0,0 +1,50 @@ +// +// Created by Swung 0x48 on 2025/5/6. +// + +#ifndef MOBILEGL_RHI_H +#define MOBILEGL_RHI_H + +#include "../../Includes.h" + +namespace MG_RHI::GLES { + enum class State { + None, + DrawArrays, + DrawElements + }; + + class RHI { + public: + void TransitionTo(State toState); + + static inline RHI& GetInstance() { + static RHI s_rhi; + return s_rhi; + } + + static void CheckError(); + + void SyncAllTexturesToGLES(TextureState* textureState); + void SyncAllBuffersToGLES(BufferState* bufferState); + private: + + State currentState_ = State::None; + std::unordered_map textureMap_; + std::unordered_map> textureLevelUploaded_; + std::unordered_map bufferMap_; + std::array lastBoundTextures { 0 }; + std::unordered_map vaoMap_; + + std::unordered_map programMap_; + std::unordered_map framebufferMap_; + + GLuint lastBoundVAO = 0; + GLuint lastBoundProgram = 0; + GLuint lastBoundFBO = 0; + }; +} + +#define MGL_TRY(operation) MG_Util::Debug::LogD("GLES call: %s", #operation); operation MG_RHI::GLES::RHI::CheckError(); + +#endif //MOBILEGL_RHI_H diff --git a/MG/MG_UTIL/Program/DebugTool.cpp b/MG/MG_UTIL/Program/DebugTool.cpp index 608b9495..9e930876 100644 --- a/MG/MG_UTIL/Program/DebugTool.cpp +++ b/MG/MG_UTIL/Program/DebugTool.cpp @@ -147,4 +147,39 @@ namespace MG_Util::Program { } DumpUniforms(state, current); } + + 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/DebugTool.h b/MG/MG_UTIL/Program/DebugTool.h index e052257f..4d375352 100644 --- a/MG/MG_UTIL/Program/DebugTool.h +++ b/MG/MG_UTIL/Program/DebugTool.h @@ -11,6 +11,7 @@ namespace MG_Util::Program { std::string FormatUniformValue(const UniformValue& value); void DumpUniforms(const ProgramState& state, GLuint program); void DumpCurrentUniforms(const ProgramState& state); + std::string CompileSPIRVToGLSL(std::vector spirv, uint glslVersion, bool isES); } #endif //MOBILEGL_PROGRAM_DEBUGTOOL_H diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..2f87c3ba --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.library' + +android { + namespace 'top.mobilegl.mobilegl' + compileSdk 34 + + defaultConfig { + minSdk 26 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + ndkVersion '27.1.12297006' + } + + 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 + } +}