mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +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:
@@ -802,6 +802,53 @@ namespace MobileGL::MG_State {
|
||||
m_transformFeedbackObjects[id] = {};
|
||||
}
|
||||
}
|
||||
// Program pipeline
|
||||
void GLContext::GenProgramPipelineNames(Uint number, Vector<Uint>& pipelines) {
|
||||
pipelines.resize(number);
|
||||
// Names only: glIsProgramPipeline must answer GL_FALSE until one is bound or created.
|
||||
m_programPipelineNames.Generate(number, pipelines.data());
|
||||
}
|
||||
|
||||
void GLContext::CreateProgramPipelineObject(Uint index) {
|
||||
m_programPipelines[index] = MakeShared<ProgramPipelineObject>(index);
|
||||
}
|
||||
|
||||
Bool GLContext::ValidateProgramPipelineName(Uint index) const {
|
||||
return index == 0 || m_programPipelineNames.IsValid(index);
|
||||
}
|
||||
|
||||
Bool GLContext::IsProgramPipelineObject(Uint index) const {
|
||||
if (index == 0 || !m_programPipelineNames.IsValid(index)) return false;
|
||||
return m_programPipelines.find(index) != m_programPipelines.end();
|
||||
}
|
||||
|
||||
void GLContext::BindProgramPipelineObject(Uint index) {
|
||||
if (index != 0 && m_programPipelines.find(index) == m_programPipelines.end()) {
|
||||
// First bind is what turns a reserved name into an object.
|
||||
m_programPipelines[index] = MakeShared<ProgramPipelineObject>(index);
|
||||
}
|
||||
m_boundProgramPipeline = index;
|
||||
}
|
||||
|
||||
void GLContext::MarkProgramPipelineForDeletion(Uint index) {
|
||||
if (index == 0 || !m_programPipelineNames.IsValid(index)) return;
|
||||
if (index == m_boundProgramPipeline) {
|
||||
m_boundProgramPipeline = 0;
|
||||
}
|
||||
m_programPipelines.erase(index);
|
||||
m_programPipelineNames.Delete(index);
|
||||
}
|
||||
|
||||
const SharedPtr<ProgramPipelineObject>& GLContext::GetProgramPipelineObject(Uint index) const {
|
||||
static const SharedPtr<ProgramPipelineObject> kNone;
|
||||
const auto it = m_programPipelines.find(index);
|
||||
return it == m_programPipelines.end() ? kNone : it->second;
|
||||
}
|
||||
|
||||
const SharedPtr<ProgramPipelineObject>& GLContext::GetBoundProgramPipeline() const {
|
||||
return GetProgramPipelineObject(m_boundProgramPipeline);
|
||||
}
|
||||
|
||||
|
||||
Bool GLContext::ValidateTransformFeedbackName(Uint index) const {
|
||||
return index == 0 || m_transformFeedbackNames.IsValid(index);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "MG_State/GLState/RenderbufferState/RenderbufferState.h"
|
||||
#include "RenderState/RenderState.h"
|
||||
#include "ProgramState/ProgramState.h"
|
||||
#include "ProgramState/ProgramPipelineObject.h"
|
||||
#include "SamplerState/SamplerState.h"
|
||||
#include "TextureState/TextureState.h"
|
||||
#include "FramebufferState/FramebufferState.h"
|
||||
@@ -137,6 +138,19 @@ namespace MobileGL {
|
||||
void UseProgram(Uint program);
|
||||
const SharedPtr<ProgramObject>& GetCurrentProgram();
|
||||
|
||||
// Program pipeline (GL_ARB_separate_shader_objects, GL 4.6 core 7.4). Like queries
|
||||
// and transform feedbacks, glGenProgramPipelines only RESERVES a name - the object
|
||||
// appears on first bind - while glCreateProgramPipelines makes it immediately.
|
||||
void GenProgramPipelineNames(Uint number, Vector<Uint>& pipelines);
|
||||
void CreateProgramPipelineObject(Uint index);
|
||||
Bool ValidateProgramPipelineName(Uint index) const;
|
||||
Bool IsProgramPipelineObject(Uint index) const;
|
||||
void BindProgramPipelineObject(Uint index);
|
||||
void MarkProgramPipelineForDeletion(Uint index);
|
||||
const SharedPtr<ProgramPipelineObject>& GetProgramPipelineObject(Uint index) const;
|
||||
Uint GetBoundProgramPipelineName() const { return m_boundProgramPipeline; }
|
||||
const SharedPtr<ProgramPipelineObject>& GetBoundProgramPipeline() const;
|
||||
|
||||
// RenderState
|
||||
Uint GetRenderStateParametersVersion() const;
|
||||
const RenderStateParameters& GetRenderStateParameters() const;
|
||||
@@ -390,6 +404,11 @@ namespace MobileGL {
|
||||
UnorderedMap<Uint, TransformFeedbackObjectState> m_transformFeedbackObjects;
|
||||
IndexGenerator<Uint> m_transformFeedbackNames;
|
||||
Uint m_boundTransformFeedback = 0;
|
||||
// Map membership IS object existence here: a pipeline has no stateful default
|
||||
// object 0, so no everBound flag is needed.
|
||||
UnorderedMap<Uint, SharedPtr<ProgramPipelineObject>> m_programPipelines;
|
||||
IndexGenerator<Uint> m_programPipelineNames;
|
||||
Uint m_boundProgramPipeline = 0;
|
||||
TextureState m_textureState;
|
||||
ProgramState m_programState;
|
||||
RenderState m_renderState;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// MobileGL - MobileGL/MG_State/GLState/ProgramState/ProgramPipelineObject.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>
|
||||
#include "ProgramObject.h"
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_State {
|
||||
namespace GLState {
|
||||
// A GL_ARB_separate_shader_objects program pipeline (GL 4.6 core 7.4): a set of
|
||||
// per-stage program references plus the program glProgramUniform* addresses when no
|
||||
// program object is in use.
|
||||
class ProgramPipelineObject {
|
||||
public:
|
||||
explicit ProgramPipelineObject(Uint externalIndex) : m_externalIndex(externalIndex) {}
|
||||
|
||||
const SharedPtr<ProgramObject>& GetStageProgram(ShaderStage stage) const {
|
||||
return m_stagePrograms[static_cast<SizeT>(stage)];
|
||||
}
|
||||
void SetStageProgram(ShaderStage stage, const SharedPtr<ProgramObject>& program) {
|
||||
m_stagePrograms[static_cast<SizeT>(stage)] = program;
|
||||
}
|
||||
|
||||
const SharedPtr<ProgramObject>& GetActiveProgram() const { return m_activeProgram; }
|
||||
void SetActiveProgram(const SharedPtr<ProgramObject>& program) { m_activeProgram = program; }
|
||||
|
||||
// Distinct from ProgramObject's, which starts true: a pipeline that has never been
|
||||
// validated must report GL_VALIDATE_STATUS as 0 (GL 4.6 core table 23.31).
|
||||
Bool GetValidateStatus() const { return m_validateStatus; }
|
||||
void SetValidateStatus(Bool value) { m_validateStatus = value; }
|
||||
|
||||
const String& GetInfoLog() const { return m_infoLog; }
|
||||
void SetInfoLog(String log) { m_infoLog = Move(log); }
|
||||
|
||||
Uint GetExternalIndex() const { return m_externalIndex; }
|
||||
|
||||
private:
|
||||
Array<SharedPtr<ProgramObject>, static_cast<SizeT>(ShaderStage::ShaderStageCount)> m_stagePrograms{};
|
||||
SharedPtr<ProgramObject> m_activeProgram;
|
||||
String m_infoLog;
|
||||
const Uint m_externalIndex = 0;
|
||||
Bool m_validateStatus = false;
|
||||
};
|
||||
} // namespace GLState
|
||||
} // namespace MG_State
|
||||
} // namespace MobileGL
|
||||
Reference in New Issue
Block a user