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

258 lines
7.9 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"
GLenum ProgramState::CreateShader(GLenum type, GLuint* shader) {
if (!shader) return GL_INVALID_VALUE;
if (type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER)
return GL_INVALID_ENUM;
GLuint id = freeShaderIds_.empty() ? ++lastShaderId_ : *freeShaderIds_.begin();
if (!freeShaderIds_.empty()) freeShaderIds_.erase(freeShaderIds_.begin());
ShaderObject obj;
obj.type = type;
obj.compiled = UncertainBool::False;
obj.markedForDeletion = false;
shaders_[id] = obj;
*shader = id;
return GL_NO_ERROR;
}
GLenum ProgramState::CreateProgram(GLuint* program) {
if (!program) return GL_INVALID_VALUE;
GLuint id = freeProgramIds_.empty() ? ++lastProgramId_ : *freeProgramIds_.begin();
if (!freeProgramIds_.empty()) freeProgramIds_.erase(freeProgramIds_.begin());
ProgramObject obj;
obj.linked = UncertainBool::False;
obj.markedForDeletion = false;
programs_[id] = obj;
*program = id;
return GL_NO_ERROR;
}
GLenum ProgramState::DeleteShader(GLuint shader) {
if (!ValidateShader_(shader)) return GL_INVALID_VALUE;
if (shaders_[shader].markedForDeletion) return GL_NO_ERROR;
bool inUse = false;
for (auto& [progId, program] : programs_) {
auto it = std::find(program.attachedShaders.begin(),
program.attachedShaders.end(), shader);
if (it != program.attachedShaders.end()) {
inUse = true;
break;
}
}
if (inUse) {
shaders_[shader].markedForDeletion = true;
} else {
shaders_.erase(shader);
freeShaderIds_.insert(shader);
}
return GL_NO_ERROR;
}
GLenum ProgramState::DeleteProgram(GLuint program) {
if (!ValidateProgram_(program)) return GL_INVALID_VALUE;
programs_.erase(program);
freeProgramIds_.insert(program);
if (currentProgram_ == program)
currentProgram_ = 0;
return GL_NO_ERROR;
}
GLenum ProgramState::LinkShaderToProgram(GLuint program, GLuint shader) {
if (!ValidateProgram_(program) || !ValidateShader_(shader))
return GL_INVALID_VALUE;
auto& prog = programs_[program];
if (std::find(prog.attachedShaders.begin(),
prog.attachedShaders.end(), shader) != prog.attachedShaders.end())
return GL_INVALID_OPERATION;
prog.attachedShaders.push_back(shader);
return GL_NO_ERROR;
}
GLenum ProgramState::UploadShaderSource(GLuint shader, GLsizei count,
const GLchar** string, const GLint* length) {
if (!ValidateShader_(shader)) return GL_INVALID_VALUE;
if (count < 0) return GL_INVALID_VALUE;
std::string source;
for (GLsizei i = 0; i < count; ++i) {
const GLchar* str = string[i];
GLint len = (length && length[i] >= 0) ? length[i] : strlen(str);
source.append(str, len);
}
shaders_[shader].source = std::move(source);
return GL_NO_ERROR;
}
GLenum ProgramState::BuildShaderStage(GLuint shader) {
if (!ValidateShader_(shader)) return GL_INVALID_VALUE;
auto& obj = shaders_[shader];
// TODO: Add glslang to convert it to SPIR-V first, and then check it in the actual backend.
obj.compiled = {UncertainBool::Unknown, UncertainBool::True};
obj.compileStatus = GL_TRUE;
obj.infoLog.clear();
return GL_NO_ERROR;
}
GLenum ProgramState::FinalizeProgramPipeline(GLuint program) {
if (!ValidateProgram_(program)) return GL_INVALID_VALUE;
auto& prog = programs_[program];
for (GLuint shader : prog.attachedShaders) {
if (!shaders_[shader].compiled.toBool())
return GL_INVALID_OPERATION;
}
// TODO: Check the actual program linking status.
// We might consider using a program emulator (or real backend checking?) or something better in the future.
prog.linked = {UncertainBool::Unknown, UncertainBool::True};
prog.linkStatus = GL_TRUE;
prog.infoLog.clear();
for (auto& [name, index] : prog.attribBindings) {
prog.attribLocations[name] = index;
}
CleanupShaders_();
return GL_NO_ERROR;
}
GLenum ProgramState::ActivateRenderProgram(GLuint program) {
if (program != 0 && !ValidateProgram_(program))
return GL_INVALID_VALUE;
currentProgram_ = program;
return GL_NO_ERROR;
}
GLenum ProgramState::DefineProgramAttributeBinding(GLuint program, GLuint index, const GLchar* name) {
if (!ValidateProgram_(program)) return GL_INVALID_VALUE;
if (index >= GL_MAX_VERTEX_ATTRIBS) return GL_INVALID_VALUE;
programs_[program].attribBindings[name] = index;
return GL_NO_ERROR;
}
GLint ProgramState::QueryProgramAttributeBinding(GLuint program, const GLchar* name) {
if (!ValidateProgram_(program)) return -1;
auto& prog = programs_[program];
if (prog.attribLocations.count(name))
return prog.attribLocations[name];
return -1;
}
GLint ProgramState::QueryProgramUniformLocation(GLuint program, const GLchar* name) {
if (!ValidateProgram_(program)) return -1;
auto& prog = programs_[program];
if (prog.uniformLocations.count(name))
return prog.uniformLocations[name];
return -1;
}
GLenum ProgramState::QueryProgramStateIntVector(GLuint program, GLenum pname, GLint* params) {
if (!ValidateProgram_(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: return GL_INVALID_ENUM;
}
return GL_NO_ERROR;
}
GLenum ProgramState::QueryShaderStateIntVector(GLuint shader, GLenum pname, GLint* params) {
if (!ValidateShader_(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: return GL_INVALID_ENUM;
}
return GL_NO_ERROR;
}
bool ProgramState::SetProgramStatus(GLuint program, GLboolean status) {
if (!ValidateProgram_(program)) return false;
auto& prog = programs_[program];
if (prog.linked && status != GL_TRUE) {
return false;
}
if (status == GL_TRUE)
prog.linked = true;
else
prog.linked = false;
prog.linkStatus = status;
return true;
}
bool ProgramState::SetShaderStatus(GLuint shader, GLboolean status) {
if (!ValidateShader_(shader)) return false;
auto& obj = shaders_[shader];
if (status == GL_TRUE)
obj.compiled = true;
else
obj.compiled = false;
obj.compileStatus = status;
return true;
}
bool ProgramState::ValidateShader_(GLuint shader) {
return shaders_.count(shader) && !shaders_[shader].markedForDeletion;
}
bool ProgramState::ValidateProgram_(GLuint program) {
return programs_.count(program) && !programs_[program].markedForDeletion;
}
void ProgramState::CleanupShaders_() {
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) {
freeShaderIds_.insert(it->first);
it = shaders_.erase(it);
} else {
++it;
}
} else {
++it;
}
}
}
GLuint ProgramState::GetCurrentProgram() const {
return currentProgram_;
}