[Feat] (GL_Drawing): Add DrawElementsSHITTILY. Add SPIRV->GLSL ES conversion.

This commit is contained in:
BZLZHH
2025-05-05 10:04:35 +08:00
parent 31d2d62a59
commit 3fa6aa660c
14 changed files with 903 additions and 38 deletions
+1 -1
View File
@@ -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";
@@ -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);
@@ -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<std::string> 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<GLuint, GLuint> s_textureMap;
static std::unordered_map<GLuint, GLuint> s_vaoMap;
static std::unordered_map<GLuint, GLuint> s_bufferMap;
static std::unordered_map<GLuint, GLuint> s_programMap;
static std::unordered_map<GLuint, GLuint> 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<GLuint, std::unordered_map<GLint, bool>> 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<GLuint, void*> s_bufferDirtyFlags_bufferObj;
void SyncAllBuffersToGLES(BufferState* bufferState) {
GLint currentVBO = 0, currentEBO = 0;
CallAndCheck(::GLES::glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &currentVBO);)
CallAndCheck(::GLES::glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &currentEBO);)
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<GLuint, 32> 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<GLsizei>(uniform.count / 1);
CallAndCheck(::GLES::glUniform1fv(location, num, uniform.floatData.data());)
break;
}
case GL_FLOAT_VEC2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 2);
CallAndCheck(::GLES::glUniform2fv(location, num, uniform.floatData.data());)
break;
}
case GL_FLOAT_VEC3: {
GLsizei num = static_cast<GLsizei>(uniform.count / 3);
CallAndCheck(::GLES::glUniform3fv(location, num, uniform.floatData.data());)
break;
}
case GL_FLOAT_VEC4: {
GLsizei num = static_cast<GLsizei>(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<GLsizei>(uniform.count / 1);
CallAndCheck(::GLES::glUniform1iv(location, num, uniform.intData.data());)
break;
}
case GL_INT_VEC2:
case GL_BOOL_VEC2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 2);
CallAndCheck(::GLES::glUniform2iv(location, num, uniform.intData.data());)
break;
}
case GL_INT_VEC3:
case GL_BOOL_VEC3: {
GLsizei num = static_cast<GLsizei>(uniform.count / 3);
CallAndCheck(::GLES::glUniform3iv(location, num, uniform.intData.data());)
break;
}
case GL_INT_VEC4:
case GL_BOOL_VEC4: {
GLsizei num = static_cast<GLsizei>(uniform.count / 4);
CallAndCheck(::GLES::glUniform4iv(location, num, uniform.intData.data());)
break;
}
case GL_UNSIGNED_INT: {
GLsizei num = static_cast<GLsizei>(uniform.count / 1);
CallAndCheck(::GLES::glUniform1uiv(location, num, uniform.uintData.data());)
break;
}
case GL_UNSIGNED_INT_VEC2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 2);
CallAndCheck(::GLES::glUniform2uiv(location, num, uniform.uintData.data());)
break;
}
case GL_UNSIGNED_INT_VEC3: {
GLsizei num = static_cast<GLsizei>(uniform.count / 3);
CallAndCheck(::GLES::glUniform3uiv(location, num, uniform.uintData.data());)
break;
}
case GL_UNSIGNED_INT_VEC4: {
GLsizei num = static_cast<GLsizei>(uniform.count / 4);
CallAndCheck(::GLES::glUniform4uiv(location, num, uniform.uintData.data());)
break;
}
case GL_FLOAT_MAT2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 4);
CallAndCheck(::GLES::glUniformMatrix2fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT3: {
GLsizei num = static_cast<GLsizei>(uniform.count / 9);
CallAndCheck(::GLES::glUniformMatrix3fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT4: {
GLsizei num = static_cast<GLsizei>(uniform.count / 16);
CallAndCheck(::GLES::glUniformMatrix4fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT2x3: {
GLsizei num = static_cast<GLsizei>(uniform.count / 6);
CallAndCheck(::GLES::glUniformMatrix2x3fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT2x4: {
GLsizei num = static_cast<GLsizei>(uniform.count / 8);
CallAndCheck(::GLES::glUniformMatrix2x4fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT3x2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 6);
CallAndCheck(::GLES::glUniformMatrix3x2fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT3x4: {
GLsizei num = static_cast<GLsizei>(uniform.count / 12);
CallAndCheck(::GLES::glUniformMatrix3x4fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT4x2: {
GLsizei num = static_cast<GLsizei>(uniform.count / 8);
CallAndCheck(::GLES::glUniformMatrix4x2fv(location, num, GL_FALSE, uniform.floatData.data());)
break;
}
case GL_FLOAT_MAT4x3: {
GLsizei num = static_cast<GLsizei>(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);
}
}
@@ -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);
+2 -2
View File
@@ -32,11 +32,11 @@ public:
void Delete(GLuint buffer);
GLuint GetCurrentBinding(GLenum target) const;
private:
std::unordered_map<GLenum, GLuint> currentBindings_;
std::unordered_map<GLuint, BufferObject> buffers_;
private:
std::set<GLuint> freeIds_;
GLuint lastId_ = 0;
std::unordered_map<GLenum, GLuint> currentBindings_;
static bool IsValidTarget_(GLenum target);
};
+1
View File
@@ -107,6 +107,7 @@ GLenum CommonState::Clear(GLbitfield mask) {
if ((mask & ~validBits) != 0) {
return GL_INVALID_VALUE;
}
clearMask = mask;
return GL_NO_ERROR;
}
+11 -10
View File
@@ -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<GLenum, GLint> 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<GLenum, bool> 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
@@ -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;
@@ -35,9 +35,10 @@ public:
GLenum glValidateCompleteness(GLenum target);
FramebufferObject* GetCurrentFBO(GLenum target);
std::unordered_map<GLenum, GLuint> currentBindings_; // READ/DRAW/FRAMEBUFFER
private:
std::unordered_map<GLuint, FramebufferObject> framebuffers_;
std::unordered_map<GLenum, GLuint> currentBindings_; // READ/DRAW/FRAMEBUFFER
std::set<GLuint> freeIds_;
GLuint lastId_ = 0;
bool ValidateHandle(GLuint framebuffer);
+1 -2
View File
@@ -47,7 +47,7 @@ struct ProgramObject {
};
class ProgramState {
private:
public:
std::unordered_map<GLuint, ShaderObject> shaders_;
std::unordered_map<GLuint, ProgramObject> programs_;
std::set<GLuint> freeShaderIds_;
@@ -56,7 +56,6 @@ private:
GLuint lastProgramId_ = 0;
GLuint currentProgram_ = 0;
public:
ProgramState();
~ProgramState();
+1 -1
View File
@@ -55,7 +55,6 @@ public:
class TextureState {
private:
std::vector<TextureUnitState> 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<TextureUnitState> textureUnits_;
private:
size_t CalculatePixelDataSize_(GLenum format, GLenum type, GLsizei width, GLsizei height);
void InvalidateTextureInAllUnits_(GLuint texture);
@@ -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<GLuint, VertexArrayObject> vaos_;
private:
std::set<GLuint> freeIds_;
GLuint lastId_ = 0;
GLuint currentVao_ = 0;
};
#endif //MOBILEGL_VERTEXARRAYSTATE_H
+35
View File
@@ -402,4 +402,39 @@ namespace MG_Util::Program {
}
spvc_context_destroy(context);
}
std::string CompileSPIRVToGLSL(std::vector<unsigned int> 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;
}
}
+1
View File
@@ -177,6 +177,7 @@ namespace MG_Util::Program {
std::string CompileGLSLToTShader(GLenum shaderType, const std::string& source, glslang::TShader *&shader);
std::vector<std::vector<unsigned>> CompileMultipleShadersToSPIRV(const ProgramState& state, ProgramObject& prog, std::string& infoLog);
void ReflectSPIRVUniforms(const std::vector<std::vector<unsigned>>& allSpirv, ProgramObject& prog, std::string& infoLog);
std::string CompileSPIRVToGLSL(std::vector<unsigned int> spirv, uint glslVersion, bool isES);
}
#endif //MOBILEGL_GLSLTOOL_H