mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (MG_State, MG_Impl, DirectGLES): transform feedback objects, pause/resume and the special capture names
GL 4.0 folds ARB_transform_feedback2 and _3 into core, and neither existed: glGenTransformFeedbacks, glBindTransformFeedback, glDeleteTransformFeedbacks, glIsTransformFeedback, glPause/ResumeTransformFeedback, the whole glDrawTransformFeedback family and glBegin/EndQueryIndexed were all stubs, and gl_NextBuffer / gl_SkipComponents1..4 failed the link as "not an output of the vertex stage". Seven KHR-GL40.transform_feedback* cases failed on it, three of them by leaving a capture open at deinit and taking the process down. Objects. The capture state and the indexed GL_TRANSFORM_FEEDBACK_BUFFER bindings are object state, but the context keeps one live copy of both, which is what every existing reader - each backend's per-draw sync, the drawing and getter paths - is written against. Rather than teach all of them about objects, a bind saves the live copy into the outgoing object and restores the incoming one's. Object 0 is the default object and needs no seeding; operator[] materialises the rest on first touch. Pause. A paused span captures nothing, and three rules key off that: a draw is exempt from the capture primitive-mode match, it feeds PRIMITIVES_GENERATED but not TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, and glUseProgram is allowed again (that last one was already refused for an active capture, correctly for GL 3.3, which has no pause). glDrawTransformFeedback replays the vertices the object captured in its last completed span, recorded at End. "Has a completed span" is tracked separately from that count, because a completed empty span draws nothing while an object that never ended one is INVALID_OPERATION. Drawing from the object whose capture is currently open is deliberately allowed - feeding a result straight into the next span is the point of KHR-GL40.transform_feedback.draw_xfb_feedbackk_test. DirectGLES gets a real driver object per frontend object. That is the only reason the default one would not do: several objects can be paused at once, and a paused span lives inside the driver's object. The deferred driver-side Begin (still needed - ES wants the program current and the buffers bound) now also has to be held back while the span is paused, or a pause taken before the first draw would open the span on that draw and subject it to the primitive-mode rule it is exempt from. Special names. gl_NextBuffer and gl_SkipComponents<n> are consumed during varying resolution and never become varyings of their own, so they only move where the following ones land - and stay out of the name list the backend declares on its own driver. ES cannot express the resulting layout at all: it packs every captured varying into one gap-free record. So when the layout has holes or spans several buffers, DirectGLES captures into a scratch buffer bound in place of the application's, and End distributes the records to the offsets GL asked for. Only the bytes a varying occupies are written, which is exactly what makes the holes keep the contents the application left there - the property KHR-GL40.transform_feedback3.skip_components checks. glBegin/EndQueryIndexed and glGetQueryIndexediv differ from the plain forms only in the vertex stream they address, so they validate the index and forward. GL_MAX_VERTEX_STREAMS stays at 1: multi-stream capture needs ARB_gpu_shader5 stream qualifiers that no ES driver implements, and the CTS cases that need more than one stream check the limit and skip. KHR-GL40.transform_feedback, transform_feedback2 and transform_feedback3: 38/38.
This commit is contained in:
@@ -211,9 +211,12 @@ namespace MobileGL {
|
||||
void SetScissorBox(IntVec4 box); // x, y, width, height
|
||||
const IntVec4& GetScissorBox() const; // x, y, width, height
|
||||
|
||||
// Transform feedback (GL 3.0 core Begin/End; no feedback objects yet)
|
||||
// Transform feedback. The fields below are the state of the transform
|
||||
// feedback object currently bound to GL_TRANSFORM_FEEDBACK; see the object
|
||||
// block further down for how a bind swaps them.
|
||||
void BeginTransformFeedback(GLenum primitiveMode, const SharedPtr<ProgramObject>& program) {
|
||||
m_transformFeedbackActive = true;
|
||||
m_transformFeedbackPaused = false;
|
||||
m_transformFeedbackPrimitiveMode = primitiveMode;
|
||||
m_transformFeedbackProgram = program;
|
||||
++m_transformFeedbackGeneration;
|
||||
@@ -222,9 +225,16 @@ namespace MobileGL {
|
||||
}
|
||||
void EndTransformFeedback() {
|
||||
m_transformFeedbackActive = false;
|
||||
m_transformFeedbackPaused = false;
|
||||
m_transformFeedbackProgram.reset();
|
||||
// What glDrawTransformFeedback on this object replays from now on.
|
||||
auto& object = m_transformFeedbackObjects[m_boundTransformFeedback];
|
||||
object.recordedVertices = m_transformFeedbackCapturedVertices;
|
||||
object.hasCompletedSpan = true;
|
||||
}
|
||||
Bool IsTransformFeedbackActive() const { return m_transformFeedbackActive; }
|
||||
Bool IsTransformFeedbackPaused() const { return m_transformFeedbackPaused; }
|
||||
void SetTransformFeedbackPaused(Bool paused) { m_transformFeedbackPaused = paused; }
|
||||
GLenum GetTransformFeedbackPrimitiveMode() const { return m_transformFeedbackPrimitiveMode; }
|
||||
const SharedPtr<ProgramObject>& GetTransformFeedbackProgram() const {
|
||||
return m_transformFeedbackProgram;
|
||||
@@ -252,6 +262,29 @@ namespace MobileGL {
|
||||
}
|
||||
Uint64 GetTransformFeedbackInputPrimitives() const { return m_transformFeedbackInputPrimitives; }
|
||||
|
||||
// Transform feedback objects (ARB_transform_feedback2 / GL 4.0 core).
|
||||
// The capture state above and the indexed GL_TRANSFORM_FEEDBACK_BUFFER
|
||||
// binding points are object state, but the context keeps exactly one live
|
||||
// copy of both so that every existing reader - the backends' per-draw sync,
|
||||
// the drawing and getter paths - needs no notion of which object owns them.
|
||||
// A bind therefore saves the live copy into the outgoing object and restores
|
||||
// the incoming one's. Object 0 is the default object and always exists.
|
||||
static constexpr Uint MAX_TRANSFORM_FEEDBACK_BUFFERS = 4;
|
||||
void GenTransformFeedbackNames(Uint number, Vector<Uint>& ids);
|
||||
// A name glGenTransformFeedbacks handed out and glDeleteTransformFeedbacks
|
||||
// has not taken back. Name 0 is always valid.
|
||||
Bool ValidateTransformFeedbackName(Uint index) const;
|
||||
void BindTransformFeedbackObject(Uint index);
|
||||
void MarkTransformFeedbackObjectForDeletion(Uint index);
|
||||
Uint GetBoundTransformFeedbackName() const { return m_boundTransformFeedback; }
|
||||
// Vertices the object captured in its last completed span; the vertex count
|
||||
// glDrawTransformFeedback replays.
|
||||
Uint64 GetTransformFeedbackRecordedVertices(Uint index) const;
|
||||
// Whether the object has ever completed a capture span. glDrawTransformFeedback
|
||||
// on an object that has not is INVALID_OPERATION, which a zero vertex count
|
||||
// cannot express: an empty completed span is legal and draws nothing.
|
||||
Bool HasTransformFeedbackCompletedSpan(Uint index) const;
|
||||
|
||||
// Framebuffer
|
||||
void GenFramebufferNames(Uint number, Vector<Uint>& framebuffers);
|
||||
const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
|
||||
@@ -285,12 +318,40 @@ namespace MobileGL {
|
||||
VertexArrayState m_vertexArrayState;
|
||||
Array<CurrentVertexAttributeValue, VertexArrayObject::MAX_VERTEX_ATTRIBS> m_currentVertexAttributes{};
|
||||
Bool m_transformFeedbackActive = false;
|
||||
Bool m_transformFeedbackPaused = false;
|
||||
GLenum m_transformFeedbackPrimitiveMode = GL_POINTS;
|
||||
SharedPtr<ProgramObject> m_transformFeedbackProgram;
|
||||
Uint64 m_transformFeedbackGeneration = 0;
|
||||
// Not object state: the transform feedback queries snapshot it at BeginQuery
|
||||
// and take the delta at EndQuery, which spans whatever objects were used.
|
||||
Uint64 m_transformFeedbackPrimitiveCounter = 0;
|
||||
Uint64 m_transformFeedbackCapturedVertices = 0;
|
||||
Uint64 m_transformFeedbackInputPrimitives = 0;
|
||||
|
||||
// Everything a transform feedback object owns while it is NOT the bound one.
|
||||
struct TransformFeedbackObjectState {
|
||||
struct SavedBufferBinding {
|
||||
SharedPtr<BufferObject> buffer;
|
||||
Range1D range;
|
||||
Bool hasExplicitRange = false;
|
||||
};
|
||||
Array<SavedBufferBinding, MAX_TRANSFORM_FEEDBACK_BUFFERS> bindings;
|
||||
Bool active = false;
|
||||
Bool paused = false;
|
||||
GLenum primitiveMode = GL_POINTS;
|
||||
SharedPtr<ProgramObject> program;
|
||||
Uint64 capturedVertices = 0;
|
||||
Uint64 inputPrimitives = 0;
|
||||
Uint64 recordedVertices = 0;
|
||||
Bool hasCompletedSpan = false;
|
||||
};
|
||||
void SaveBoundTransformFeedbackState();
|
||||
void RestoreBoundTransformFeedbackState();
|
||||
// operator[] materialises an entry with the default state on first touch, so
|
||||
// the default object (name 0) needs no seeding here.
|
||||
UnorderedMap<Uint, TransformFeedbackObjectState> m_transformFeedbackObjects;
|
||||
IndexGenerator<Uint> m_transformFeedbackNames;
|
||||
Uint m_boundTransformFeedback = 0;
|
||||
TextureState m_textureState;
|
||||
ProgramState m_programState;
|
||||
RenderState m_renderState;
|
||||
|
||||
Reference in New Issue
Block a user