Files
MobileGL/MG/MG_GL/State/Program/ProgramState.cpp
T

531 lines
23 KiB
C++

//
// Created by BZLZHH on 2025/5/1.
// TODO: Add more gl error check for buffer state manager.
// TODO: Check if the state machine really meets up to OpenGL 3 standard.
#include "ProgramState.h"
// Re-Include Includes.h, cuz of the absence of Program/DebugTool.h and Program/GLSLTool.cpp.
#undef MOBILEGL_GLSLTOOL_H
#undef MOBILEGL_PROGRAM_DEBUGTOOL_H
#include "../../../Includes.h"
ProgramState::ProgramState() {
glslang::InitializeProcess();
}
ProgramState::~ProgramState() {
glslang::FinalizeProcess();
}
GLenum ProgramState::CreateProgram(GLuint* program) {
MG_Util::Debug::LogD("MG_State: Program: CreateProgram called, program ptr=%p", program);
if (!program) {
MG_Util::Debug::LogE("MG_State: Program: CreateProgram error: program pointer is null");
return GL_INVALID_VALUE;
}
GLuint id = freeProgramIds_.empty() ? ++lastProgramId_ : *freeProgramIds_.begin();
if (!freeProgramIds_.empty())
freeProgramIds_.erase(freeProgramIds_.begin());
ProgramObject obj;
obj.linked = {UncertainBool::Unknown, UncertainBool::False};
obj.linkStatus = GL_FALSE;
obj.markedForDeletion = false;
programs_[id] = obj;
*program = id;
MG_Util::Debug::LogD("MG_State: Program: CreateProgram success, new id=%u", id);
return GL_NO_ERROR;
}
GLenum ProgramState::DeleteProgram(GLuint program) {
MG_Util::Debug::LogD("MG_State: Program: DeleteProgram called, id=%u", program);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: DeleteProgram error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
programs_.erase(program);
freeProgramIds_.insert(program);
if (currentProgram_ == program)
currentProgram_ = 0;
MG_Util::Debug::LogD("MG_State: Program: DeleteProgram success, id=%u", program);
return GL_NO_ERROR;
}
GLenum ProgramState::LinkShaderToProgram(GLuint program, GLuint shader) {
MG_Util::Debug::LogD("MG_State: Program: LinkShaderToProgram called, program=%u, shader=%u",
program, shader);
if (!ValidateProgram_(program) || !ValidateShader_(shader)) {
MG_Util::Debug::LogE("MG_State: Program: LinkShaderToProgram error: invalid ids program=%u shader=%u",
program, shader);
return GL_INVALID_VALUE;
}
auto& prog = programs_[program];
if (std::find(prog.attachedShaders.begin(),
prog.attachedShaders.end(), shader) != prog.attachedShaders.end()) {
MG_Util::Debug::LogW("MG_State: Program: LinkShaderToProgram warning: shader %u already attached to program %u",
shader, program);
return GL_INVALID_OPERATION;
}
prog.attachedShaders.push_back(shader);
MG_Util::Debug::LogD("MG_State: Program: LinkShaderToProgram success: shader %u attached to program %u",
shader, program);
return GL_NO_ERROR;
}
GLenum ProgramState::FinalizeProgramPipeline(GLuint program) {
MG_Util::Debug::LogD("MG_State: Program: FinalizeProgramPipeline called, id=%u", program);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: FinalizeProgramPipeline error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
auto& prog = programs_[program];
for (GLuint shader : prog.attachedShaders) {
if (!shaders_[shader].compiled.toBool()) {
MG_Util::Debug::LogE("MG_State: Program: FinalizeProgramPipeline error: shader %u not compiled", shader);
return GL_INVALID_OPERATION;
}
}
prog.infoLog.clear();
for (GLuint shaderId : prog.attachedShaders) {
auto& shader = shaders_[shaderId];
if (!shader.compiled.toBool() || shader.compiledSpirv.empty()) {
prog.infoLog = "Program linking failed: \n" +
MG_Util::Program::GetShaderTypeName(shader.type) + " is not compiled.";
prog.linked = UncertainBool::False;
prog.linkStatus = GL_FALSE;
MG_Util::Debug::LogE("MG_State: Program: FinalizeProgramPipeline error: %s", prog.infoLog.c_str());
return GL_INVALID_OPERATION;
}
}
std::vector<std::vector<unsigned>> allSpirv =
MG_Util::Program::CompileMultipleShadersToSPIRV(*this, prog, prog.infoLog);
MG_Util::Program::ReflectSPIRVUniforms(allSpirv, prog, prog.infoLog);
if (!prog.infoLog.empty()) {
prog.linked = UncertainBool::False;
prog.linkStatus = GL_FALSE;
prog.infoLog = "Program linking failed: " + prog.infoLog;
MG_Util::Debug::LogE("MG_State: Program: FinalizeProgramPipeline error: %s", prog.infoLog.c_str());
return GL_INVALID_OPERATION;
}
for (auto& [name, index] : prog.attribBindings) {
prog.attribLocations[name] = index;
}
prog.linked = {UncertainBool::Unknown, UncertainBool::True};
prog.linkStatus = GL_TRUE;
CleanupShaders_();
MG_Util::Program::DumpUniforms(*this, program);
MG_Util::Debug::LogD("MG_State: Program: FinalizeProgramPipeline success, id=%u", program);
return GL_NO_ERROR;
}
GLenum ProgramState::ActivateRenderProgram(GLuint program) {
MG_Util::Debug::LogD("MG_State: Program: ActivateRenderProgram called, id=%u", program);
if (program != 0 && !ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: ActivateRenderProgram error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
MG_Util::Program::DumpCurrentUniforms(*this);
currentProgram_ = program;
MG_Util::Program::DumpCurrentUniforms(*this);
MG_Util::Debug::LogD("MG_State: Program: ActivateRenderProgram success, current program=%u", currentProgram_);
return GL_NO_ERROR;
}
GLenum ProgramState::DefineProgramAttributeBinding(GLuint program, GLuint index, const GLchar* name) {
MG_Util::Debug::LogD("MG_State: Program: DefineProgramAttributeBinding called, program=%u, index=%u, name=%s",
program, index, name);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: DefineProgramAttributeBinding error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
if (index >= GL_MAX_VERTEX_ATTRIBS) {
MG_Util::Debug::LogE("MG_State: Program: DefineProgramAttributeBinding error: index %u out of range", index);
return GL_INVALID_VALUE;
}
programs_[program].attribBindings[name] = index;
MG_Util::Debug::LogD("MG_State: Program: DefineProgramAttributeBinding success: %s -> %u", name, index);
return GL_NO_ERROR;
}
GLint ProgramState::QueryProgramAttributeBinding(GLuint program, const GLchar* name) {
MG_Util::Debug::LogD("MG_State: Program: QueryProgramAttributeBinding called, program=%u, name=%s",
program, name);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: QueryProgramAttributeBinding error: invalid program id %u", program);
return -1;
}
auto& prog = programs_[program];
auto it = prog.attribLocations.find(name);
if (it != prog.attribLocations.end()) {
GLint loc = it->second;
MG_Util::Debug::LogD("MG_State: Program: QueryProgramAttributeBinding success: %s -> %d", name, loc);
return loc;
}
MG_Util::Debug::LogW("MG_State: Program: QueryProgramAttributeBinding warning: name '%s' not found", name);
return -1;
}
GLenum ProgramState::QueryProgramStateIntVector(GLuint program, GLenum pname, GLint* params) {
MG_Util::Debug::LogD("MG_State: Program: QueryProgramStateIntVector called, program=%u, pname=0x%X",
program, pname);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: QueryProgramStateIntVector error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
auto& prog = programs_[program];
switch (pname) {
case GL_LINK_STATUS: *params = prog.linkStatus; break;
case GL_DELETE_STATUS: *params = prog.markedForDeletion; break;
case GL_ATTACHED_SHADERS:*params = (GLint)prog.attachedShaders.size(); break;
default:
MG_Util::Debug::LogE("MG_State: Program: QueryProgramStateIntVector error: invalid pname 0x%X", pname);
return GL_INVALID_ENUM;
}
MG_Util::Debug::LogD("MG_State: Program: QueryProgramStateIntVector success: value=%d", *params);
return GL_NO_ERROR;
}
GLenum ProgramState::QueryShaderStateIntVector(GLuint shader, GLenum pname, GLint* params) {
MG_Util::Debug::LogD("MG_State: Program: QueryShaderStateIntVector called, shader=%u, pname=0x%X",
shader, pname);
if (!ValidateShader_(shader)) {
MG_Util::Debug::LogE("MG_State: Program: QueryShaderStateIntVector error: invalid shader id %u", shader);
return GL_INVALID_VALUE;
}
auto& obj = shaders_[shader];
switch (pname) {
case GL_COMPILE_STATUS: *params = obj.compileStatus; break;
case GL_DELETE_STATUS: *params = obj.markedForDeletion; break;
case GL_SHADER_TYPE: *params = obj.type; break;
default:
MG_Util::Debug::LogE("MG_State: Program: QueryShaderStateIntVector error: invalid pname 0x%X", pname);
return GL_INVALID_ENUM;
}
MG_Util::Debug::LogD("MG_State: Program: QueryShaderStateIntVector success: value=%d", *params);
return GL_NO_ERROR;
}
template<typename T>
GLenum ProgramState::SetUniform(GLuint program, GLint location, GLsizei count, const T* value, GLenum type) {
MG_Util::Debug::LogD("MG_State: Program: SetUniform called, program=%u, location=%d, count=%d, type=0x%X",
program, location, count, type);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: SetUniform error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
if (location < -1) {
MG_Util::Debug::LogE("MG_State: Program: SetUniform error: invalid location %d", location);
return GL_INVALID_VALUE;
}
if (count < 0) {
MG_Util::Debug::LogE("MG_State: Program: SetUniform error: invalid count %d", count);
return GL_INVALID_VALUE;
}
if (location == -1) {
MG_Util::Debug::LogW("MG_State: Program: SetUniform warning: location -1, no-op");
return GL_NO_ERROR;
}
ProgramObject& prog = programs_[program];
std::string uniformName;
for (const auto& pair : prog.uniformLocations) {
if (pair.second == location) {
uniformName = pair.first;
break;
}
}
if (uniformName.empty()) {
MG_Util::Debug::LogE("MG_State: Program: SetUniform error: no uniform at location %d in program %u",
location, program);
return GL_INVALID_OPERATION;
}
UniformValue& uniform = prog.uniformValues[uniformName];
uniform.setData(type, count, value);
MG_Util::Debug::LogD("MG_State: Program: SetUniform success: %s set", uniformName.c_str());
return GL_NO_ERROR;
}
template<typename T>
GLenum ProgramState::SetUniformMatrix(GLuint program, GLint location, GLsizei count, GLboolean transpose, const T* value, GLenum matrixType) {
MG_Util::Debug::LogD("MG_State: Program: SetUniformMatrix called, program=%u, location=%d, count=%d, matrixType=0x%X",
program, location, count, matrixType);
(void)transpose;
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: SetUniformMatrix error: invalid program id %u", program);
return GL_INVALID_VALUE;
}
if (location < -1) {
MG_Util::Debug::LogE("MG_State: Program: SetUniformMatrix error: invalid location %d", location);
return GL_INVALID_VALUE;
}
if (count < 0) {
MG_Util::Debug::LogE("MG_State: Program: SetUniformMatrix error: invalid count %d", count);
return GL_INVALID_VALUE;
}
if (location == -1) {
MG_Util::Debug::LogW("MG_State: Program: SetUniformMatrix warning: location -1, no-op");
return GL_NO_ERROR;
}
ProgramObject& prog = programs_[program];
std::string uniformName;
for (const auto& pair : prog.uniformLocations) {
if (pair.second == location) {
uniformName = pair.first;
break;
}
}
if (uniformName.empty()) {
MG_Util::Debug::LogE("MG_State: Program: SetUniformMatrix error: no uniform at location %d in program %u",
location, program);
return GL_INVALID_OPERATION;
}
UniformValue& uniform = prog.uniformValues[uniformName];
uniform.setData(matrixType, count * MG_Util::Program::GetMatrixElementCount(matrixType), value);
MG_Util::Debug::LogD("MG_State: Program: SetUniformMatrix success: %s set", uniformName.c_str());
return GL_NO_ERROR;
}
#define IMPLEMENT_UNIFORM_FUNCTIONS(type, suffix, vecType) \
void ProgramState::UpdateUniform##suffix##1(GLint location, type v0) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "1 called, location=%d", location); \
SetUniform<type>(currentProgram_, location, 1, &v0, vecType); \
} \
void ProgramState::UpdateUniform##suffix##2(GLint location, type v0, type v1) { \
type values[2] = {v0, v1}; \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "2 called, location=%d", location); \
SetUniform<type>(currentProgram_, location, 1, values, vecType); \
} \
void ProgramState::UpdateUniform##suffix##3(GLint location, type v0, type v1, type v2) { \
type values[3] = {v0, v1, v2}; \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "3 called, location=%d", location); \
SetUniform<type>(currentProgram_, location, 1, values, vecType); \
} \
void ProgramState::UpdateUniform##suffix##4(GLint location, type v0, type v1, type v2, type v3) { \
type values[4] = {v0, v1, v2, v3}; \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "4 called, location=%d", location); \
SetUniform<type>(currentProgram_, location, 1, values, vecType); \
} \
void ProgramState::UpdateUniform##suffix##Vector1(GLint location, GLsizei count, const type* value) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "Vector1 called, location=%d, count=%d", location, count); \
SetUniform<type>(currentProgram_, location, count, value, vecType); \
} \
void ProgramState::UpdateUniform##suffix##Vector2(GLint location, GLsizei count, const type* value) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "Vector2 called, location=%d, count=%d", location, count); \
SetUniform<type>(currentProgram_, location, count * 2, value, vecType); \
} \
void ProgramState::UpdateUniform##suffix##Vector3(GLint location, GLsizei count, const type* value) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "Vector3 called, location=%d, count=%d", location, count); \
SetUniform<type>(currentProgram_, location, count * 3, value, vecType); \
} \
void ProgramState::UpdateUniform##suffix##Vector4(GLint location, GLsizei count, const type* value) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniform" #suffix "Vector4 called, location=%d, count=%d", location, count); \
SetUniform<type>(currentProgram_, location, count * 4, value, vecType); \
}
IMPLEMENT_UNIFORM_FUNCTIONS(GLfloat, Float, GL_FLOAT_VEC4)
IMPLEMENT_UNIFORM_FUNCTIONS(GLint, Int, GL_INT_VEC4)
IMPLEMENT_UNIFORM_FUNCTIONS(GLuint, UInt, GL_UNSIGNED_INT_VEC4)
IMPLEMENT_UNIFORM_FUNCTIONS(GLboolean, Bool, GL_BOOL_VEC4)
#undef IMPLEMENT_UNIFORM_FUNCTIONS
#define IMPLEMENT_MATRIX_FUNCTIONS(suffix, matrixType) \
void ProgramState::UpdateUniformMatrix##suffix##Vector(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { \
MG_Util::Debug::LogD("MG_State: Program: UpdateUniformMatrix" #suffix "Vector called, location=%d, count=%d", location, count); \
SetUniformMatrix<GLfloat>(currentProgram_, location, count, transpose, value, matrixType); \
}
IMPLEMENT_MATRIX_FUNCTIONS(2x2, GL_FLOAT_MAT2)
IMPLEMENT_MATRIX_FUNCTIONS(3x3, GL_FLOAT_MAT3)
IMPLEMENT_MATRIX_FUNCTIONS(4x4, GL_FLOAT_MAT4)
IMPLEMENT_MATRIX_FUNCTIONS(2x3, GL_FLOAT_MAT2x3)
IMPLEMENT_MATRIX_FUNCTIONS(2x4, GL_FLOAT_MAT2x4)
IMPLEMENT_MATRIX_FUNCTIONS(3x2, GL_FLOAT_MAT3x2)
IMPLEMENT_MATRIX_FUNCTIONS(3x4, GL_FLOAT_MAT3x4)
IMPLEMENT_MATRIX_FUNCTIONS(4x2, GL_FLOAT_MAT4x2)
IMPLEMENT_MATRIX_FUNCTIONS(4x3, GL_FLOAT_MAT4x3)
#undef IMPLEMENT_MATRIX_FUNCTIONS
GLint ProgramState::QueryProgramUniformLocation(GLuint program, const GLchar* name) {
MG_Util::Debug::LogD("MG_State: Program: QueryProgramUniformLocation called, program=%u, name=%s", program, name);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: QueryProgramUniformLocation error: invalid program id %u", program);
return -1;
}
auto& prog = programs_[program];
auto it = prog.uniformLocations.find(name);
if (it != prog.uniformLocations.end()) {
GLint loc = it->second;
MG_Util::Debug::LogD("MG_State: Program: QueryProgramUniformLocation success: %s -> %d", name, loc);
return loc;
}
MG_Util::Debug::LogW("MG_State: Program: QueryProgramUniformLocation warning: name '%s' not found", name);
return -1;
}
bool ProgramState::SetProgramStatus(GLuint program, GLboolean status) {
MG_Util::Debug::LogD("MG_State: Program: SetProgramStatus called, program=%u, status=%d", program, status);
if (!ValidateProgram_(program)) {
MG_Util::Debug::LogE("MG_State: Program: SetProgramStatus error: invalid program id %u", program);
return false;
}
auto& prog = programs_[program];
if (prog.linked.getValue() != UncertainBool::Unknown) {
MG_Util::Debug::LogW("MG_State: Program: SetProgramStatus warning: link state already known");
return false;
}
prog.linked = (status == GL_TRUE) ? UncertainBool::True : UncertainBool::False;
prog.linkStatus = status;
MG_Util::Debug::LogD("MG_State: Program: SetProgramStatus success, status=%d", status);
return true;
}
GLuint ProgramState::GetCurrentProgram() const {
MG_Util::Debug::LogD("MG_State: Program: GetCurrentProgram called, returning %u", currentProgram_);
return currentProgram_;
}
ShaderObject ProgramState::GetShaderObject(GLuint shader) const {
MG_Util::Debug::LogD("MG_State: Program: GetShaderObject called, shader=%u", shader);
auto it = shaders_.find(shader);
if (it == shaders_.end()) {
MG_Util::Debug::LogW("MG_State: Program: GetShaderObject warning: invalid shader id %u", shader);
return ShaderObject();
}
MG_Util::Debug::LogD("MG_State: Program: GetShaderObject success, returning object");
return it->second;
}
ProgramObject ProgramState::GetProgramObject(GLuint program) const {
MG_Util::Debug::LogD("MG_State: Program: GetProgramObject called, program=%u", program);
auto it = programs_.find(program);
if (it == programs_.end()) {
MG_Util::Debug::LogW("MG_State: Program: GetProgramObject warning: invalid program id %u", program);
return ProgramObject();
}
MG_Util::Debug::LogD("MG_State: Program: GetProgramObject success, returning object");
return it->second;
}
bool ProgramState::ValidateProgram_(GLuint program) {
bool ok = programs_.count(program) && !programs_[program].markedForDeletion;
MG_Util::Debug::LogD("MG_State: Program: ValidateProgram_ called, program=%u, result=%d", program, ok);
return ok;
}
void ProgramState::CleanupShaders_() {
MG_Util::Debug::LogD("MG_State: Program: CleanupShaders_ start");
auto it = shaders_.begin();
while (it != shaders_.end()) {
if (it->second.markedForDeletion) {
bool inUse = false;
for (auto& [progId, program] : programs_) {
if (std::find(program.attachedShaders.begin(),
program.attachedShaders.end(), it->first) != program.attachedShaders.end()) {
inUse = true;
break;
}
}
if (!inUse) {
MG_Util::Debug::LogD("MG_State: Program: CleanupShaders_ deleting shader %u", it->first);
freeShaderIds_.insert(it->first);
it = shaders_.erase(it);
continue;
}
}
++it;
}
MG_Util::Debug::LogD("MG_State: Program: CleanupShaders_ end");
}
// UniformValue
template<typename T>
void UniformValue::setData(GLenum uniformType, GLsizei numElements, const T* values) {
MG_Util::Debug::LogD("MG_State: Program: UniformValue::setData called, type=0x%X, count=%d", uniformType, numElements);
//type = uniformType;
count = numElements;
floatData.clear();
intData.clear();
uintData.clear();
boolData.clear();
for (GLsizei i = 0; i < numElements; ++i) {
switch (uniformType) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
floatData.push_back(static_cast<GLfloat>(values[i]));
break;
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
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:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_BUFFER:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
intData.push_back(static_cast<GLint>(values[i]));
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
uintData.push_back(static_cast<GLuint>(values[i]));
break;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
boolData.push_back(values[i] ? GL_TRUE : GL_FALSE);
break;
default:
MG_Util::Debug::LogW("MG_State: Program: UniformValue::setData warning: unsupported type 0x%X", uniformType);
break;
}
}
MG_Util::Debug::LogD("MG_State: Program: UniformValue::setData end");
}