mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 22:58:30 +09:00
[Feat] (MG_State, MG_Impl): give program pipelines their object and their state
Every program pipeline entry point was an export stub, and the stub macro's `return (type)1` made glIsProgramPipeline answer GL_TRUE for anything - including the names glGenProgramPipelines had never written. All four direct_state_access.program_pipelines cases failed. ProgramPipelineObject holds what GL 4.6 core 7.4 says a pipeline is: a program reference per shader stage, the active program glProgramUniform* addresses, a validate status and an info log. Its validate status starts false, unlike ProgramObject's, because a pipeline that has never been validated must report GL_VALIDATE_STATUS as 0. The name rules follow the shape queries and transform feedbacks already use, and which the CTS checks first: glGenProgramPipelines only RESERVES a name and glIsProgramPipeline answers GL_FALSE for it; the object appears on first bind, or immediately from glCreateProgramPipelines. Map membership is object existence - a pipeline, unlike a transform feedback, has no stateful default object zero, so no everBound flag is needed. glGet(GL_PROGRAM_PIPELINE_BINDING) reports the real binding now instead of a hardcoded zero whose comment said the entry points were stubbed. This is the state half only. program_pipelines_functional needs mixed-stage rendering - a vertex-only and a fragment-only program drawn together - and stays failing; glCreateShaderProgramv is deliberately left stubbed until that lands, so nothing can half-work in between. Takes program_pipelines_creation, _defaults and _errors from failing to passing on both backends.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include "../Texture/GL_Texture.h"
|
||||
#include "../Drawing/GL_Drawing.h"
|
||||
#include "../Program/GL_Program.h"
|
||||
#include "../Program/GL_ProgramPipeline.h"
|
||||
#include "../RenderState/GL_RenderState.h"
|
||||
#include "../Framebuffer/GL_Framebuffer.h"
|
||||
#include "../VertexArray/GL_VertexArray.h"
|
||||
@@ -317,14 +318,14 @@ DECLARE_GL_FUNCTION_HEAD(GLuint, GetProgramResourceIndex, GLuint program, GLenum
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceName, program, programInterface, index, bufSize, length, name)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params)
|
||||
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_STUB_HEAD(void, UseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UseProgramStages, pipeline, stages, program)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ActiveShaderProgram, GLuint pipeline, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ActiveShaderProgram, pipeline, program)
|
||||
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_STUB_HEAD(void, BindProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteProgramPipelines, GLsizei n, const GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteProgramPipelines, n, pipelines)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenProgramPipelines, n, pipelines)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineiv, pipeline, pname, params)
|
||||
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)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END(GLboolean, IsProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramPipelineiv, pipeline, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1i, GLuint program, GLint location, GLint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1i, program, location, v0)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2i, program, location, v0, v1)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3i, program, location, v0, v1, v2)
|
||||
@@ -358,8 +359,8 @@ DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix2x4fv, GLuint program, GLint
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x2fv, program, location, count, transpose, value)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix3x4fv, program, location, count, transpose, value)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x3fv, program, location, count, transpose, value)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ValidateProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ValidateProgramPipeline, pipeline)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBooleani_v, target, index, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrier, barriers)
|
||||
@@ -1096,7 +1097,7 @@ DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLi
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexediv, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexediv, vaobj, index, pname, param)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexed64iv, GLuint vaobj, GLuint index, GLenum pname, GLint64* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexed64iv, vaobj, index, pname, param)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CreateSamplers, GLsizei n, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateSamplers, n, samplers)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateProgramPipelines, n, pipelines)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CreateProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateProgramPipelines, n, pipelines)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CreateQueries, GLenum target, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateQueries, target, n, ids)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjecti64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjecti64v, id, buffer, pname, offset)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjectiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjectiv, id, buffer, pname, offset)
|
||||
|
||||
@@ -1432,7 +1432,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
*params = 0; // program-binary entrypoints are stubbed
|
||||
return;
|
||||
case GL_PROGRAM_PIPELINE_BINDING:
|
||||
*params = 0; // program-pipeline entrypoints are stubbed
|
||||
*params = static_cast<GLint>(MG_State::pGLContext->GetBoundProgramPipelineName());
|
||||
return;
|
||||
case GL_PROGRAM_POINT_SIZE:
|
||||
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE;
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
// MobileGL - MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#include "GL_ProgramPipeline.h"
|
||||
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_State/GLState/ErrorState/ErrorInfo.h>
|
||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
namespace {
|
||||
void RecordPipelineError(ErrorCode code, const char* function, String message) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
code, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", function, Move(message)));
|
||||
}
|
||||
|
||||
// A pipeline name only names an object once it has been bound or created; querying a
|
||||
// reserved-but-unmaterialised name is INVALID_OPERATION (GL 4.6 core 7.4).
|
||||
const SharedPtr<MG_State::GLState::ProgramPipelineObject>* TryGetPipeline(GLuint pipeline,
|
||||
const char* function) {
|
||||
if (!MG_State::pGLContext->IsProgramPipelineObject(pipeline)) {
|
||||
RecordPipelineError(ErrorCode::InvalidOperation, function,
|
||||
std::format("Program pipeline {} does not exist.", pipeline));
|
||||
return nullptr;
|
||||
}
|
||||
return &MG_State::pGLContext->GetProgramPipelineObject(pipeline);
|
||||
}
|
||||
|
||||
Bool ValidatePipelineCount(GLsizei n, const char* function) {
|
||||
if (n < 0) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, function, "n must be non-negative.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// GL 4.6 core table 7.1 maps each stage bit onto a shader stage.
|
||||
Bool TryResolveStageBit(GLbitfield bit, ShaderStage& outStage) {
|
||||
switch (bit) {
|
||||
case GL_VERTEX_SHADER_BIT: outStage = ShaderStage::Vertex; return true;
|
||||
case GL_TESS_CONTROL_SHADER_BIT: outStage = ShaderStage::TessControl; return true;
|
||||
case GL_TESS_EVALUATION_SHADER_BIT: outStage = ShaderStage::TessEval; return true;
|
||||
case GL_GEOMETRY_SHADER_BIT: outStage = ShaderStage::Geometry; return true;
|
||||
case GL_FRAGMENT_SHADER_BIT: outStage = ShaderStage::Fragment; return true;
|
||||
case GL_COMPUTE_SHADER_BIT: outStage = ShaderStage::Compute; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr GLbitfield kAllStageBits = GL_VERTEX_SHADER_BIT | GL_TESS_CONTROL_SHADER_BIT |
|
||||
GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
|
||||
GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT;
|
||||
} // namespace
|
||||
|
||||
void GenProgramPipelines(GLsizei n, GLuint* pipelines) {
|
||||
if (!ValidatePipelineCount(n, __func__)) return;
|
||||
if (n == 0 || !pipelines) return;
|
||||
|
||||
static thread_local Vector<GLuint> names;
|
||||
MG_State::pGLContext->GenProgramPipelineNames(static_cast<Uint>(n), names);
|
||||
Memcpy(pipelines, names.data(), static_cast<SizeT>(n) * sizeof(GLuint));
|
||||
}
|
||||
|
||||
void CreateProgramPipelines(GLsizei n, GLuint* pipelines) {
|
||||
if (!ValidatePipelineCount(n, __func__)) return;
|
||||
if (n == 0 || !pipelines) return;
|
||||
|
||||
static thread_local Vector<GLuint> names;
|
||||
MG_State::pGLContext->GenProgramPipelineNames(static_cast<Uint>(n), names);
|
||||
for (GLsizei i = 0; i < n; ++i) {
|
||||
pipelines[i] = names[static_cast<SizeT>(i)];
|
||||
MG_State::pGLContext->CreateProgramPipelineObject(names[static_cast<SizeT>(i)]);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteProgramPipelines(GLsizei n, const GLuint* pipelines) {
|
||||
if (!ValidatePipelineCount(n, __func__)) return;
|
||||
if (!pipelines) return;
|
||||
|
||||
for (GLsizei i = 0; i < n; ++i) {
|
||||
// Deleting zero, an unknown name, or a name that was only reserved is silently ignored.
|
||||
MG_State::pGLContext->MarkProgramPipelineForDeletion(pipelines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void BindProgramPipeline(GLuint pipeline) {
|
||||
if (pipeline != 0 && !MG_State::pGLContext->ValidateProgramPipelineName(pipeline)) {
|
||||
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||
std::format("Program pipeline name {} is not valid.", pipeline));
|
||||
return;
|
||||
}
|
||||
MG_State::pGLContext->BindProgramPipelineObject(pipeline);
|
||||
}
|
||||
|
||||
GLboolean IsProgramPipeline(GLuint pipeline) {
|
||||
return MG_State::pGLContext->IsProgramPipelineObject(pipeline) ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
|
||||
void GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint* params) {
|
||||
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||
if (!pipelineObject || !params) return;
|
||||
|
||||
const auto stageProgramName = [&](ShaderStage stage) -> GLint {
|
||||
const auto& program = (*pipelineObject)->GetStageProgram(stage);
|
||||
return program ? static_cast<GLint>(program->GetExternalIndex()) : 0;
|
||||
};
|
||||
|
||||
switch (pname) {
|
||||
case GL_ACTIVE_PROGRAM: {
|
||||
const auto& active = (*pipelineObject)->GetActiveProgram();
|
||||
*params = active ? static_cast<GLint>(active->GetExternalIndex()) : 0;
|
||||
break;
|
||||
}
|
||||
case GL_VERTEX_SHADER: *params = stageProgramName(ShaderStage::Vertex); break;
|
||||
case GL_TESS_CONTROL_SHADER: *params = stageProgramName(ShaderStage::TessControl); break;
|
||||
case GL_TESS_EVALUATION_SHADER: *params = stageProgramName(ShaderStage::TessEval); break;
|
||||
case GL_GEOMETRY_SHADER: *params = stageProgramName(ShaderStage::Geometry); break;
|
||||
case GL_FRAGMENT_SHADER: *params = stageProgramName(ShaderStage::Fragment); break;
|
||||
case GL_COMPUTE_SHADER: *params = stageProgramName(ShaderStage::Compute); break;
|
||||
case GL_VALIDATE_STATUS: *params = (*pipelineObject)->GetValidateStatus() ? GL_TRUE : GL_FALSE; break;
|
||||
case GL_INFO_LOG_LENGTH: {
|
||||
// GL counts the null terminator, and reports 0 rather than 1 for an empty log.
|
||||
const auto& log = (*pipelineObject)->GetInfoLog();
|
||||
*params = log.empty() ? 0 : static_cast<GLint>(log.length()) + 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
RecordPipelineError(ErrorCode::InvalidEnum, __func__,
|
||||
std::format("pname {} is not a program pipeline parameter.",
|
||||
MG_Util::ConvertGLEnumToString(pname)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
|
||||
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||
if (!pipelineObject) return;
|
||||
if (bufSize < 0) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__, "bufSize must be non-negative.");
|
||||
return;
|
||||
}
|
||||
if (bufSize == 0 || !infoLog) {
|
||||
if (length) *length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& log = (*pipelineObject)->GetInfoLog();
|
||||
const auto copied = std::min<GLsizei>(bufSize - 1, static_cast<GLsizei>(log.length()));
|
||||
if (copied > 0) Memcpy(infoLog, log.data(), static_cast<SizeT>(copied));
|
||||
infoLog[copied] = '\0';
|
||||
if (length) *length = copied;
|
||||
}
|
||||
|
||||
void UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program) {
|
||||
if (stages != GL_ALL_SHADER_BITS && (stages & ~kAllStageBits) != 0) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__, "stages names a bit that is not a shader stage.");
|
||||
return;
|
||||
}
|
||||
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||
if (!pipelineObject) return;
|
||||
|
||||
SharedPtr<MG_State::GLState::ProgramObject> programObject;
|
||||
if (program != 0) {
|
||||
if (!MG_State::pGLContext->ValidateProgramName(program)) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||
std::format("{} is not the name of a program object.", program));
|
||||
return;
|
||||
}
|
||||
programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||
if (!programObject) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||
std::format("{} is not the name of a program object.", program));
|
||||
return;
|
||||
}
|
||||
if (!programObject->GetLinkStatus()) {
|
||||
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||
std::format("Program {} has not been linked successfully.", program));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const GLbitfield selected = stages == GL_ALL_SHADER_BITS ? kAllStageBits : stages;
|
||||
for (GLbitfield bit = 1; bit != 0 && bit <= kAllStageBits; bit <<= 1) {
|
||||
if ((selected & bit) == 0) continue;
|
||||
ShaderStage stage = ShaderStage::Unknown;
|
||||
if (!TryResolveStageBit(bit, stage)) continue;
|
||||
// program == 0 clears the stage, which is what a null program reference means here.
|
||||
(*pipelineObject)->SetStageProgram(stage, programObject);
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveShaderProgram(GLuint pipeline, GLuint program) {
|
||||
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||
if (!pipelineObject) return;
|
||||
|
||||
if (program == 0) {
|
||||
(*pipelineObject)->SetActiveProgram(nullptr);
|
||||
return;
|
||||
}
|
||||
if (!MG_State::pGLContext->ValidateProgramName(program)) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||
std::format("{} is not the name of a program object.", program));
|
||||
return;
|
||||
}
|
||||
auto programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||
if (!programObject) {
|
||||
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||
std::format("{} is not the name of a program object.", program));
|
||||
return;
|
||||
}
|
||||
if (!programObject->GetLinkStatus()) {
|
||||
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||
std::format("Program {} has not been linked successfully.", program));
|
||||
return;
|
||||
}
|
||||
(*pipelineObject)->SetActiveProgram(programObject);
|
||||
}
|
||||
|
||||
void ValidateProgramPipeline(GLuint pipeline) {
|
||||
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||
if (!pipelineObject) return;
|
||||
// Nothing here can fail today: MobileGL links each stage program on its own, so there is no
|
||||
// cross-stage interface to re-check at validation time. The log stays empty, which GL allows.
|
||||
(*pipelineObject)->SetValidateStatus(true);
|
||||
}
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
@@ -0,0 +1,23 @@
|
||||
// MobileGL - MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
void GenProgramPipelines(GLsizei n, GLuint* pipelines);
|
||||
void CreateProgramPipelines(GLsizei n, GLuint* pipelines);
|
||||
void DeleteProgramPipelines(GLsizei n, const GLuint* pipelines);
|
||||
void BindProgramPipeline(GLuint pipeline);
|
||||
GLboolean IsProgramPipeline(GLuint pipeline);
|
||||
void GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint* params);
|
||||
void GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
|
||||
void UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program);
|
||||
void ActiveShaderProgram(GLuint pipeline, GLuint program);
|
||||
void ValidateProgramPipeline(GLuint pipeline);
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
Reference in New Issue
Block a user