mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 22:58:30 +09:00
[Feat] (MG_State, MG_Impl, MG_Backend): let a bound program pipeline actually draw
The pipeline object bookkeeping landed already - names, stage slots, queries - but nothing consumed it. Every draw asked the context for the current program, got null because a pipeline is used with program zero, and drew nothing; glCreateShaderProgramv was still a stub returning zero, so direct_state_access.program_pipelines_functional could not even build its stage programs and reported InternalError on both backends. glCreateShaderProgramv is written as the exact call sequence the spec defines it to be, with one deviation that matters: the link goes straight to ProgramObject::Link(false) rather than through LinkProgram, because LinkProgram injects a default fragment shader into a program that has none - correct for a whole program, wrong for a separable vertex-stage one whose fragment stage comes from the pipeline. glDetachShader defers removal to the next link, so the program keeps the shader object it was built from while correctly no longer reporting it attached. GL_PROGRAM_SEPARABLE joins glProgramParameteri and glGetProgramiv. Everything downstream of a draw - both backends, the uniform plumbing, the draw validation - is written against one linked program, so rather than teach all of it about stages, the pipeline is flattened: GetProgramForDraw() composites the stage programs' shaders into a single hidden program object and caches it against a signature of each stage program's lifetime id and link generation, so it is rebuilt exactly when a stage or a stage's link changes. The composite carries no GL name - it must not answer glIsProgram, and it must not consume a name the application could be handed. Uniform entry points get their own resolver rather than sharing that one: glUniform* addresses the pipeline's active program, not the composited draw program. GL_CURRENT_PROGRAM still reads the program in use, which is zero here. Fixes program_pipelines_functional on both backends.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
static Bool ValidateCurrentProgramForExecution(const char* functionName) {
|
||||
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
|
||||
const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();
|
||||
if (!currentProgram) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -37,7 +37,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
static Bool ValidateCurrentProgramForCompute(const char* functionName) {
|
||||
if (!ValidateCurrentProgramForExecution(functionName)) return false;
|
||||
|
||||
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
|
||||
const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();
|
||||
if (currentProgram->GetShaderIndexByStage(ShaderStage::Compute) < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -173,7 +173,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// input primitive (GL 4.6 core 11.3.1); anything else is INVALID_OPERATION. GL_PATCHES
|
||||
// is the tessellation pipeline's input and reaches the geometry stage already
|
||||
// converted, so it is not constrained here.
|
||||
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
|
||||
const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();
|
||||
const GLenum gsInput = currentProgram ? currentProgram->GetGeometryInputType() : GL_NONE;
|
||||
if (gsInput != GL_NONE && mode != GL_PATCHES) {
|
||||
Bool compatible = false;
|
||||
@@ -707,7 +707,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Transform feedback is already active."));
|
||||
return;
|
||||
}
|
||||
const auto& program = MG_State::pGLContext->GetCurrentProgram();
|
||||
const auto& program = MG_State::pGLContext->GetProgramForDraw();
|
||||
if (!program || !program->GetLinkStatus() || program->GetTransformFeedbackVaryingCount() == 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
|
||||
@@ -320,7 +320,7 @@ DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceiv, GLuint program, GLenum prog
|
||||
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocation, program, programInterface, name)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, UseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UseProgramStages, pipeline, stages, program)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ActiveShaderProgram, GLuint pipeline, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ActiveShaderProgram, pipeline, program)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateShaderProgramv, GLenum type, GLsizei count, const GLchar* const* strings) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateShaderProgramv, type, count, strings)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLuint, CreateShaderProgramv, GLenum type, GLsizei count, const GLchar* const* strings) DECLARE_GL_FUNCTION_END(GLuint, CreateShaderProgramv, type, count, strings)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, BindProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DeleteProgramPipelines, GLsizei n, const GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteProgramPipelines, n, pipelines)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GenProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenProgramPipelines, n, pipelines)
|
||||
|
||||
@@ -701,6 +701,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
|
||||
*params = programObject->GetBinaryRetrievableHint() ? GL_TRUE : GL_FALSE;
|
||||
break;
|
||||
case GL_PROGRAM_SEPARABLE:
|
||||
*params = programObject->GetSeparable() ? GL_TRUE : GL_FALSE;
|
||||
break;
|
||||
|
||||
case GL_GEOMETRY_VERTICES_OUT:
|
||||
case GL_GEOMETRY_INPUT_TYPE:
|
||||
@@ -1094,7 +1097,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void Uniformv_State(GLint location, GLsizei count, T* value) {
|
||||
if (location == -1) return;
|
||||
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -1286,7 +1289,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// If transpose is GL_TRUE, we need to transpose the matrix data
|
||||
if (location == -1) return;
|
||||
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -1321,7 +1324,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// If transpose is GL_TRUE, we need to transpose the matrix data
|
||||
if (location == -1) return;
|
||||
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -1361,7 +1364,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// If transpose is GL_TRUE, we need to transpose the matrix data
|
||||
if (location == -1) return;
|
||||
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -1394,7 +1397,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void UniformMatrixNonSquarefv_State(const char* caller, GLint location, GLsizei count) {
|
||||
if (location == -1) return;
|
||||
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2055,7 +2058,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2081,7 +2084,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2107,7 +2110,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2133,7 +2136,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix2x3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2159,7 +2162,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix2x4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2185,7 +2188,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix3x2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2211,7 +2214,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix3x4dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2237,7 +2240,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix4x2dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2263,7 +2266,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
void UniformMatrix4x3dv(GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) {
|
||||
if (location == -1) return;
|
||||
auto& programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||
auto& programObject = MG_State::pGLContext->GetProgramForUniform();
|
||||
if (programObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -2746,7 +2749,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void ProgramParameteri(GLuint program, GLenum pname, GLint value) {
|
||||
auto& programObject = TryToGetProgramObject(program);
|
||||
if (!programObject) return;
|
||||
if (pname != GL_PROGRAM_BINARY_RETRIEVABLE_HINT) {
|
||||
if (pname != GL_PROGRAM_BINARY_RETRIEVABLE_HINT && pname != GL_PROGRAM_SEPARABLE) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "pname is not an accepted value."));
|
||||
@@ -2758,9 +2761,48 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "value must be GL_TRUE or GL_FALSE."));
|
||||
return;
|
||||
}
|
||||
if (pname == GL_PROGRAM_SEPARABLE) {
|
||||
programObject->SetSeparable(value == GL_TRUE);
|
||||
return;
|
||||
}
|
||||
programObject->SetBinaryRetrievableHint(value == GL_TRUE);
|
||||
}
|
||||
|
||||
// GL 4.6 core 7.3: glCreateShaderProgramv is defined as the exact sequence below, so it
|
||||
// is written as that sequence rather than as a private shortcut - every error it can
|
||||
// raise is one of theirs, raised at the point they would raise it.
|
||||
GLuint CreateShaderProgramv(GLenum type, GLsizei count, const GLchar* const* strings) {
|
||||
const GLuint shader = CreateShader_State(type);
|
||||
if (shader == 0) return 0;
|
||||
|
||||
ShaderSource_State(shader, count, strings, nullptr);
|
||||
CompileShader_State(shader);
|
||||
|
||||
const GLuint program = CreateProgram_State();
|
||||
if (program != 0) {
|
||||
const auto& shaderObject = MG_State::pGLContext->GetShaderObject(shader);
|
||||
const auto& programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||
// The program is separable whether or not the shader compiled: a failed
|
||||
// compile leaves an unlinked but otherwise well-formed separable program.
|
||||
if (programObject) programObject->SetSeparable(true);
|
||||
if (shaderObject && programObject && shaderObject->GetCompileStatus()) {
|
||||
AttachShader_State(program, shader);
|
||||
// Not LinkProgram_State: that injects a default fragment shader into a
|
||||
// program that has none, which is exactly wrong for a separable
|
||||
// vertex-stage program - the pipeline supplies the real one.
|
||||
programObject->Link(false);
|
||||
// glDetachShader defers the removal to the next link, so the program keeps
|
||||
// the shader object it was built from while no longer reporting it attached.
|
||||
DetachShader_State(program, shader);
|
||||
}
|
||||
if (shaderObject && programObject && !shaderObject->GetInfoLog().empty()) {
|
||||
programObject->AppendInfoLog(shaderObject->GetInfoLog());
|
||||
}
|
||||
}
|
||||
DeleteShader_State(shader);
|
||||
return program;
|
||||
}
|
||||
|
||||
void GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) {
|
||||
(void)binaryFormat;
|
||||
(void)binary;
|
||||
|
||||
@@ -174,6 +174,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void GetUniformdv(GLuint program, GLint location, GLdouble* params);
|
||||
void ValidateProgram(GLuint program);
|
||||
void ProgramParameteri(GLuint program, GLenum pname, GLint value);
|
||||
GLuint CreateShaderProgramv(GLenum type, GLsizei count, const GLchar* const* strings);
|
||||
void GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary);
|
||||
void ProgramBinary(GLuint program, GLenum binaryFormat, const void* binary, GLsizei length);
|
||||
void TransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode);
|
||||
|
||||
Reference in New Issue
Block a user