[Fix] (Pipe): retire wire's local Named constant now that the contract mints MGPipeFramebufferTarget::Named

This commit is contained in:
2026-09-08 15:08:22 -04:00
parent 8f1eaafa79
commit 712c946744
3 changed files with 12 additions and 26 deletions
+2 -2
View File
@@ -2145,12 +2145,12 @@ namespace MobileGL::MG_Pipe {
// GL HAS TWO INDEPENDENT FRAMEBUFFER BINDINGS AND THIS RECORD DESCRIBES ONE FRAMEBUFFER, // GL HAS TWO INDEPENDENT FRAMEBUFFER BINDINGS AND THIS RECORD DESCRIBES ONE FRAMEBUFFER,
// so Target says which binding - if any - the description also BINDS it to. Both is one // so Target says which binding - if any - the description also BINDS it to. Both is one
// object bound to both and moves both handles from one call; Named // object bound to both and moves both handles from one call; Named
// (kMGPipeFramebufferTargetNamed) moves neither, which is how the DSA entry points // (MGPipeFramebufferTarget::Named) moves neither, which is how the DSA entry points
// describe a framebuffer they are about to blit into or clear without claiming it is // describe a framebuffer they are about to blit into or clear without claiming it is
// bound (ID-19). A value above Named is not a target this server has, and guessing one // bound (ID-19). A value above Named is not a target this server has, and guessing one
// would put a draw's attachments into the read binding or the other way round. // would put a draw's attachments into the read binding or the other way round.
const char* fault = nullptr; const char* fault = nullptr;
if (state.Target > kMGPipeFramebufferTargetNamed) { if (state.Target >= static_cast<Uint8>(MGPipeFramebufferTarget::Count)) {
fault = "the record names no framebuffer binding target"; fault = "the record names no framebuffer binding target";
} }
// THE RECORD IS ADDRESSED BY THE FRAMEBUFFER HANDLE NOW, so the handle has to be one. // THE RECORD IS ADDRESSED BY THE FRAMEBUFFER HANDLE NOW, so the handle has to be one.
+7 -21
View File
@@ -155,25 +155,11 @@ namespace MobileGL::MG_Pipe {
// 4-billion-entry resize. // 4-billion-entry resize.
inline constexpr Uint32 kMGPipeMaxFramebufferSlots = 1u << 16; inline constexpr Uint32 kMGPipeMaxFramebufferSlots = 1u << 16;
// THE FOURTH set_framebuffer_state TARGET, AND IT IS THE CONTRACT'S TO MINT (c0e: // THE FOURTH set_framebuffer_state TARGET is the contract's MGPipeFramebufferTarget::Named (c0e):
// MGPipeFramebufferTarget::Named = 3). It is declared here as a plain constant because wire // "this record describes the framebuffer it names; no binding changes." Draw / Read / Both
// v3 and c0e run in parallel: the applier must ADMIT the value now, and this package may not // write the record AND set the bound handle(s); Named writes the record only, which is how
// write MGPipeTypes.h. When c0e lands, this constant is deleted and every use below becomes // the DSA entry points - BlitNamedFramebuffer and the four ClearNamedFramebuffer* - hand the
// static_cast<Uint8>(MGPipeFramebufferTarget::Named) - wire's verification round retires it, // server a record for a framebuffer bound to neither binding (esprytobj review C-1, ID-19).
// and the static_assert underneath is what makes forgetting impossible: the day the
// enumerator exists, MGPipeFramebufferTarget::Count becomes 4 and this fires.
//
// ITS MEANING: "this record describes the framebuffer it names; no binding changes." Draw /
// Read / Both write the record AND set the bound handle(s); Named writes the record only.
// That is what lets the DSA entry points - BlitNamedFramebuffer and the four
// ClearNamedFramebuffer* - be handed a record for a framebuffer that is bound to neither
// binding, which is the hole esprytobj's C-1 found: the applier used to hold the two BOUND
// records only, so a named blit or clear reached a driver FBO that never got its
// attachments.
inline constexpr Uint8 kMGPipeFramebufferTargetNamed = 3;
static_assert(static_cast<Uint8>(MGPipeFramebufferTarget::Count) == kMGPipeFramebufferTargetNamed,
"c0e has landed MGPipeFramebufferTarget::Named: delete kMGPipeFramebufferTargetNamed "
"and spell the enumerator (wire's verification round, ID-21)");
// The two framebuffer BINDINGS, and there are two rather than three: Both and Named are // The two framebuffer BINDINGS, and there are two rather than three: Both and Named are
// things a RECORD says, not bindings a server has. MGPipeApplierState::BoundFramebuffer is // things a RECORD says, not bindings a server has. MGPipeApplierState::BoundFramebuffer is
@@ -592,7 +578,7 @@ namespace MobileGL::MG_Pipe {
// described this binding yet", which is what a make-current leaves behind. // described this binding yet", which is what a make-current leaves behind.
// //
// set_framebuffer_state Draw / Read / Both writes the RECORD at state.Fbo's slot AND // set_framebuffer_state Draw / Read / Both writes the RECORD at state.Fbo's slot AND
// sets the handle(s) here; Named (kMGPipeFramebufferTargetNamed) writes the record and // sets the handle(s) here; Named (MGPipeFramebufferTarget::Named) writes the record and
// touches nothing here at all - that is the whole of the fourth target's meaning. // touches nothing here at all - that is the whole of the fourth target's meaning.
Array<MGPipeHandle, kMGPipeFramebufferBindingCount> BoundFramebuffer{}; Array<MGPipeHandle, kMGPipeFramebufferBindingCount> BoundFramebuffer{};
// ONE SERIAL FOR THE FAMILY, and it moves on EVERY write - a Named record's included, // ONE SERIAL FOR THE FAMILY, and it moves on EVERY write - a Named record's included,
@@ -962,7 +948,7 @@ namespace MobileGL::MG_Pipe {
// can never displace another's, and a slot whose object has been recycled is simply // can never displace another's, and a slot whose object has been recycled is simply
// overwritten by its successor's record (D-I2: no wire lifetime, so nothing to retire). // overwritten by its successor's record (D-I2: no wire lifetime, so nothing to retire).
// - Draw / Read / Both ADDITIONALLY set BoundFramebuffer[Draw] / [Read] / both. // - Draw / Read / Both ADDITIONALLY set BoundFramebuffer[Draw] / [Read] / both.
// kMGPipeFramebufferTargetNamed sets NEITHER: it is how a DSA entry point hands Espryt // MGPipeFramebufferTarget::Named sets NEITHER: it is how a DSA entry point hands Espryt
// a framebuffer it is about to blit into or clear WITHOUT claiming it is bound. // a framebuffer it is about to blit into or clear WITHOUT claiming it is bound.
// //
// FramebufferSerial advances on every applied record, Named included. // FramebufferSerial advances on every applied record, Named included.
@@ -273,7 +273,7 @@ TEST(FramebufferEmit, ATargetOutsideTheThreeBindingsIsRefusedNamingTheRecord) {
#else #else
ApplierGuard guard; ApplierGuard guard;
MGPFramebufferState bad = FramebufferRecord(MGPipeHandle{7, 4}, MGPipeFramebufferTarget::Draw, 100); MGPFramebufferState bad = FramebufferRecord(MGPipeHandle{7, 4}, MGPipeFramebufferTarget::Draw, 100);
bad.Target = static_cast<Uint8>(kMGPipeFramebufferTargetNamed + 1); bad.Target = static_cast<Uint8>(MGPipeFramebufferTarget::Count);
const Uint64 serialBefore = MGPipeApplier().FramebufferSerial; const Uint64 serialBefore = MGPipeApplier().FramebufferSerial;
ExpectRefusedNaming("set_framebuffer_state {slot=7, gen=4, target=4}: the record names no framebuffer " ExpectRefusedNaming("set_framebuffer_state {slot=7, gen=4, target=4}: the record names no framebuffer "
@@ -440,7 +440,7 @@ TEST(FramebufferEmit, ANamedRecordDescribesTheFramebufferItNamesWithoutMovingEit
const Uint64 serialBefore = MGPipeApplier().FramebufferSerial; const Uint64 serialBefore = MGPipeApplier().FramebufferSerial;
MGPFramebufferState named = FramebufferRecord(MGPipeHandle{6, 3}, MGPipeFramebufferTarget::Draw, 300); MGPFramebufferState named = FramebufferRecord(MGPipeHandle{6, 3}, MGPipeFramebufferTarget::Draw, 300);
named.Target = kMGPipeFramebufferTargetNamed; named.Target = static_cast<Uint8>(MGPipeFramebufferTarget::Named);
named.Color[0].Res = MGPipeHandle{21, 1}; named.Color[0].Res = MGPipeHandle{21, 1};
MGPipeApplySetFramebufferState(named); MGPipeApplySetFramebufferState(named);
@@ -451,7 +451,7 @@ TEST(FramebufferEmit, ANamedRecordDescribesTheFramebufferItNamesWithoutMovingEit
"clears into a driver framebuffer with no attachments"; "clears into a driver framebuffer with no attachments";
EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Width, 300u); EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Width, 300u);
EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Color[0].Res, (MGPipeHandle{21, 1})); EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Color[0].Res, (MGPipeHandle{21, 1}));
EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Target, kMGPipeFramebufferTargetNamed); EXPECT_EQ(MGPipeApplier().FramebufferRecordFor(MGPipeHandle{6, 3})->Target, static_cast<Uint8>(MGPipeFramebufferTarget::Named));
// (b) AND NEITHER BINDING MOVED. // (b) AND NEITHER BINDING MOVED.
ASSERT_NE(MGPipeApplier().DrawFramebuffer(), nullptr); ASSERT_NE(MGPipeApplier().DrawFramebuffer(), nullptr);