[Feat] (MG_State, MG_Impl): transform feedback state, validation and reflection

First stage of GL 3.0 transform feedback: glTransformFeedbackVaryings /
glGetTransformFeedbackVarying / glBeginTransformFeedback /
glEndTransformFeedback were unimplemented stubs. This adds

- per-program capture state: requested varyings apply on the next link and
  resolve against the last vertex-processing stage's linker objects (with
  gl_Position/gl_PointSize handled as builtins), failing the link on
  unknown or duplicate names or exceeded interleaved/separate limits, with
  offsets and strides computed per GL rules;
- context Begin/End state with the GL 3.3 error semantics: invalid
  primitive modes, redundant Begin/End, missing program or capture-buffer
  bindings, primitive-mode compatibility at draw time, and the
  while-active prohibitions on rebinding capture buffers, switching
  programs, and relinking the captured program;
- GetProgramiv TRANSFORM_FEEDBACK_* queries and a 4-slot bound on indexed
  GL_TRANSFORM_FEEDBACK_BUFFER binding points.

KHR-GL33.transform_feedback api_errors/linking_errors/get_xfb_varying now
pass; GPU-side capture is the remaining stage.
This commit is contained in:
BZLZHH
2026-07-31 17:08:37 -04:00
parent a389477f78
commit 48dd1c5956
10 changed files with 422 additions and 7 deletions
@@ -66,6 +66,34 @@ namespace MobileGL::MG_Impl::GLImpl {
return false;
}
// While transform feedback is active the draw's primitive type must match
// the feedback primitive mode (GL 3.3 core 13.2.2).
if (MG_State::pGLContext->IsTransformFeedbackActive()) {
const GLenum feedbackMode = MG_State::pGLContext->GetTransformFeedbackPrimitiveMode();
Bool compatible = false;
switch (feedbackMode) {
case GL_POINTS:
compatible = mode == GL_POINTS;
break;
case GL_LINES:
compatible = mode == GL_LINES || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP;
break;
case GL_TRIANGLES:
compatible = mode == GL_TRIANGLES || mode == GL_TRIANGLE_STRIP || mode == GL_TRIANGLE_FAN;
break;
default:
break;
}
if (!compatible) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", functionName,
"Primitive mode is incompatible with the active transform feedback primitive mode."));
return false;
}
}
return true;
}
@@ -438,4 +466,54 @@ namespace MobileGL::MG_Impl::GLImpl {
DrawElements_Backend(mode, count, type, indices);
}
void BeginTransformFeedback(GLenum primitiveMode) {
if (primitiveMode != GL_POINTS && primitiveMode != GL_LINES && primitiveMode != GL_TRIANGLES) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"primitiveMode must be GL_POINTS, GL_LINES or GL_TRIANGLES."));
return;
}
if (MG_State::pGLContext->IsTransformFeedbackActive()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Transform feedback is already active."));
return;
}
const auto& program = MG_State::pGLContext->GetCurrentProgram();
if (!program || !program->GetLinkStatus() || program->GetTransformFeedbackVaryingCount() == 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
"No program with transform feedback varyings is active."));
return;
}
// Every capture buffer slot the program's mode uses must have a buffer bound.
const SizeT usedBufferCount = program->GetTransformFeedbackBufferCount();
for (SizeT i = 0; i < usedBufferCount; ++i) {
const auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::TransformFeedback,
static_cast<Uint>(i));
if (point.GetBoundObject() == nullptr) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
"Transform feedback buffer binding point " + std::to_string(i) + " has no buffer bound."));
return;
}
}
MG_State::pGLContext->BeginTransformFeedback(primitiveMode, program);
}
void EndTransformFeedback(void) {
if (!MG_State::pGLContext->IsTransformFeedbackActive()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Transform feedback is not active."));
return;
}
MG_State::pGLContext->EndTransformFeedback();
}
} // namespace MobileGL::MG_Impl::GLImpl