diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index e6752f88..c5930126 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -691,24 +691,29 @@ namespace MobileGL::MG_Backend::DirectGLES { // family bit, so an operator can put the whole binding-point family back on the // frontend walk with one cleared bit. // - // BIT 13 REQUIRES BIT 7, refused here rather than half-run, in - // ResolveVertexInputSubsystemArm's exact shape and for its exact reason: every - // MGPBufferRange::Res is resolved through the resource slot table and only bit 7 puts - // twins there, so the pair would produce a walk in which every point logged once and - // `continue`d WITHOUT unbinding - every draw then reading through whatever the driver - // last had at that point. + // THE BINDING-POINT FAMILY'S DEPENDENCY ROW IS READ FROM MG_Pipe/SubsystemDeps.def + // (P3b/P4b R-5, switched over by wave 2-D package D3), refused here rather than half-run, + // in ResolveVertexInputSubsystemArm's exact shape. The hand-rolled + // `bitSet && !resourcesBitSet` this used to be was the sixth statement of D-K2's rule - + // the .def's own "WHO READS IT" section names this function, and now it does. The reason + // is the table's and is printed from it. Bool ResolveBufferBindingSubsystemArm() { if (MG_Config::Transport == MG_Config::TransportMode::Monolith) return false; - const Bool bitSet = - (MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemBufferBindings) != 0; - const Bool resourcesBitSet = - (MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemResources) != 0; - if (bitSet && !resourcesBitSet) { + const Uint64 mask = MG_Config::Features.PipePush; + const Bool bitSet = (mask & MG_Pipe::kMGPipeSubsystemBufferBindings) != 0; + if (bitSet && + !MG_Pipe::MGPipeSubsystemDependenciesAreSet(MG_Pipe::kMGPipeSubsystemBufferBindings, mask)) { + const Uint64 required = + MG_Pipe::MGPipeSubsystemRequires(MG_Pipe::kMGPipeSubsystemBufferBindings); MGLOG_E("MGPipe: kMGPipeSubsystemBufferBindings (bit 13) is set but " - "kMGPipeSubsystemResources (bit 7) is clear; every MGPBufferRange::Res " - "resolves through the resource slot table, which only bit 7 populates - " - "REFUSING bit 13 and running the legacy binding-point walk. Set bit 7 as " - "well, or clear both"); + "MOBILEGL_PIPE_PUSH=0x%llx does not carry every bit " + "MG_Pipe/SubsystemDeps.def says it requires (requires 0x%llx, missing " + "0x%llx): %s - REFUSING bit 13 and running the legacy binding-point walk. " + "Set every bit of the row, or clear bit 13", + static_cast(mask), + static_cast(required), + static_cast(required & ~mask), + MG_Pipe::MGPipeSubsystemDependencyWhy(MG_Pipe::kMGPipeSubsystemBufferBindings)); return false; } MGLOG_D("MGPipe: Espryt binding-point family runs the %s arm", diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 6b18f809..e891754b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -2952,24 +2952,26 @@ namespace MobileGL::MG_Backend::DirectGLES { } Bool ResolveVertexInputSubsystemArm() { - const Bool bitSet = (MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemVertexInput) != 0; - const Bool resourcesBitSet = - (MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemResources) != 0; - // BIT 8 REQUIRES BIT 7, and it is refused here rather than half-run. The vertex-input - // handle arm resolves every attribute's driver buffer id through - // FindBufferResourceForHandle, i.e. out of the resource SLOT TABLE - and only bit 7 - // puts twins in that table (with bit 7 clear EnsureBufferResource takes the legacy - // arm and parks the twin on PipeResource::m_backend instead). The pair therefore - // produced a walk in which every BindAttributeBufferByHandle logged once and - // `continue`d WITHOUT disabling the array, so every draw fetched through whatever - // pointer the driver VAO last held. The mirror pair (bit 7 set, bit 8 clear) is fine: - // the legacy VAO walk calls BindAttributeBuffer -> EnsureBufferResource, which - // dispatches to the handle arm by itself. - if (bitSet && !resourcesBitSet) { - MGLOG_E("MGPipe: kMGPipeSubsystemVertexInput (bit 8) is set but kMGPipeSubsystemResources " - "(bit 7) is clear; the vertex-input handle arm resolves attribute buffer ids " - "through the resource slot table, which only bit 7 populates - REFUSING bit 8 and " - "running the legacy vertex-input arm. Set bit 7 as well, or clear both"); + const Uint64 mask = MG_Config::Features.PipePush; + const Bool bitSet = (mask & MG_Pipe::kMGPipeSubsystemVertexInput) != 0; + // The vertex-input family's row, READ FROM MG_Pipe/SubsystemDeps.def (P3b/P4b R-5, + // switched over by wave 2-D package D3) rather than restated as the hand-rolled + // `bitSet && !resourcesBitSet` this function used to carry - one of the six statements + // of D-K2's rule that R-5 found with nothing comparing them. The table's own sentence + // is what the refusal prints. The mirror pair (bit 7 set, bit 8 clear) is fine - the + // legacy VAO walk calls BindAttributeBuffer -> EnsureBufferResource, which dispatches + // to the handle arm by itself - and lives with the other mirror pairs in MGPipe.h. + if (bitSet && !MG_Pipe::MGPipeSubsystemDependenciesAreSet(MG_Pipe::kMGPipeSubsystemVertexInput, mask)) { + MGLOG_E("MGPipe: kMGPipeSubsystemVertexInput (bit 8) is set but MOBILEGL_PIPE_PUSH=" + "0x%llx does not carry every bit MG_Pipe/SubsystemDeps.def says it requires " + "(requires 0x%llx, missing 0x%llx): %s - REFUSING bit 8 and running the " + "legacy vertex-input arm. Set every bit of the row, or clear bit 8", + static_cast(mask), + static_cast( + MG_Pipe::MGPipeSubsystemRequires(MG_Pipe::kMGPipeSubsystemVertexInput)), + static_cast( + MG_Pipe::MGPipeSubsystemRequires(MG_Pipe::kMGPipeSubsystemVertexInput) & ~mask), + MG_Pipe::MGPipeSubsystemDependencyWhy(MG_Pipe::kMGPipeSubsystemVertexInput)); return false; } #if MOBILEGL_PIPE_LEGACY_MEMOS @@ -4228,14 +4230,32 @@ namespace MobileGL::MG_Backend::DirectGLES { // Fatal{PipeLegacyMemosDisabled, ...} rather than running the very arm the operator asked // to have taken away and handing back a result measured on it. namespace { - // Shared by the three dependent families, so the refusal reads the same way three times - // and a future fourth cannot invent a different wording. Returns true when the - // dependency is missing, having said so once. - Bool PipeSubsystemDependencyMissing(Uint64 mask, Uint64 dependencyBit, const char* what) { - if ((mask & dependencyBit) != 0) return false; - MGLOG_E("MGPipe: %s - REFUSING the dependent bit and running the legacy arm. " - "Set both bits, or clear both", - what); + // Shared by the dependent families, so the refusal reads the same way every time and a + // future family cannot invent a different wording. Returns true when the dependency is + // missing, having said so once. + // + // P3b/P4b wave 2-D package D3: THE ROWS ARE READ FROM MG_Pipe/SubsystemDeps.def AND ARE + // NO LONGER RESTATED HERE. This function used to take a single dependency BIT written out + // at each call site - the server's own copy of the rule, one of the six statements R-5 + // found and two of which had drifted - and a family with two required bits needed two + // guarded calls with two hand-written sentences. It now takes the FAMILY and asks + // MGPipeSubsystemDependenciesAreSet, so the set of required bits, the subset that is + // actually missing and the reason all come from the table; `what` is only the family's + // own name. The client's gate reads the same rows + // (MG_Impl/Pipe/PipeFill.cpp P4aFamilyDependencyBits), which is what makes + // MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp's comparison of the two a comparison + // of two readers rather than of two copies. + Bool PipeSubsystemDependencyMissing(Uint64 mask, Uint64 family, const char* what) { + if (MG_Pipe::MGPipeSubsystemDependenciesAreSet(family, mask)) return false; + const Uint64 required = MG_Pipe::MGPipeSubsystemRequires(family); + MGLOG_E("MGPipe: %s is set but MOBILEGL_PIPE_PUSH=0x%llx does not carry every bit " + "MG_Pipe/SubsystemDeps.def says it requires (requires 0x%llx, missing 0x%llx): " + "%s - REFUSING the dependent bit and running the legacy arm. Set every bit of " + "the row, or clear the family's own", + what, static_cast(mask), + static_cast(required), + static_cast(required & ~mask), + MG_Pipe::MGPipeSubsystemDependencyWhy(family)); return true; } @@ -4344,17 +4364,13 @@ namespace MobileGL::MG_Backend::DirectGLES { const Bool bitSet = (mask & MG_Pipe::kMGPipeSubsystemFramebuffer) != 0; Bool refused = false; if (bitSet) { - // BIT 9 REQUIRES BIT 10. Every MGPSurface::Res in the record names a Texture or a - // Renderbuffer handle, and only bit 10 puts twins in those two slot tables; without - // it every attachment lookup would miss and the walk would leave the driver - // framebuffer holding whatever the last owner attached. The mirror pair (bit 10 set, - // bit 9 clear) is FINE: the legacy FBO sync reaches the texture twin through - // SyncTextureObjectToBackend, which dispatches to the handle arm by itself. - refused = PipeSubsystemDependencyMissing( - mask, MG_Pipe::kMGPipeSubsystemTextureResources, - "kMGPipeSubsystemFramebuffer (bit 9) is set but kMGPipeSubsystemTextureResources " - "(bit 10) is clear; MGPSurface::Res names a Texture or Renderbuffer handle and " - "only bit 10 populates those slot tables"); + // The framebuffer family's row, from MG_Pipe/SubsystemDeps.def. The mirror pair (bit + // 10 set, bit 9 clear) is FINE - the legacy FBO sync reaches the texture twin through + // SyncTextureObjectToBackend, which dispatches to the handle arm by itself - and that + // is stated with the other mirror pairs in MGPipe.h, beside the table it is the + // complement of, rather than here. + refused = PipeSubsystemDependencyMissing(mask, MG_Pipe::kMGPipeSubsystemFramebuffer, + "kMGPipeSubsystemFramebuffer (bit 9)"); // D-C3: the wire array is Color[8] and this is the driver's RAW ES cap, which is // NOT clamped to 8 on the GLES path (ValidateColorAttachmentInRange rejects at or // above it, and BackendObject_DirectGLES publishes it verbatim). Every campaign @@ -4403,42 +4419,19 @@ namespace MobileGL::MG_Backend::DirectGLES { const Bool bitSet = (mask & MG_Pipe::kMGPipeSubsystemTextureResources) != 0; Bool refused = false; if (bitSet) { - // BIT 10 REQUIRES BIT 7. A buffer texture's MGPResourceDesc::BufferForTexBuffer - // names a Buffer handle and only bit 7 puts twins in the resource slot table, so - // with bit 7 clear every glTexBuffer would resolve to no storage at all. The mirror - // pair (bit 7 set, bit 10 clear) is FINE and is P3a's shipped configuration. - refused = PipeSubsystemDependencyMissing( - mask, MG_Pipe::kMGPipeSubsystemResources, - "kMGPipeSubsystemTextureResources (bit 10) is set but kMGPipeSubsystemResources " - "(bit 7) is clear; a buffer texture's BufferForTexBuffer names a Buffer handle " - "and only bit 7 populates the resource slot table"); - // BIT 10 REQUIRES BIT 11 - D-K2's FOURTH ROW (ID-14/ID-15), and it is the exact - // mirror of ResolveVertexInputSubsystemArm's bit-8-requires-bit-7 refusal above: - // the same "the handle arm resolves through a slot table only the other bit - // populates" shape, refused here rather than half-run, with the legacy arm as the - // fall-back. MGPTextureParams::BuiltinSampler is a SamplerCso HANDLE and only bit 11 - // mints sampler CSOs (contract c0b's four unconditional mints deliberately exclude - // it), so with bit 11 clear every set_texture_params would carry a null there - and - // the applier's verdict for a null BuiltinSampler is Fatal{ProtocolCorruption}, not - // a decline. The brief's original sentence "bit 10 without 11 is fine" is WITHDRAWN - // for P4a as built; package F pins this arm at 0x5ff. + // The texture family's row, from MG_Pipe/SubsystemDeps.def. It is the ONLY row with + // two required bits, and it used to be TWO guarded calls here with the pair of + // sentences written out by hand - bit 7 for a buffer texture's BufferForTexBuffer, + // and bit 11, D-K2's FOURTH ROW (ID-14/ID-15), for MGPTextureParams::BuiltinSampler. + // One call now asks the table for both, and the refusal names whichever of the two is + // actually missing instead of always naming the first. Package F pins this arm at + // 0x5ff and CMakeLists.txt's refusal lane at 0x7ff; both are rows of the table now. // - // THE TWO MIRROR PAIRS THAT STAY FINE, said out loud because an unreachable branch - // that says something different is how the reachable one drifts: - // - bit 7 set, bit 10 clear: P3a's shipped configuration (above). - // - bit 11 set, bit 10 clear: refused one function down, by the bit-11-requires- - // bit-10 row, so the pair is symmetric and neither half can run alone. That - // symmetry is the point: bits 10 and 11 are now ONE arm with two switches, and - // the only two masks that reach the texture handle arm are "both set" and - // "neither set". - if (!refused) { - refused = PipeSubsystemDependencyMissing( - mask, MG_Pipe::kMGPipeSubsystemSamplers, - "kMGPipeSubsystemTextureResources (bit 10) is set but kMGPipeSubsystemSamplers " - "(bit 11) is clear; MGPTextureParams::BuiltinSampler is a SamplerCso handle, " - "only bit 11 mints sampler CSOs, and the applier's verdict for a null one is " - "Fatal{ProtocolCorruption}"); - } + // The mirror pairs live in MGPipe.h with the rest. The one worth remembering at this + // call site is that bits 10 and 11 are SYMMETRIC - each row names the other - so the + // only two masks that reach the texture handle arm are "both set" and "neither set". + refused = PipeSubsystemDependencyMissing(mask, MG_Pipe::kMGPipeSubsystemTextureResources, + "kMGPipeSubsystemTextureResources (bit 10)"); } const BufferImpl::PipeSubsystemArmVerdict verdict = BufferImpl::ClassifyPipeSubsystemArm( bitSet && !refused, MG_Config::Features.PipeLegacyMemos, @@ -4469,23 +4462,12 @@ namespace MobileGL::MG_Backend::DirectGLES { const Bool bitSet = (mask & MG_Pipe::kMGPipeSubsystemSamplers) != 0; Bool refused = false; if (bitSet) { - // BIT 11 REQUIRES BIT 10, and this is the pair G12 drives at 0x9ff. Every - // MGPBoundView::Texture and every MGPImageView::Res names a Texture handle, and - // only bit 10 puts one in the slot table; without it every per-unit lookup would - // miss and the walk would `continue` WITHOUT unbinding - i.e. every draw would - // sample through whatever the unit last held, which is exactly the shape the - // bit-8-without-bit-7 refusal exists to prevent one family over. - // - // THE MIRROR PAIR (bit 10 set, bit 11 clear) IS NOT FINE EITHER, and that is D-K2's - // fourth row (ID-14/ID-15): it is refused by ResolveTextureResourceSubsystemArm - // above, because MGPTextureParams::BuiltinSampler is a SamplerCso handle only bit 11 - // mints. So this dependency is SYMMETRIC - the two bits are one arm with two - // switches - and this sentence used to claim the opposite. - refused = PipeSubsystemDependencyMissing( - mask, MG_Pipe::kMGPipeSubsystemTextureResources, - "kMGPipeSubsystemSamplers (bit 11) is set but kMGPipeSubsystemTextureResources " - "(bit 10) is clear; every MGPBoundView::Texture and MGPImageView::Res names a " - "Texture handle and only bit 10 populates that slot table"); + // The sampler family's row, from MG_Pipe/SubsystemDeps.def, and this is the pair G12 + // drives at 0x9ff. Its mirror (bit 10 set, bit 11 clear) is NOT fine either - that is + // D-K2's fourth row, refused by ResolveTextureResourceSubsystemArm above - so the + // dependency is SYMMETRIC and the table carries both halves. + refused = PipeSubsystemDependencyMissing(mask, MG_Pipe::kMGPipeSubsystemSamplers, + "kMGPipeSubsystemSamplers (bit 11)"); } const BufferImpl::PipeSubsystemArmVerdict verdict = BufferImpl::ClassifyPipeSubsystemArm( bitSet && !refused, MG_Config::Features.PipeLegacyMemos, diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 21e61029..e0023f79 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -687,12 +687,24 @@ namespace MobileGL::MG_Backend::DirectGLES { // BackendSamplerObject* rows; // programs - g_programTwinLookupMemo. // - // THREE OF THEM CARRY A DEPENDENCY (MGPipe.h, D-K2) and it is diagnosed and REFUSED here - // rather than half-run, the bit-8-requires-bit-7 shape ResolveVertexInputSubsystemArm - // already ships: bit 11 requires bit 10, bit 9 requires bit 10, bit 10 requires bit 7. The - // mirror pairs (10 without 11, 10 without 9, 7 without 10) are all FINE and are said so out - // loud, because an unreachable branch that says something different is how the reachable - // one drifts. Bit 12 depends on nothing: a ShaderCso handle names no texture and no buffer. + // THE DEPENDENCY ROWS ARE IN MG_Pipe/SubsystemDeps.def, ONCE (P3b/P4b R-5), and the four + // resolvers below READ them through MGPipeSubsystemDependenciesAreSet rather than carrying a + // copy: a dependency the table declares is diagnosed and REFUSED here rather than half-run, + // the bit-8-requires-bit-7 shape ResolveVertexInputSubsystemArm already ships. + // + // THIS COMMENT USED TO RESTATE THE ROWS AND WAS WRONG IN TWO WAYS THAT NOTHING COULD FAIL, + // in the header of the file that IMPLEMENTS the refusal. It said "THREE OF THEM CARRY A + // DEPENDENCY" when there are six rows (bits 8 and 13 were the two it did not know about), + // and it listed "10 without 11" among the mirror pairs that are FINE - when 10-without-11 is + // precisely D-K2's FOURTH row (ID-14/ID-15), refused twenty lines below by + // ResolveTextureResourceSubsystemArm and withheld by the client at PipeFill.cpp's own gate. + // MGPipe.h carried the identical pair of defects and lost them with the table; this copy was + // left behind because Managers.h belonged to a package in flight. The rows are not restated + // here again: the file that states them is the one the code reads. + // + // The mirror pairs that really ARE fine are stated in MGPipe.h beside the table, because an + // absence is not a row - and because that is the one place where saying it cannot drift away + // from the rows it is the complement of. // // ResolveFramebufferSubsystemArm additionally carries D-C3's bring-up refusal: the wire // array is MGPFramebufferState::Color[8] and GetDynamicParameters().MaxColorAttachments is diff --git a/MobileGL/MG_Impl/Pipe/PipeFill.cpp b/MobileGL/MG_Impl/Pipe/PipeFill.cpp index 4f76483b..58a8df71 100644 --- a/MobileGL/MG_Impl/Pipe/PipeFill.cpp +++ b/MobileGL/MG_Impl/Pipe/PipeFill.cpp @@ -1474,79 +1474,44 @@ namespace MobileGL::MG_Pipe { // above: with a dependency unmet the client emits NOTHING for that family and the legacy // pull path runs untouched, on both sides of the boundary. // - // THE TABLE IS WRITTEN ONCE, HERE, and every one of its rows is the client mirror of the - // refusal Espryt already implements, bit for bit and non-transitively - the two must say - // the SAME thing, because a client that withheld more than the server refuses would - // leave the server's handle arm live with no records to read, and a client that withheld - // less is the defect above. - struct P4aFamilyDependencyRow { - Uint64 Family; // exactly one bit, and it is one of kMGPipeP4aFamilySubsystems - Uint64 Requires; // the bits MOBILEGL_PIPE_PUSH must ALSO carry for it to be live - }; - - inline constexpr P4aFamilyDependencyRow kMGPipeP4aFamilyDependencies[] = { - // BIT 9 REQUIRES BIT 10. Every MGPSurface::Res in a set_framebuffer_state record - // names a Texture or a Renderbuffer handle, and only bit 10 populates those two slot - // tables (Managers.cpp ResolveFramebufferSubsystemArm). - {kMGPipeSubsystemFramebuffer, kMGPipeSubsystemTextureResources}, - - // BIT 10 REQUIRES BIT 7 - a buffer texture's MGPResourceDesc::BufferForTexBuffer - // names a Buffer handle and only bit 7 puts twins in the resource slot table (D-D1, - // ResolveTextureResourceSubsystemArm's first row) - AND BIT 11, which is D-K2's - // FOURTH row (ID-14/ID-15): MGPTextureParams::BuiltinSampler is a SamplerCso HANDLE, - // only bit 11 mints sampler CSOs (c0b's four unconditional mints deliberately - // exclude it), and the applier's verdict for a null one is Fatal{ProtocolCorruption} - // rather than a decline. The brief's original "bit 10 without 11 is fine" is - // WITHDRAWN for P4a as built. - {kMGPipeSubsystemTextureResources, - kMGPipeSubsystemResources | kMGPipeSubsystemSamplers}, - - // BIT 11 REQUIRES BIT 10. Every MGPBoundView::Texture and every MGPImageView::Res - // names a Texture handle and only bit 10 populates that slot table; without it every - // per-unit lookup would miss and the walk would `continue` WITHOUT unbinding - // (ResolveSamplerSubsystemArm). With the row above this is SYMMETRIC: bits 10 and 11 - // are one arm with two switches, and the only two masks that reach either handle arm - // are "both set" and "neither set". - {kMGPipeSubsystemSamplers, kMGPipeSubsystemTextureResources}, - - // BIT 12 DEPENDS ON NOTHING, and that is a ROW rather than an absence so the table - // covers the four families exhaustively (the static_assert below): a ShaderCso handle - // names no texture and no buffer, the archive rides beside the record as a companion - // pointer, and the extra inputs the server specialises on are read from state the - // backend already holds (ResolveProgramSubsystemArm). - {kMGPipeSubsystemPrograms, 0}, - }; - - // THE MIRROR PAIRS THAT STAY FINE, said out loud rather than left as an absence, because - // an unreachable branch that says something different is how the reachable one drifts - // (Managers.cpp's own words at :2377-2381) - and because the table is only trustworthy if - // what it does NOT contain was decided rather than forgotten: - // - bit 10 set, bit 9 clear: FINE. The legacy FBO sync reaches the texture twin through - // SyncTextureObjectToBackend, which dispatches to the handle arm by itself. - // - bit 11 set, bit 9 clear: FINE, for the same reason - a sampler view names a texture, - // never a framebuffer. - // - bit 7 set, bit 10 clear: FINE, and it is P3a's shipped configuration. - // - bit 12 set with any or none of 9/10/11: FINE, per the last row. - // - bit 10 set, bit 11 clear (and its mirror) is NOT fine and is the row above; this is - // the one sentence in the brief that P4a as built withdrew. + // THE TABLE IS WRITTEN ONCE, IN MG_Pipe/SubsystemDeps.def, and every one of its rows is + // the client mirror of the refusal Espryt already implements, bit for bit and + // non-transitively - the two must say the SAME thing, because a client that withheld more + // than the server refuses would leave the server's handle arm live with no records to + // read, and a client that withheld less is the defect above. + // + // THIS FILE USED TO HOLD ITS OWN COPY of the four P4a rows (P3b/P4b R-5 found six + // statements of one rule and two of them drifted). The rows moved to the .def with R-5 + // and this reader was left pointing at the copy deliberately, because PipeFill.cpp is the + // contract package's file for the phase; wave 2-D package D3 switches it over. + // + // THE ARGUMENT IS MASKED TO kMGPipeP4aFamilySubsystems, AND THAT IS THE WHOLE DIFFERENCE + // BETWEEN A REWRITE AND A BEHAVIOUR CHANGE. The .def carries SIX rows; the table deleted + // from here carried FOUR, and this predicate is `wants()`'s fourth conjunct for P4a's + // families only. Reading all six unmasked would make the CLIENT withhold bit 8's + // vertex-input emissions at a mask with bit 7 clear - P3a's own rule, which this gate has + // never narrowed and which TextureEmitTest's + // EveryDKTwoDependencyRowGatesItsOwnFamilyAndTheMirrorPairsStayLive pins by name ("bit 8 + // alone, with bit 7 clear: P3a's own rule, which this table must not touch"). Bit 13 is + // not narrowed here either; it asks the table for itself in P5eFamilyIsLive below, + // because bit 13's conjunct is bit 13's own. So the ROWS now come from one place and WHO + // each gate speaks for is still each gate's. + // + // The P4a-specific COVERAGE assertion stays for the same reason: a fifth P4a family + // without a row in the .def has to be a compile error, not a silent "depends on nothing". constexpr Uint64 P4aFamilyDependencyBits(Uint64 subsystem) { - Uint64 required = 0; - for (const P4aFamilyDependencyRow& row : kMGPipeP4aFamilyDependencies) { - if ((subsystem & row.Family) != 0) required |= row.Requires; - } - return required; + return MGPipeSubsystemRequires(subsystem & kMGPipeP4aFamilySubsystems); } - // The table covers the four families this phase migrates and nothing else, so a fifth - // family added to kMGPipeP4aFamilySubsystems without a row here does not silently inherit - // "depends on nothing". constexpr Uint64 P4aFamilyDependencyTableCoverage() { Uint64 covered = 0; - for (const P4aFamilyDependencyRow& row : kMGPipeP4aFamilyDependencies) covered |= row.Family; + for (const MGPipeSubsystemDependencyRow& row : kMGPipeSubsystemDependencies) covered |= row.Family; return covered; } - static_assert(P4aFamilyDependencyTableCoverage() == kMGPipeP4aFamilySubsystems, - "every P4a family needs a D-K2 dependency row, even an empty one"); + static_assert((P4aFamilyDependencyTableCoverage() & kMGPipeP4aFamilySubsystems) == + kMGPipeP4aFamilySubsystems, + "every P4a family needs a D-K2 dependency row in MG_Pipe/SubsystemDeps.def, " + "even an empty one"); static_assert(P4aFamilyDependencyBits(kMGPipeSubsystemFramebuffer) == kMGPipeSubsystemTextureResources, "bit 9 requires bit 10"); @@ -1602,14 +1567,18 @@ namespace MobileGL::MG_Pipe { // server whose four binding-point walks still read the frontend, so a record sent to it // would be stored and never looked at while the client latched its suppressor. // - // AND BIT 13 REQUIRES BIT 7, for bit 11's reason one family over: every - // MGPBufferRange::Res names a Buffer handle and only bit 7 puts one in the resource - // slot table, so without it every EnsureBufferResourceForHandle on the server would - // mint a twin with no record behind it. Withholding the whole family is the safe - // direction - the legacy frontend walk runs untouched on both sides. + // AND BIT 13'S OWN DEPENDENCY ROW IS READ FROM MG_Pipe/SubsystemDeps.def, exactly as + // P4a's four are (P3b/P4b R-5, switched over by wave 2-D package D3). It used to be the + // hand-coded `(pushMask & kMGPipeSubsystemResources) == 0` below - the sixth statement of + // the rule, and the one that sat fifteen lines from the block stating the other three, + // which is how "THREE OF THEM" survived two phases. Its reason is the table's: every + // MGPBufferRange::Res names a Buffer handle and only bit 7 puts one in the resource slot + // table, so without it every EnsureBufferResourceForHandle on the server would mint a + // twin with no record behind it. Withholding the whole family is the safe direction - the + // legacy frontend walk runs untouched on both sides. Bool P5eFamilyIsLive(Uint64 subsystem, Uint64 pushMask) { if ((subsystem & kMGPipeSubsystemBufferBindings) == 0) return true; - if ((pushMask & kMGPipeSubsystemResources) == 0) return false; + if (!MGPipeSubsystemDependenciesAreSet(kMGPipeSubsystemBufferBindings, pushMask)) return false; #if MOBILEGL_BUILD_DISAGGREGATED if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { return MG_Remote::Client::CapsMirrorInstance().ServerConsumes( diff --git a/MobileGL/MG_Pipe/MGPipe.h b/MobileGL/MG_Pipe/MGPipe.h index c7e6fa9b..953eb7d6 100644 --- a/MobileGL/MG_Pipe/MGPipe.h +++ b/MobileGL/MG_Pipe/MGPipe.h @@ -222,6 +222,18 @@ namespace MobileGL::MG_Pipe { return (pushMask & required) == required; } + // The ROW'S OWN REASON, so a reader that refuses prints the table's sentence instead of a + // copy of it (P3b/P4b wave 2-D package D3). Empty for a family whose row requires nothing, + // which is the same answer as "there is nothing to explain": a reader only asks after + // MGPipeSubsystemDependenciesAreSet has already said no, and that cannot happen for a row + // that requires 0. + inline constexpr const char* MGPipeSubsystemDependencyWhy(Uint64 subsystem) { + for (const MGPipeSubsystemDependencyRow& row : kMGPipeSubsystemDependencies) { + if ((subsystem & row.Family) != 0 && row.Requires != 0) return row.Why; + } + return ""; + } + static_assert(MGPipeSubsystemRequires(kMGPipeSubsystemTextureResources) == (kMGPipeSubsystemResources | kMGPipeSubsystemSamplers), "D-K2's fourth row (bit 10 requires bit 11) has gone missing again"); diff --git a/MobileGL/MG_Pipe/SubsystemDeps.def b/MobileGL/MG_Pipe/SubsystemDeps.def index 24966946..691236f4 100644 --- a/MobileGL/MG_Pipe/SubsystemDeps.def +++ b/MobileGL/MG_Pipe/SubsystemDeps.def @@ -28,23 +28,34 @@ // MG_Remote/FatalFamilies.def - no generator reads it - and it carries pure bit constants, so // including it from MGPipe.h keeps check_include_closure.py's value-header rule satisfied. // -// WHO READS IT. MGPipe.h turns it into kMGPipeSubsystemDependencies[] and -// MGPipeSubsystemRequires(). The two executable statements of the rule are: +// WHO READS IT. MGPipe.h turns it into kMGPipeSubsystemDependencies[], +// MGPipeSubsystemRequires(), MGPipeSubsystemDependenciesAreSet() and +// MGPipeSubsystemDependencyWhy(). The two executable statements of the rule read those, and as +// of wave 2-D package D3 they read NOTHING ELSE - every hand-written copy is gone: // -// CLIENT dependency check MG_Impl/Pipe/PipeFill.cpp's kMGPipeP4aFamilyDependencies / -// P4aFamilyDependenciesAreSet (bits 9-12), plus P5eFamilyIsLive's -// hand-coded bit-13 row. +// CLIENT dependency check MG_Impl/Pipe/PipeFill.cpp's P4aFamilyDependencyBits / +// P4aFamilyDependenciesAreSet (bits 9-12) and P5eFamilyIsLive +// (bit 13). The local kMGPipeP4aFamilyDependencies[] is deleted; +// what stays is the P4a-specific COVERAGE static_assert, because +// that gate answers for four of these six families and a fifth P4a +// family without a row here must still be a compile error. // SERVER consumer gate MG_Backend/DirectGLES/Managers.cpp's four -// ResolveSubsystemArm() over PipeSubsystemDependencyMissing -// (bits 9-12), plus ResolveVertexInputSubsystemArm (bit 8) and -// DirectGLES.cpp's ResolveBufferBindingSubsystemArm (bit 13). +// ResolveSubsystemArm() over PipeSubsystemDependencyMissing, +// which now takes the FAMILY rather than a literal bit (bits 9-12), +// plus ResolveVertexInputSubsystemArm (bit 8) and DirectGLES.cpp's +// ResolveBufferBindingSubsystemArm (bit 13). The texture family's +// two guarded calls collapse into one, and the refusal names +// whichever required bit is actually missing instead of always the +// first, because the missing set is computed from the row. // -// THEY ARE NOT SWITCHED OVER IN THIS COMMIT, deliberately: PipeFill.cpp is the contract -// package's file for the whole phase (TextureEmit.h states that rule) and Managers.cpp belongs -// to wave 2-D package D1 while it is in flight. The anti-drift property does not wait for that, -// because MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp drives BOTH readers at every +// The rows moved here one commit before the readers did (R-5 landed with PipeFill.cpp and +// Managers.cpp belonging to packages in flight), and the anti-drift property never waited for +// that: MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp drives BOTH readers at every // interesting mask and compares them against these rows - a change to either side alone reds. -// Switching the two readers to read this table is two one-line changes for the integrator. +// It is now a comparison of two READERS rather than of two copies, which is strictly weaker as +// a drift detector and strictly stronger as a guarantee; it is kept because the two sides still +// fold in reasons of their own (the server's own family bit, D-C3's wire-caps refusal, the +// client's consumer and wired conjuncts) and those can still disagree. // // THE ROWS ARE NOT TRANSITIVE, and that is load-bearing rather than an oversight: each reader // tests the required BITS against MOBILEGL_PIPE_PUSH alone, never the other family's liveness,