mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 21:28:32 +09:00
[Fix] (Pipe): carry the class a vertex-attribute default was written through, and stop the applier's lossy write from being observable
- set_vertex_attrib_defaults hard-coded MGPAttribValue::ValueClass to 0 for every attribute and always sent the FLOAT view's four words. A CurrentVertexAttributeValue is one value in three views and GLContext converts numerically between them, so those bytes cannot reproduce the frontend value: glVertexAttrib4f(loc, 1.5f, ..) leaves 1 in intValue and 0x3FC00000 in floatValue, and every glVertexAttribI4i/ui default was wrong too - GLContext now records which view each glVertexAttrib* write filled directly (GetCurrentVertexAttributeClass, push-only) and the payload carries that class and THAT class's own words. It is kept beside the value rather than inside it because CurrentVertexAttributeValue is mirrored into PipeInputs and compared there by a memcmp whose size assertion lives in a file this package does not own - MGPipeFillAttribValue is the flattening, in one named place, so TrackerAttribPayload can pin it: the old defect turns three of its four cases red - the applier (package A's) still memcpys the four words into all three views regardless of ValueClass, so the emitter now CHECKS: it compares the mirror the applier wrote against the frontend's value and, when they differ, copies the field itself and says so once. That closes the window the old code left wrong - a glVertexAttrib* write followed by a verb whose class does not read the field, where the residual fill does not run for it - and it stops repairing by itself the day A's applier honours the class - MGPipeVertexAttribDefaultRepairCount() makes that repair observable to a test without reading storage the fill table forbids that verb to read - the emission gate now goes through MGPipeSubsystemForDirty, the one bit-to-subsystem map, instead of a second copy of it written out by hand at the validate point; five static_asserts tie that map to the field-emitter map it has to agree with - the staging mirror is advanced only by the branch that sent dynamic bytes, with the invariant it used to rely on (BumpVersions moves both counters, RenderState.h) asserted here rather than assumed of another package's file - TrackerShippedEmitter drives MGPipeValidateForVerb itself and reads the real singletons back, so the blend-toggle and viewport shapes are pinned on the shipped emitter and not only on the unit tests' local re-implementation; the fixtures reset the applier, the cache and the tracker together, which is the only consistent state of the three - comments: the derivation probe is a one-field sample, the residual block's trip wire is half a tautology until package A's c1 lands, and the widened counter cannot see a change of exactly 65536 - all three recorded where the code is, not only in a review
This commit is contained in:
@@ -214,6 +214,11 @@ namespace MobileGL::MG_State {
|
||||
current.intValue[component] = static_cast<Int32>(value[component]);
|
||||
current.uintValue[component] = static_cast<Uint32>(value[component]);
|
||||
}
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The two views above are CONVERSIONS, not bit copies, so which one was written
|
||||
// is part of the value; set_vertex_attrib_defaults carries it.
|
||||
m_currentVertexAttributeClasses[index] = kVertexAttribValueClassFloat;
|
||||
#endif
|
||||
MGP_NOTE_AGGREGATE(VertexAttribDefault);
|
||||
}
|
||||
|
||||
@@ -229,6 +234,9 @@ namespace MobileGL::MG_State {
|
||||
current.floatValue[component] = static_cast<Float>(value[component]);
|
||||
current.uintValue[component] = static_cast<Uint32>(value[component]);
|
||||
}
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
m_currentVertexAttributeClasses[index] = kVertexAttribValueClassInt;
|
||||
#endif
|
||||
MGP_NOTE_AGGREGATE(VertexAttribDefault);
|
||||
}
|
||||
|
||||
@@ -244,6 +252,9 @@ namespace MobileGL::MG_State {
|
||||
current.floatValue[component] = static_cast<Float>(value[component]);
|
||||
current.intValue[component] = static_cast<Int32>(value[component]);
|
||||
}
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
m_currentVertexAttributeClasses[index] = kVertexAttribValueClassUint;
|
||||
#endif
|
||||
MGP_NOTE_AGGREGATE(VertexAttribDefault);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,26 @@ namespace MobileGL {
|
||||
void Init();
|
||||
|
||||
namespace GLState {
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// MGPAttribValue::ValueClass' encoding (MG_Pipe/MGPipeTypes.h documents the order
|
||||
// "Float | Int | Uint | Double"). It lives here rather than in MG_Pipe because the
|
||||
// FRONTEND is the only thing that knows which of the three views below a value was
|
||||
// written through - the other two are numeric conversions of it - and MG_Pipe has
|
||||
// no enum for the field yet. If package A introduces one, this becomes its alias.
|
||||
inline constexpr Uint32 kVertexAttribValueClassFloat = 0;
|
||||
inline constexpr Uint32 kVertexAttribValueClassInt = 1;
|
||||
inline constexpr Uint32 kVertexAttribValueClassUint = 2;
|
||||
#endif
|
||||
|
||||
struct CurrentVertexAttributeValue {
|
||||
Array<Float, 4> floatValue{0.f, 0.f, 0.f, 1.f};
|
||||
Array<Int32, 4> intValue{0, 0, 0, 1};
|
||||
Array<Uint32, 4> uintValue{0u, 0u, 0u, 1u};
|
||||
// Three scalar arrays and NOTHING ELSE. MG_Backend/MGPipe/PipeInputs.cpp
|
||||
// compares this storage with one memcmp and asserts that size, so a fourth
|
||||
// member here is a build break in a file P2 package B does not own. The
|
||||
// written-class discriminator set_vertex_attrib_defaults needs therefore
|
||||
// lives beside the array on GLContext, not inside the value.
|
||||
};
|
||||
|
||||
// Which of the three views above a shader input of a given GLSL type consumes.
|
||||
@@ -233,6 +249,27 @@ namespace MobileGL {
|
||||
Uint64 GetAnyVertexAttribDefaultGeneration() const {
|
||||
return m_anyVertexAttribDefaultGeneration;
|
||||
}
|
||||
|
||||
// Which of the three views of m_currentVertexAttributes[index] the last
|
||||
// glVertexAttrib* write filled DIRECTLY. The other two are NUMERIC
|
||||
// conversions of it (SetCurrentVertexAttribute* below), not bit copies, so
|
||||
// four words on a wire are not the value unless the class travels with them:
|
||||
// glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in
|
||||
// floatValue. set_vertex_attrib_defaults carries this as MGPAttribValue's
|
||||
// ValueClass so the applier can redo the conversion instead of memcpying one
|
||||
// view into all three.
|
||||
//
|
||||
// It is kept BESIDE the array rather than inside CurrentVertexAttributeValue
|
||||
// because that struct is mirrored into PipeInputs and compared there by a
|
||||
// memcmp whose size assertion (MG_Backend/MGPipe/PipeInputs.cpp) is a file
|
||||
// this package does not own - and because it need not be mirrored: the class
|
||||
// only decides how to REBUILD the three views, so two writes that leave the
|
||||
// three views identical rebuild identically whichever class they carried.
|
||||
Uint32 GetCurrentVertexAttributeClass(Uint index) const {
|
||||
return index < m_currentVertexAttributeClasses.size()
|
||||
? m_currentVertexAttributeClasses[index]
|
||||
: kVertexAttribValueClassFloat;
|
||||
}
|
||||
#endif
|
||||
|
||||
// RenderState
|
||||
@@ -554,6 +591,8 @@ namespace MobileGL {
|
||||
SharedPtr<ProgramObject> m_transformFeedbackProgram;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
Uint64 m_anyVertexAttribDefaultGeneration = 0;
|
||||
// Parallel to m_currentVertexAttributes; see GetCurrentVertexAttributeClass.
|
||||
Array<Uint32, VertexArrayObject::MAX_VERTEX_ATTRIBS> m_currentVertexAttributeClasses{};
|
||||
#endif
|
||||
Uint64 m_transformFeedbackGeneration = 0;
|
||||
// Source of the per-span ids above; never rolls back with an object switch.
|
||||
|
||||
Reference in New Issue
Block a user