Merge branch 'p5c-rv' into feat/disaggregated (P5c rv: residual-value record, value-class pulls retired)

This commit is contained in:
2026-09-17 09:27:47 -04:00
33 changed files with 899 additions and 278 deletions
+29
View File
@@ -13,6 +13,15 @@
// the live context happens on the client side, in MG_Impl/Pipe/PipeFill.cpp. // the live context happens on the client side, in MG_Impl/Pipe/PipeFill.cpp.
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c (rv, CONTRACT-P5C.md §5.3): the three texture shutters' server-side answer lives in the
// applier - MGPipeApplierTextureShutterSerial() / MGPipeApplierContextSerial(), declared here
// so the accessors below can answer with them under a server-stamped verb. MG_Pipe is below
// MG_Backend, so this direction is the layering's, and PipeApply.h forward-declares
// PipeInputs rather than including this header, so there is no cycle.
#include <MG_Pipe/PipeApply.h>
#endif
// MOBILEGL_PIPE_POISON: the per-verb generation stamps and the read-side // MOBILEGL_PIPE_POISON: the per-verb generation stamps and the read-side
// Fatal{UnmigratedPipeInput} check. Derived here, once. The repository's debug gate is // Fatal{UnmigratedPipeInput} check. Derived here, once. The repository's debug gate is
// MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG (Defines.h); the verify CI build is // MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG (Defines.h); the verify CI build is
@@ -471,9 +480,19 @@ namespace MobileGL::MG_Pipe {
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetRenderStateParameters, 0, 0); MGP_INPUT_VERIFY_READ(MGPipeInputField::GetRenderStateParameters, 0, 0);
return m_renderState; return m_renderState;
} }
// THE THREE TEXTURE SHUTTERS (P5c rv, CONTRACT-P5C.md §5.3). Their FieldOwnership rows
// have said "a shutter, not a value: the server answers from its own Serial" since P5;
// rv is the edit that makes the accessor DO it. Under a SERVER-STAMPED verb the answer
// is the applier's own serial (APPLIER_DERIVED): server-owned, monotone, moved by every
// applied record that can move what the frontend generation guarded. Everywhere else -
// monolith, a split build on monolith transport, any read outside a stamped verb - the
// storage answer is kept, byte for byte (G1).
Uint64 GetSamplingResolutionGeneration() const { Uint64 GetSamplingResolutionGeneration() const {
MGP_INPUT_CHECK(MGPipeInputField::GetSamplingResolutionGeneration); MGP_INPUT_CHECK(MGPipeInputField::GetSamplingResolutionGeneration);
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetSamplingResolutionGeneration, 0, 0); MGP_INPUT_VERIFY_READ(MGPipeInputField::GetSamplingResolutionGeneration, 0, 0);
#if MOBILEGL_BUILD_DISAGGREGATED
if (m_serverStampedVerb) return MGPipeApplierTextureShutterSerial();
#endif
return m_samplingResolutionGeneration; return m_samplingResolutionGeneration;
} }
const IntVec4& GetScissorBox() const { const IntVec4& GetScissorBox() const {
@@ -489,11 +508,21 @@ namespace MobileGL::MG_Pipe {
Uint64 GetTextureBindGeneration() const { Uint64 GetTextureBindGeneration() const {
MGP_INPUT_CHECK(MGPipeInputField::GetTextureBindGeneration); MGP_INPUT_CHECK(MGPipeInputField::GetTextureBindGeneration);
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureBindGeneration, 0, 0); MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureBindGeneration, 0, 0);
#if MOBILEGL_BUILD_DISAGGREGATED
// See GetSamplingResolutionGeneration: the server answers from its own Serial.
if (m_serverStampedVerb) return MGPipeApplierTextureShutterSerial();
#endif
return m_textureBindGeneration; return m_textureBindGeneration;
} }
Uint64 GetTextureContextId() const { Uint64 GetTextureContextId() const {
MGP_INPUT_CHECK(MGPipeInputField::GetTextureContextId); MGP_INPUT_CHECK(MGPipeInputField::GetTextureContextId);
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureContextId, 0, 0); MGP_INPUT_VERIFY_READ(MGPipeInputField::GetTextureContextId, 0, 0);
#if MOBILEGL_BUILD_DISAGGREGATED
// A context IDENTITY rather than a generation: stable within the served context,
// moved by every MGPipeApplierReset - which is all the backends' per-context memo
// keys ask of it.
if (m_serverStampedVerb) return MGPipeApplierContextSerial();
#endif
return m_textureContextId; return m_textureContextId;
} }
Uint64 GetTransformFeedbackCapturedVertices() const { Uint64 GetTransformFeedbackCapturedVertices() const {
+138 -36
View File
@@ -1886,6 +1886,11 @@ namespace MobileGL::MG_Pipe {
case MGPipeFieldEmitter::SetDrawProgram: case MGPipeFieldEmitter::SetDrawProgram:
case MGPipeFieldEmitter::SetDispatchProgram: case MGPipeFieldEmitter::SetDispatchProgram:
return kMGPipeSubsystemPrograms; return kMGPipeSubsystemPrograms;
// P5c rv (CONTRACT-P5C.md §5.3): the residual-value record rides the residual
// subsystem - the one family with no dirty bit of its own, which is why its
// emission gate is the subsystem bit plus the whole-record hash and nothing else.
case MGPipeFieldEmitter::SetContextValues:
return kMGPipeSubsystemResidualValues;
case MGPipeFieldEmitter::kNone: case MGPipeFieldEmitter::kNone:
break; break;
} }
@@ -2062,10 +2067,15 @@ namespace MobileGL::MG_Pipe {
// commit, not at the merge, and no file is touched twice. // commit, not at the merge, and no file is touched twice.
// //
// The sampler bit covers SamplerEmit.h AND ImageEmit.h: one family, one A/B. // The sampler bit covers SamplerEmit.h AND ImageEmit.h: one family, one A/B.
// P5c rv: the residual subsystem joins the wired mask for set_context_values - the
// record's emission is NOT gated on a dirty bit (there is none for the family,
// NoDirtyBitOwnsTheResidualSubsystem says so), so this bit is what the residual-fill
// skip consults for the eight fields the record supplies.
constexpr Uint64 kMGPipeWiredSubsystems = kMGPipeSubsystemRenderState | constexpr Uint64 kMGPipeWiredSubsystems = kMGPipeSubsystemRenderState |
kMGPipeSubsystemPixelPack | kMGPipeSubsystemPixelPack |
kMGPipeSubsystemPatchState | kMGPipeSubsystemPatchState |
kMGPipeSubsystemVertexAttribDefaults | kMGPipeSubsystemVertexAttribDefaults |
kMGPipeSubsystemResidualValues |
kMGPipeSubsystemResources | kMGPipeSubsystemResources |
kMGPipeSubsystemVertexInput | kMGPipeSubsystemVertexInput |
kMGPipeWiredFramebufferSubsystem | kMGPipeWiredFramebufferSubsystem |
@@ -2097,17 +2107,6 @@ namespace MobileGL::MG_Pipe {
// (ARCHITECTURE.md 4.6 D5, MGPipeTypes.h). The unpack half has no carrier at all, // (ARCHITECTURE.md 4.6 D5, MGPipeTypes.h). The unpack half has no carrier at all,
// so the field keeps being pulled and the verify comparator keeps proving it. // so the field keeps being pulled and the verify comparator keeps proving it.
// //
// GetCurrentVertexAttribute's three views are NOT bit-identical: GLContext
// CONVERTS between them (SetCurrentVertexAttributeFloat writes (Int32)value into
// intValue), while MGPipeApplySetVertexAttribDefaults (package A's) memcpys one
// Data[4] into all three views and ignores MGPAttribValue::ValueClass. The CLIENT
// half of that is fixed - the call now carries the class the frontend actually
// wrote and that class's own bytes - but the APPLIER still cannot reproduce the
// conversion, so this row stays SHAPE-ONLY: the field keeps being pulled, and
// retiring that pull is blocked on A teaching the applier to switch on
// ValueClass. EmitVertexAttribDefaults checks rather than trusts, and repairs the
// mirror when the applier's write does not reproduce the value.
//
// GetBoundVertexArray is P3a's row, and Coverage.def asks for the decision to be // GetBoundVertexArray is P3a's row, and Coverage.def asks for the decision to be
// taken HERE, deliberately, rather than inherited from the row's presence. THE // taken HERE, deliberately, rather than inherited from the row's presence. THE
// ANSWER IS NO, and it is not a matter of degree: the field's storage is a // ANSWER IS NO, and it is not a matter of degree: the field's storage is a
@@ -2137,25 +2136,21 @@ namespace MobileGL::MG_Pipe {
// null on every draw of every push build. What retires them is not a better // null on every draw of every push build. What retires them is not a better
// applier, it is the phase where the backend stops reading a frontend object. // applier, it is the phase where the backend stops reading a frontend object.
// //
// GetMaxTouchedTextureUnit is the sixth and its argument is different, which is // GetMaxTouchedTextureUnit was the sixth and its argument was different - a plain
// why it is written out: it is a plain Int, and set_sampler_views' Count IS that // Int whose carrier (set_sampler_views' Count) is hash-suppressed while the
// value plus one. But the set is SUPPRESSED on an unchanged content hash and is // high-water mark still moves on a redundant re-bind. P5c rv RETIRED it from this
// emitted only when NEW_SAMPLER_VIEWS fires, and that bit's shutter - // list (CONTRACT-P5C.md §5.3): set_context_values carries the mark as a VALUE of
// Mix(textureContent, GetTextureBindGeneration()) - does NOT move on a redundant // its own, whole-record suppressed, so the lag the suppressor could introduce is
// re-bind of the object a unit already holds, while the high-water mark DOES. So // gone and the derivation's RECORD_SUPPLIED answer is honest.
// the applier's Count can lag the frontend's mark by exactly the case the
// suppressor exists to swallow, and the field keeps being pulled.
constexpr Bool EmittedCallSuppliesTheWholeField(MGPipeInputField field) { constexpr Bool EmittedCallSuppliesTheWholeField(MGPipeInputField field) {
switch (field) { switch (field) {
case MGPipeInputField::GetPixelStoreParameters: case MGPipeInputField::GetPixelStoreParameters:
case MGPipeInputField::GetCurrentVertexAttribute:
case MGPipeInputField::GetBoundVertexArray: case MGPipeInputField::GetBoundVertexArray:
case MGPipeInputField::GetFramebufferBindingSlot: case MGPipeInputField::GetFramebufferBindingSlot:
case MGPipeInputField::GetImageTextureBinding: case MGPipeInputField::GetImageTextureBinding:
case MGPipeInputField::GetTextureUnitObject: case MGPipeInputField::GetTextureUnitObject:
case MGPipeInputField::GetProgramForDraw: case MGPipeInputField::GetProgramForDraw:
case MGPipeInputField::GetProgramForDispatch: case MGPipeInputField::GetProgramForDispatch:
case MGPipeInputField::GetMaxTouchedTextureUnit:
return false; return false;
default: default:
return true; return true;
@@ -2175,6 +2170,16 @@ namespace MobileGL::MG_Pipe {
case MGPipeInputField::GetPatchDefaultOuterLevel: case MGPipeInputField::GetPatchDefaultOuterLevel:
case MGPipeInputField::GetPatchDefaultInnerLevel: case MGPipeInputField::GetPatchDefaultInnerLevel:
case MGPipeInputField::GetCurrentVertexAttribute: case MGPipeInputField::GetCurrentVertexAttribute:
// P5c rv's eight: MGPipeApplySetContextValues writes them out of the record,
// field for field, through MGPipeApplyAccess::SetContextValues.
case MGPipeInputField::GetActiveTextureUnit:
case MGPipeInputField::GetMaxTouchedTextureUnit:
case MGPipeInputField::GetTouchedBufferBindingPointCount:
case MGPipeInputField::IsTransformFeedbackActive:
case MGPipeInputField::IsTransformFeedbackPaused:
case MGPipeInputField::GetTransformFeedbackGeneration:
case MGPipeInputField::GetBoundTransformFeedbackLifetimeId:
case MGPipeInputField::GetTransformFeedbackCapturedVertices:
return true; return true;
default: default:
return false; return false;
@@ -2256,15 +2261,18 @@ namespace MobileGL::MG_Pipe {
// when the hash has not moved. That is coalescing rule 4, and this is its one wired // when the hash has not moved. That is coalescing rule 4, and this is its one wired
// consumer in P2. // consumer in P2.
// //
// THE PAYLOAD AND ITS ONE MISSING HALF. A CurrentVertexAttributeValue is one value in // THE PAYLOAD, SINCE P5c rv (CONTRACT-P5C.md §5.3). A CurrentVertexAttributeValue is
// three views, and GLContext CONVERTS between them numerically, so "the bytes of one // one value in three views, and GLContext CONVERTS between them numerically, so "the
// view" is not the value: glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and // bytes of one view" is not the value: glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in
// 0x3FC00000 in floatValue, and every glVertexAttribI4i/ui is a different pair again. // intValue and 0x3FC00000 in floatValue, and every glVertexAttribI4i/ui is a different
// MGPAttribValue carries ValueClass for exactly this reason, so the client sends the // pair again. MGPAttribValue now carries all three views VERBATIM
// class the frontend actually wrote (GLContext::GetCurrentVertexAttributeClass) and // (FloatView/IntView/UintView) plus the class the frontend actually wrote
// THAT class's own four words. What is still missing is the other half: // (GLContext::GetCurrentVertexAttributeClass), and MGPipeApplySetVertexAttribDefaults
// MGPipeApplySetVertexAttribDefaults (package A's file) memcpys the four words into // writes each view from its own array - the cross-view conversion's authoritative
// all three views regardless of ValueClass, which cannot reproduce the conversion. // answer is the client's, and the applier no longer reconverts anything. The pre-rv
// shape (one Data[4] memcpied into all three views, ValueClass ignored) is exactly
// what kept this row EMITTED-AND-STILL-PULLED in EmittedCallSuppliesTheWholeField;
// with the applier fixed, the field is RECORD_SUPPLIED outright.
// //
// The suppressing memcmp below is over the three VIEWS only, and that is not an // The suppressing memcmp below is over the three VIEWS only, and that is not an
// oversight: the class decides how the views are REBUILT, so two writes that leave // oversight: the class decides how the views are REBUILT, so two writes that leave
@@ -2277,8 +2285,9 @@ namespace MobileGL::MG_Pipe {
// and says so once. That is what keeps the block correct in the window this call used // and says so once. That is what keeps the block correct in the window this call used
// to corrupt - a glVertexAttrib4f followed by a non-kDraw verb, where the residual // to corrupt - a glVertexAttrib4f followed by a non-kDraw verb, where the residual
// fill does not run for this field and nothing else would have put the value back. // fill does not run for this field and nothing else would have put the value back.
// The day the applier honours ValueClass the compare stops failing and the repair // Since rv the applier writes all three views verbatim, so the compare below is
// stops happening, with no edit here. // expected to pass on every emission; it stays armed because it is the one observable
// of that write being verbatim in a window no other gate looks at.
Uint64 g_attribDefaultRepairs = 0; Uint64 g_attribDefaultRepairs = 0;
// The header of the last set_vertex_attrib_defaults that actually went out. Count == 0 // The header of the last set_vertex_attrib_defaults that actually went out. Count == 0
@@ -2349,14 +2358,73 @@ namespace MobileGL::MG_Pipe {
} }
if (!reproduced) { if (!reproduced) {
++g_attribDefaultRepairs; ++g_attribDefaultRepairs;
MGLOG_W_ONCE("MGPipe: MGPipeApplySetVertexAttribDefaults does not reproduce the " MGLOG_W_ONCE("MGPipe: MGPipeApplySetVertexAttribDefaults did not reproduce the "
"carried value on this build (it ignores MGPAttribValue::ValueClass) " "carried three views on this build - the client is keeping "
"- the client is keeping m_currentVertexAttribute authoritative"); "m_currentVertexAttribute authoritative");
MGPipeFillAccess::CopyField(gPipeInputs, ctx, MGPipeInputField::GetCurrentVertexAttribute); MGPipeFillAccess::CopyField(gPipeInputs, ctx, MGPipeInputField::GetCurrentVertexAttribute);
} }
return sizeof(MGPVertexAttribDefaults) + header.Count * sizeof(MGPAttribValue); return sizeof(MGPVertexAttribDefaults) + header.Count * sizeof(MGPAttribValue);
} }
#if MOBILEGL_BUILD_DISAGGREGATED
// set_context_values (P5c rv, CONTRACT-P5C.md §5.3): the residual-value record. One
// POD carrying every value-class field no other set_* supplies - the two texture-unit
// counters, the 15 per-target touched-buffer-binding counts and the five XFB values -
// emitted at validate WHEN ANY COVERED VALUE MOVED, which the whole-record hash says:
// there is deliberately no dirty bit for the family (the tracker's value-class dirty
// accounting is untouched - "零新增记账", ARCHITECTURE.md 5.2) and no dirty mask in the
// payload, so a suppressed record means "nothing moved", never "field invalid" (§1).
//
// THE PRODUCER IS TRANSPORT-GATED, and the residual-fill skip for the same eight fields
// is gated on the same answer (MGPipeValidateForVerb's contextValuesWireLive): under
// monolith - or with a transport configured but no live session (the bring-up window, a
// server-role-only fixture) - nothing is emitted and the fields keep being pulled, byte
// for byte as before (G1).
Uint64 EmitContextValues(GLContext& ctx) {
MGPContextValues values{};
values.ActiveTextureUnit = static_cast<Uint32>(ctx.GetActiveTextureUnit());
values.MaxTouchedTextureUnit = static_cast<Uint32>(ctx.GetMaxTouchedTextureUnit());
// The array is indexed by BufferTarget value, all 15 of them (MGPipeTypes.h);
// targets with no binding points answer 0 (BufferState::GetTouchedBindPointCount).
static_assert(
std::extent_v<decltype(MGPContextValues::TouchedBufferBindingPointCount)> ==
static_cast<SizeT>(BufferTarget::BufferTargetCount),
"MGPContextValues' per-target array and BufferTargetCount have drifted");
for (Uint32 t = 0; t < static_cast<Uint32>(BufferTarget::BufferTargetCount); ++t) {
values.TouchedBufferBindingPointCount[t] =
static_cast<Uint32>(ctx.GetTouchedBufferBindingPointCount(static_cast<BufferTarget>(t)));
}
values.IsTransformFeedbackActive = ctx.IsTransformFeedbackActive() ? 1 : 0;
values.IsTransformFeedbackPaused = ctx.IsTransformFeedbackPaused() ? 1 : 0;
values.TransformFeedbackGeneration = ctx.GetTransformFeedbackGeneration();
values.BoundTransformFeedbackLifetimeId = ctx.GetBoundTransformFeedbackLifetimeId();
values.TransformFeedbackCapturedVertices = ctx.GetTransformFeedbackCapturedVertices();
// The whole record is the hash input, padding included - `values{}` zeroes it, so
// the pad bytes are defined and the hash is stable.
const Uint64 contentHash = XXH64(&values, sizeof(values), 0);
if (!MGPipeSetHashSuppressorInstance().ShouldEmit(MGPipeSuppressorSlot::SetContextValues,
contentHash)) {
return 0;
}
MGPipeRouteSetContextValues(values);
return sizeof(MGPContextValues);
}
// The fields set_context_values supplies. A validate whose verb class reads NONE of
// them skips the record build entirely - the record exists so a verb's reads are
// answered, and a verb that never reads them needs no publication.
constexpr Bool VerbMaskReadsContextValues(const MGPipeFieldMask& mask) {
return MGPipeFieldMaskHas(mask, MGPipeInputField::GetActiveTextureUnit) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetMaxTouchedTextureUnit) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetTouchedBufferBindingPointCount) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::IsTransformFeedbackActive) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::IsTransformFeedbackPaused) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetTransformFeedbackGeneration) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetBoundTransformFeedbackLifetimeId) ||
MGPipeFieldMaskHas(mask, MGPipeInputField::GetTransformFeedbackCapturedVertices);
}
#endif
// set_residual_value_state (P2 brief D9, ARCHITECTURE.md 9.4). // set_residual_value_state (P2 brief D9, ARCHITECTURE.md 9.4).
// //
@@ -2669,6 +2737,19 @@ namespace MobileGL::MG_Pipe {
// emission the server refuses is an emission whose acceptance already cleared a frontend // emission the server refuses is an emission whose acceptance already cleared a frontend
// dirty flag the legacy arm still owed. Same table, same four families, one place. // dirty flag the legacy arm still owed. Same table, same four families, one place.
const Uint64 pushMask = MG_Config::Features.PipePush; const Uint64 pushMask = MG_Config::Features.PipePush;
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c rv (CONTRACT-P5C.md §5.3): set_context_values is the carrier for the eight
// value-class fields ONLY with a live wire. ONE answer gates both halves - the emission
// below and the residual-fill skip in step 4 - so they can never disagree about who
// supplies a field: with no live session (a monolith transport, the bring-up window, a
// server-role-only fixture) nothing is emitted and the fields keep being pulled, byte
// for byte as before (G1).
const Bool contextValuesWireLive =
MG_Config::Transport != MG_Config::TransportMode::Monolith &&
MG_Remote::Client::ContextValuesWireLive();
#else
constexpr Bool contextValuesWireLive = false;
#endif
const auto wants = [&](MGPipeDirty bit) { const auto wants = [&](MGPipeDirty bit) {
const Uint64 subsystem = MGPipeSubsystemForDirty(bit); const Uint64 subsystem = MGPipeSubsystemForDirty(bit);
return subsystem != 0 && (pushMask & subsystem) != 0 && return subsystem != 0 && (pushMask & subsystem) != 0 &&
@@ -2808,6 +2889,19 @@ namespace MobileGL::MG_Pipe {
payloadBytes += EmitVertexAttribDefaults(*ctx, tracker.FreshlyPrimed()); payloadBytes += EmitVertexAttribDefaults(*ctx, tracker.FreshlyPrimed());
} }
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c rv (CONTRACT-P5C.md §5.3): the residual-value record, emitted when any covered
// value moved. THE GATE IS THE SUBSYSTEM BIT PLUS THE WIRE BEING LIVE - the family has
// no dirty bit (NoDirtyBitOwnsTheResidualSubsystem) and no P4a consumer predicate (it
// is not one of the four families), and the "did anything move" question is the
// whole-record hash inside EmitContextValues. The class-mask test skips the build for
// verbs that read none of the eight fields.
if (contextValuesWireLive && (pushMask & kMGPipeSubsystemResidualValues) != 0 &&
VerbMaskReadsContextValues(mask)) {
payloadBytes += EmitContextValues(*ctx);
}
#endif
// P3a's vertex segment, in the order the design fixes: vertex elements, then the // P3a's vertex segment, in the order the design fixes: vertex elements, then the
// vertex buffers that fill them, then the index binding. All three are LIVE now (m1): // vertex buffers that fill them, then the index binding. All three are LIVE now (m1):
// bits 5 / 9 / 10 map onto kMGPipeSubsystemVertexInput in Tracker.h:145-148 and all // bits 5 / 9 / 10 map onto kMGPipeSubsystemVertexInput in Tracker.h:145-148 and all
@@ -2857,12 +2951,20 @@ namespace MobileGL::MG_Pipe {
// FIELD, and on a backend with no consumer - or at a mask that leaves one of the // FIELD, and on a backend with no consumer - or at a mask that leaves one of the
// family's D-K2 dependency bits clear - no P4a call went out at all, so withholding // family's D-K2 dependency bits clear - no P4a call went out at all, so withholding
// the pull here would leave the field unfilled at the very verb that reads it. // the pull here would leave the field unfilled at the very verb that reads it.
//
// AND THE LAST CONJUNCT IS P5c rv's (CONTRACT-P5C.md §5.3): set_context_values has
// NO PRODUCER without a live wire (its emission is transport-gated, above), so its
// eight fields keep being pulled under monolith - G1's byte-for-byte rule - and are
// skipped only when the record really crosses. The two sides read the ONE answer
// computed at the top of this function, so they cannot disagree.
const Bool supplied = subsystem != 0 && (subsystem & kMGPipeWiredSubsystems) != 0 && const Bool supplied = subsystem != 0 && (subsystem & kMGPipeWiredSubsystems) != 0 &&
(pushMask & subsystem) != 0 && (pushMask & subsystem) != 0 &&
P4aFamilyHasItsConsumer(subsystem) && P4aFamilyHasItsConsumer(subsystem) &&
P4aFamilyDependenciesAreSet(subsystem, pushMask) && P4aFamilyDependenciesAreSet(subsystem, pushMask) &&
EmittedCallSuppliesTheWholeField(field) && EmittedCallSuppliesTheWholeField(field) &&
(applierDerives || AppliedWithoutDerivation(field)); (applierDerives || AppliedWithoutDerivation(field)) &&
(emitter != MGPipeFieldEmitter::SetContextValues ||
contextValuesWireLive);
if (!supplied) MGPipeFillAccess::CopyField(inputs, *ctx, field); if (!supplied) MGPipeFillAccess::CopyField(inputs, *ctx, field);
#if MOBILEGL_PIPE_POISON #if MOBILEGL_PIPE_POISON
// The value is copied either way; only the stamp is withheld for the omitted pair. // The value is copied either way; only the stamp is withheld for the omitted pair.
+4 -2
View File
@@ -113,8 +113,10 @@ namespace MobileGL::MG_Pipe {
// (EmitVertexAttribDefaults). It is the ONE observable of that repair: the window it // (EmitVertexAttribDefaults). It is the ONE observable of that repair: the window it
// covers is a verb whose class does not read m_currentVertexAttribute, where reading the // covers is a verb whose class does not read m_currentVertexAttribute, where reading the
// storage to check it would be the poison violation the fill table exists to forbid. So // storage to check it would be the poison violation the fill table exists to forbid. So
// TrackerShippedEmitter asserts on this counter instead, and the day package A's applier // TrackerShippedEmitter asserts on this counter instead. Since P5c rv the record carries
// switches on MGPAttribValue::ValueClass the counter stops moving. // all three views verbatim (CONTRACT-P5C.md §5.3) and the applier writes each from its own
// array, so the counter is expected to stay at 0 - the check that increments it is the
// trip wire that remains.
// //
// Not hot-path instrumentation: it is incremented only inside the repair branch, which // Not hot-path instrumentation: it is incremented only inside the repair branch, which
// runs only when the call actually went out, which is only when an attribute default // runs only when the call actually went out, which is only when an attribute default
+9 -1
View File
@@ -45,7 +45,9 @@ namespace MobileGL::MG_Pipe {
// One slot per kVarTail set_* (ARCHITECTURE.md 5.1's call list), PLUS // One slot per kVarTail set_* (ARCHITECTURE.md 5.1's call list), PLUS
// SetFramebufferState, which is not kVarTail at all: MGPFramebufferState carries a // SetFramebufferState, which is not kVarTail at all: MGPFramebufferState carries a
// ContentHash for TWO jobs - the server's render-pass memo key and the client's emission // ContentHash for TWO jobs - the server's render-pass memo key and the client's emission
// suppressor - and the second one needs a slot here like any other. The enum is // suppressor - and the second one needs a slot here like any other, PLUS
// SetContextValues (P5c rv), also not kVarTail: one fixed-width POD whose whole-record
// hash is the "did any covered value move" answer. The enum is
// CLIENT-ONLY and is not a wire opcode, so appending before Count is safe. // CLIENT-ONLY and is not a wire opcode, so appending before Count is safe.
enum class MGPipeSuppressorSlot : Uint32 { enum class MGPipeSuppressorSlot : Uint32 {
SetVertexBuffers = 0, // P3a - wired, and its hash includes BaseInstance SetVertexBuffers = 0, // P3a - wired, and its hash includes BaseInstance
@@ -64,6 +66,12 @@ namespace MobileGL::MG_Pipe {
SetStreamOutputTargets, // P4b SetStreamOutputTargets, // P4b
SetVertexAttribDefaults, // P2 - the one consumer that is wired SetVertexAttribDefaults, // P2 - the one consumer that is wired
SetFramebufferState, // P4a - wired SetFramebufferState, // P4a - wired
// P5c rv (CONTRACT-P5C.md §5.3). NOT kVarTail either - the same shape as
// SetFramebufferState's note: MGPContextValues is one fixed-width POD, and the
// whole-record hash IS its "did any covered value move" answer (there is deliberately
// no dirty mask in the payload - a suppressed record means "nothing moved", never
// "field invalid").
SetContextValues, // P5c rv - wired, split+transport only (PipeFill.cpp gates)
Count, Count,
}; };
+18 -19
View File
@@ -821,31 +821,30 @@ namespace MobileGL::MG_Pipe {
Uint64 m_walks[kMGPipeVerbClassCount]{}; Uint64 m_walks[kMGPipeVerbClassCount]{};
}; };
// ONE attribute default, flattened onto the wire (P2 brief D10). A named function rather // ONE attribute default, flattened onto the wire (P2 brief D10, AMENDED at P5c rv). A
// than four lines inside the emitter because this flattening is the whole correctness // named function rather than four lines inside the emitter because this flattening is the
// question of set_vertex_attrib_defaults: a CurrentVertexAttributeValue is one value in // whole correctness question of set_vertex_attrib_defaults: a CurrentVertexAttributeValue
// three views and GLContext converts NUMERICALLY between them, so four words alone are // is one value in three views and GLContext converts NUMERICALLY between them, so four
// not the value - glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in // words alone are not the value - glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue
// floatValue. MGPAttribValue::ValueClass is what makes the four words readable again, and // and 0x3FC00000 in floatValue. Since rv the record carries ALL THREE VIEWS VERBATIM
// TrackerAttribPayload pins that here instead of leaving it to the emitter's shape. // (MGPAttribValue::FloatView/IntView/UintView, CONTRACT-P5C.md §5.3) and the applier writes
// each view from its own array; ValueClass is the record of which view the application
// wrote directly, kept for the comparator and for readers - the applier no longer needs
// it to rebuild anything.
inline void MGPipeFillAttribValue(Uint32 location, inline void MGPipeFillAttribValue(Uint32 location,
const MG_State::GLState::CurrentVertexAttributeValue& value, const MG_State::GLState::CurrentVertexAttributeValue& value,
Uint32 writtenClass, MGPAttribValue& out) { Uint32 writtenClass, MGPAttribValue& out) {
out = MGPAttribValue{}; out = MGPAttribValue{};
out.Location = location; out.Location = location;
out.ValueClass = static_cast<Uint8>(writtenClass); out.ValueClass = static_cast<Uint8>(writtenClass);
static_assert(sizeof(out.Data) == sizeof(value.floatValue), "MGPAttribValue::Data is four words"); static_assert(sizeof(out.FloatView) == sizeof(value.floatValue),
switch (writtenClass) { "MGPAttribValue's views are four words each");
case MG_State::GLState::kVertexAttribValueClassInt: static_assert(sizeof(out.IntView) == sizeof(value.intValue) &&
std::memcpy(out.Data, value.intValue.data(), sizeof(out.Data)); sizeof(out.UintView) == sizeof(value.uintValue),
break; "MGPAttribValue's views are four words each");
case MG_State::GLState::kVertexAttribValueClassUint: std::memcpy(out.FloatView, value.floatValue.data(), sizeof(out.FloatView));
std::memcpy(out.Data, value.uintValue.data(), sizeof(out.Data)); std::memcpy(out.IntView, value.intValue.data(), sizeof(out.IntView));
break; std::memcpy(out.UintView, value.uintValue.data(), sizeof(out.UintView));
default:
std::memcpy(out.Data, value.floatValue.data(), sizeof(out.Data));
break;
}
} }
// The monolith's one tracker. Under split there is one per client context; the context // The monolith's one tracker. Under split there is one per client context; the context
+29 -21
View File
@@ -212,33 +212,36 @@
// mirrors null on every draw of every push build. What retires those pulls is not a better // mirrors null on every draw of every push build. What retires those pulls is not a better
// applier, it is the phase where the backend stops reading a frontend object at all. // applier, it is the phase where the backend stops reading a frontend object at all.
// //
// THE SIXTH IS A DIFFERENT ARGUMENT AND IT IS WORTH WRITING DOWN, because it looks like the // THE SIXTH left that arm at P5c rv: GetMaxTouchedTextureUnit is a plain Int whose problem was
// easy one. GetMaxTouchedTextureUnit is a plain Int, and set_sampler_views' Count IS that // never shape, it was the CARRIER - set_sampler_views' Count is the set's window, and the set
// value plus one (the second merge rule: a high-water mark is directly the count argument). // is suppressed on an unchanged content hash while the high-water mark still moves on a
// But the set is SUPPRESSED on an unchanged content hash and is emitted only when bit 12 fires, // redundant re-bind. set_context_values (CONTRACT-P5C.md §5.3) carries the mark as a VALUE of
// and bit 12's shutter is Mix(textureContent, GetTextureBindGeneration()) - which does NOT // its own, whole-record suppressed, so the mark rides an unsuppressed field and the row is
// move on a redundant re-bind of the object a unit already holds, while the high-water mark // RECORD_SUPPLIED outright.
// DOES (see NoteUnitTouched in DirtySurface.def). So the applier's Count can lag the frontend's
// high-water mark by exactly the case the suppressor exists to swallow, and the field keeps
// being pulled. Narrowing that is P3b/P4b's, with the backend debounce it takes over.
// //
// FOUR ACCESSORS THAT MAP TO A P4a CALL ARE DELIBERATELY NOT HERE, for GetPixelStoreParameters' // P5c rv ADDS EIGHT ROWS, and they are the whole of the value-class debt that was left
// reason - a row here says "this field is supplied", and for these it would be a half-truth: // (CONTRACT-P5C.md §5.3): seven residual values no set_* call carried - the two texture-unit
// GetActiveTextureUnit - glActiveTexture's selector. set_sampler_views carries the RESOLVED // counters, the 15-target touched-count array and the five transform-feedback values - plus
// per-unit set and no active-unit selector at all; nothing on the wire carries it. // GetMaxTouchedTextureUnit re-homed from SetSamplerViews. All eight name SetContextValues, one
// GetTextureContextId - a context identity the backend keys its own tables on. No call // POD emitted at validate when any covered value moved, hash-suppressed whole. The OBJECT rows
// carries it and none should: it is the server's question about the client, not state. // (GetTextureUnitObject and its four P4a siblings above) stay BARRIER_PULLED with their
// GetTextureBindGeneration / GetSamplingResolutionGeneration - frontend SHUTTERS. What // retiring phases; the three texture shutters (below) take no row here either - they carry no
// replaces them server-side is the applier's own Serial, which is a different value with a // wire value at all, the accessors answer the applier's own Serial.
// different owner; claiming the sets supply the generations would make the fill loop skip //
// two counters no record carries. // GetTextureContextId / GetTextureBindGeneration / GetSamplingResolutionGeneration stay out
// DELIBERATELY, and the reason is ownership rather than coverage: they are frontend SHUTTERS,
// and what replaces them server-side is the applier's own Serial - a different value with a
// different owner. A row here would claim the sets supply the generations, and the fill loop
// would skip two counters no record carries.
// And GetTextureObject / GetProgramObject are STICKY (see MGP_COVERAGE_STICKY_LIST): they are // And GetTextureObject / GetProgramObject are STICKY (see MGP_COVERAGE_STICKY_LIST): they are
// keyed by GL name, they are object lookups rather than verb state, and a forwarded field has // keyed by GL name, they are object lookups rather than verb state, and a forwarded field has
// no storage for an emitted call to supply. // no storage for an emitted call to supply.
#define MGP_COVERAGE_EMITTED_LIST(X) \ #define MGP_COVERAGE_EMITTED_LIST(X) \
X(GetActiveTextureUnit, SetContextValues) \
X(GetBlendColor, SetDynamicState) \ X(GetBlendColor, SetDynamicState) \
X(GetBlendEquationIndexed, CreateRenderState) \ X(GetBlendEquationIndexed, CreateRenderState) \
X(GetBlendFuncIndexed, CreateRenderState) \ X(GetBlendFuncIndexed, CreateRenderState) \
X(GetBoundTransformFeedbackLifetimeId, SetContextValues) \
X(GetBoundVertexArray, BindVertexElements) \ X(GetBoundVertexArray, BindVertexElements) \
X(GetClampReadColor, SetDynamicState) \ X(GetClampReadColor, SetDynamicState) \
X(GetClearColor, SetDynamicState) \ X(GetClearColor, SetDynamicState) \
@@ -254,7 +257,7 @@
X(GetImageTextureBinding, SetShaderImages) \ X(GetImageTextureBinding, SetShaderImages) \
X(GetLineWidth, SetDynamicState) \ X(GetLineWidth, SetDynamicState) \
X(GetLogicOp, CreateRenderState) \ X(GetLogicOp, CreateRenderState) \
X(GetMaxTouchedTextureUnit, SetSamplerViews) \ X(GetMaxTouchedTextureUnit, SetContextValues) \
X(GetMinSampleShadingValue, CreateRenderState) \ X(GetMinSampleShadingValue, CreateRenderState) \
X(GetPatchDefaultInnerLevel, SetPatchState) \ X(GetPatchDefaultInnerLevel, SetPatchState) \
X(GetPatchDefaultOuterLevel, SetPatchState) \ X(GetPatchDefaultOuterLevel, SetPatchState) \
@@ -272,9 +275,14 @@
X(GetScissorBox, SetDynamicState) \ X(GetScissorBox, SetDynamicState) \
X(GetStencilState, CreateRenderState) \ X(GetStencilState, CreateRenderState) \
X(GetTextureUnitObject, SetSamplerViews) \ X(GetTextureUnitObject, SetSamplerViews) \
X(GetTouchedBufferBindingPointCount, SetContextValues) \
X(GetTransformFeedbackCapturedVertices, SetContextValues) \
X(GetTransformFeedbackGeneration, SetContextValues) \
X(GetViewport, SetDynamicState) \ X(GetViewport, SetDynamicState) \
X(GetViewportIndexed, SetDynamicState) \ X(GetViewportIndexed, SetDynamicState) \
X(IsCapabilityEnabled, CreateRenderState) \ X(IsCapabilityEnabled, CreateRenderState) \
X(IsCapabilityEnabledIndexed, CreateRenderState) X(IsCapabilityEnabledIndexed, CreateRenderState) \
X(IsTransformFeedbackActive, SetContextValues) \
X(IsTransformFeedbackPaused, SetContextValues)
// clang-format on // clang-format on
+32 -42
View File
@@ -18,7 +18,8 @@
// the client for it. THIS CLASS IS DERIVED, NOT LISTED: it is // the client for it. THIS CLASS IS DERIVED, NOT LISTED: it is
// kMGPipeFieldEmittedBy != kNone (Coverage.def's MGP_COVERAGE_EMITTED_LIST) // kMGPipeFieldEmittedBy != kNone (Coverage.def's MGP_COVERAGE_EMITTED_LIST)
// minus the fields EmittedCallSuppliesTheWholeField refuses // minus the fields EmittedCallSuppliesTheWholeField refuses
// (MG_Impl/Pipe/PipeFill.cpp). 32 fields today. A row below that names a // (MG_Impl/Pipe/PipeFill.cpp). 41 fields since P5c rv (32 before it). A row
// below that names a
// field the derivation already placed here is a CONTRADICTION and stops // field the derivation already placed here is a CONTRADICTION and stops
// the generator - which is the only way this file can stay true as the // the generator - which is the only way this file can stay true as the
// emitted list grows. // emitted list grows.
@@ -50,14 +51,20 @@
// whose row stops the pull. It is a string, not an enum, because two of them name two // whose row stops the pull. It is a string, not an enum, because two of them name two
// phases for the two backends and flattening that would lose the half that matters. // phases for the two backends and flattening that would lose the half that matters.
#define MGP_FIELD_OWNERSHIP_LIST(X) \ #define MGP_FIELD_OWNERSHIP_LIST(X) \
/* ---- BARRIER_PULLED: the 20 non-sticky rows the reduced path actually reads ---- */ \ /* ---- BARRIER_PULLED: the 9 non-sticky rows the reduced path actually reads ---- */ \
/* scout-unmigrated-census section 3 intersects each verb class's may-read mask with "no */ \ /* scout-unmigrated-census section 3 intersects each verb class's may-read mask with "no */ \
/* record supplies it" and unions kClear (7 of 18), kDraw (19 of 47) and kReadback (12 of */ \ /* record supplies it" and unions kClear (7 of 18), kDraw (19 of 47) and kReadback (12 of */ \
/* 17). That union is 21 fields; GetPixelStoreParameters is the 21st and it is */ \ /* 17). That union was 21 fields at P5; P5c rv (CONTRACT-P5C.md §5.3) retired NINE of them */ \
/* APPLIER_DERIVED below, for the reason written there. OpenRA adds no field to this set - */ \ /* to RECORD_SUPPLIED through set_context_values (the two texture-unit counters, the */ \
/* it widens the SITE set, not the field set. */ \ /* touched-count array, the five XFB values) plus GetCurrentVertexAttribute (the amended */ \
X(GetActiveTextureUnit, BARRIER_PULLED, "P3b/P4b", \ /* set_vertex_attrib_defaults payload), and moved the three texture SHUTTERS to */ \
"the server answers from its own state; Coverage.def:215-219 says no call carries it") \ /* APPLIER_DERIVED (their own rows said it since P5: "a shutter, not a value: the server */ \
/* answers from its own Serial" - rv made the accessor DO it). GetPixelStoreParameters is */ \
/* the 21st of the census and it is APPLIER_DERIVED below, for the reason written there. */ \
/* WHAT IS LEFT IS EXACTLY THE OBJECT CLASS: nine fields whose storage is a frontend heap */ \
/* reference no record can carry, each with the phase that retires it, and */ \
/* FieldOwnershipTest pins this list as ALL the remaining non-sticky rows. OpenRA adds no */ \
/* field to this set - it widens the SITE set, not the field set. */ \
/* DirectGLES.cpp:4486, PrepareForDraw, UNCONDITIONAL ON EVERY DRAW. No #if, no arm guard, */ \ /* DirectGLES.cpp:4486, PrepareForDraw, UNCONDITIONAL ON EVERY DRAW. No #if, no arm guard, */ \
/* no record fallback, and PipeFill.cpp:1902-1905 says outright that what retires the pull */ \ /* no record fallback, and PipeFill.cpp:1902-1905 says outright that what retires the pull */ \
/* is P8, not a better applier: the storage is a SharedPtr<VertexArrayObject> and */ \ /* is P8, not a better applier: the storage is a SharedPtr<VertexArrayObject> and */ \
@@ -72,14 +79,6 @@
"7 of 15 BufferTargets have no call; the field is one array over all 15") \ "7 of 15 BufferTargets have no call; the field is one array over all 15") \
X(GetBufferBindingPoint, BARRIER_PULLED, "P3b/P4b, P7", \ X(GetBufferBindingPoint, BARRIER_PULLED, "P3b/P4b, P7", \
"frontend BindingSlotRange1D pointer") \ "frontend BindingSlotRange1D pointer") \
X(GetTouchedBufferBindingPointCount, BARRIER_PULLED, "P3b/P4b", \
"no call carries the touched-count high-water mark") \
/* The applier CANNOT reproduce GLContext's cross-view conversion: the frontend writes */ \
/* (Int32)value into intValue while MGPipeApplySetVertexAttribDefaults memcpys one Data[4] */ \
/* into all three views (PipeFill.cpp:1884-1896). So the record exists, the applier writes */ \
/* the field, and the value is still wrong - which is BARRIER_PULLED, not APPLIER_DERIVED. */ \
X(GetCurrentVertexAttribute, BARRIER_PULLED, "P3b/P4b", \
"the applier writes it but cannot reproduce the cross-view conversion") \
/* 8 Espryt sites through GetFramebufferBindingSlotChecked + 13 Magma. SyncCurrentFBO */ \ /* 8 Espryt sites through GetFramebufferBindingSlotChecked + 13 Magma. SyncCurrentFBO */ \
/* (:2995) is SELF-DECLARED monolith glue (DirectGLES.cpp:2961-2965) while BindCurrentFBO */ \ /* (:2995) is SELF-DECLARED monolith glue (DirectGLES.cpp:2961-2965) while BindCurrentFBO */ \
/* (:4303-4353) is already split-clean - the bind is migrated, the sync is not. */ \ /* (:4303-4353) is already split-clean - the bind is migrated, the sync is not. */ \
@@ -87,19 +86,6 @@
"frontend BindingSlot<FramebufferObject> pointer") \ "frontend BindingSlot<FramebufferObject> pointer") \
X(GetImageTextureBinding, BARRIER_PULLED, "P3b/P4b, P7", \ X(GetImageTextureBinding, BARRIER_PULLED, "P3b/P4b, P7", \
"frontend ImageTextureBinding base pointer") \ "frontend ImageTextureBinding base pointer") \
/* set_sampler_views' Count IS this value plus one, but the set is suppressed on an */ \
/* unchanged content hash while the high-water mark still moves (PipeFill.cpp:1917-1923). */ \
X(GetMaxTouchedTextureUnit, BARRIER_PULLED, "P3b/P4b", \
"hash-suppressed set, high-water mark still moves") \
/* The three texture SHUTTERS. Coverage.def:220-224 is explicit that no call carries them */ \
/* and none should: what replaces them server-side is the applier's own Serial, a different */ \
/* value with a different owner. So these are not values to migrate. */ \
X(GetSamplingResolutionGeneration, BARRIER_PULLED, "P3b/P4b", \
"a shutter, not a value: the server answers from its own Serial") \
X(GetTextureBindGeneration, BARRIER_PULLED, "P3b/P4b", \
"a shutter, not a value: the server answers from its own Serial") \
X(GetTextureContextId, BARRIER_PULLED, "P3b/P4b", \
"a shutter, not a value: the server answers from its own Serial") \
X(GetTextureUnitObject, BARRIER_PULLED, "P3b/P4b, P7", \ X(GetTextureUnitObject, BARRIER_PULLED, "P3b/P4b, P7", \
"frontend TextureUnit base pointer; 13 Espryt + 8 Magma sites") \ "frontend TextureUnit base pointer; 13 Espryt + 8 Magma sites") \
/* DirectGLES.cpp:4497, PrepareForDraw, the second unconditional pointer read of every draw. */ \ /* DirectGLES.cpp:4497, PrepareForDraw, the second unconditional pointer read of every draw. */ \
@@ -113,24 +99,15 @@
/* phases. Overturned by: nothing in P5b; P7/P8 retire both rows together. */ \ /* phases. Overturned by: nothing in P5b; P7/P8 retire both rows together. */ \
X(GetProgramForDispatch, BARRIER_PULLED, "P7 (Magma), P8 (Espryt)", \ X(GetProgramForDispatch, BARRIER_PULLED, "P7 (Magma), P8 (Espryt)", \
"frontend SharedPtr<ProgramObject>; the record carries a handle") \ "frontend SharedPtr<ProgramObject>; the record carries a handle") \
/* The XFB six. XFB itself is off the reduced path, but kDraw's may-read mask carries all */ \ /* The XFB family's one OBJECT row. XFB itself is off the reduced path, but kDraw's */ \
/* six and the draw walk reads them regardless - which is exactly the case a field census */ \ /* may-read mask carries it and the draw walk reads it regardless - which is exactly the */ \
/* taken from "what the scenario does" rather than from the mask would miss. */ \ /* case a field census taken from "what the scenario does" rather than from the mask would */ \
X(IsTransformFeedbackActive, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \ /* miss. The family's five VALUE rows rode set_context_values out of this list at P5c rv. */ \
"read on every kDraw walk although XFB is off the reduced path") \
X(IsTransformFeedbackPaused, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"read on every kDraw walk although XFB is off the reduced path") \
X(GetTransformFeedbackProgram, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \ X(GetTransformFeedbackProgram, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"frontend SharedPtr<ProgramObject>") \ "frontend SharedPtr<ProgramObject>") \
X(GetTransformFeedbackGeneration, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"no call carries it") \
X(GetBoundTransformFeedbackLifetimeId, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"the D21 counter-slot rekey's key; no call carries it") \
X(GetTransformFeedbackCapturedVertices, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
"no call carries it") \
\ \
/* ---- APPLIER_DERIVED ---- */ \ /* ---- APPLIER_DERIVED ---- */ \
/* THE ONE ROW, and it is the one the contract asked P5 to split into two field ids. It is */ \ /* THE FIRST ROW, and it is the one the contract asked P5 to split into two field ids. It is */ \
/* split HERE INSTEAD, by ARGUMENT, in the argument-exception list below - see that list's */ \ /* split HERE INSTEAD, by ARGUMENT, in the argument-exception list below - see that list's */ \
/* header for the evidence and for what would overturn the decision. The field itself is */ \ /* header for the evidence and for what would overturn the decision. The field itself is */ \
/* APPLIER_DERIVED because set_pixel_pack_state arrives and MGPipeApplyAccess::PackState */ \ /* APPLIER_DERIVED because set_pixel_pack_state arrives and MGPipeApplyAccess::PackState */ \
@@ -139,6 +116,19 @@
/* half of it). */ \ /* half of it). */ \
X(GetPixelStoreParameters, APPLIER_DERIVED, "-", \ X(GetPixelStoreParameters, APPLIER_DERIVED, "-", \
"set_pixel_pack_state; the applier writes m_pixelStore[0] (PipeApply.cpp:1373)") \ "set_pixel_pack_state; the applier writes m_pixelStore[0] (PipeApply.cpp:1373)") \
/* THE THREE TEXTURE SHUTTERS (P5c rv, CONTRACT-P5C.md §5.3). No call carries them and none */ \
/* should (Coverage.def's emitted-list header): what replaces them server-side is the */ \
/* applier's own Serial - a different value with a different owner. rv is the edit that */ \
/* makes the accessor DO it: under a server-stamped verb the PipeInputs accessor answers */ \
/* MGPipeApplierTextureShutterSerial() / MGPipeApplierContextSerial() (PipeApply.cpp) */ \
/* instead of the client's residual-fill copy, and the class flips from BARRIER_PULLED to */ \
/* here. Under monolith the storage answer is kept, byte for byte (G1). */ \
X(GetSamplingResolutionGeneration, APPLIER_DERIVED, "-", \
"a shutter, not a value: the server answers from its own Serial") \
X(GetTextureBindGeneration, APPLIER_DERIVED, "-", \
"a shutter, not a value: the server answers from its own Serial") \
X(GetTextureContextId, APPLIER_DERIVED, "-", \
"a shutter, not a value: the server answers from its own Serial") \
\ \
/* ---- FATAL: two non-sticky fields, each off the reduced path for a checkable reason ----- */ \ /* ---- FATAL: two non-sticky fields, each off the reduced path for a checkable reason ----- */ \
/* Three until P5b: GetProgramForDispatch moved up to BARRIER_PULLED when package i1 put */ \ /* Three until P5b: GetProgramForDispatch moved up to BARRIER_PULLED when package i1 put */ \
+51 -7
View File
@@ -936,14 +936,29 @@ namespace MobileGL::MG_Pipe {
}; };
MGP_ASSERT_POD(MGPGlobalConstants, 40); MGP_ASSERT_POD(MGPGlobalConstants, 40);
// The float/int/uint view is resolved on the CLIENT by ClassifyVertexAttribType. // One attribute default, resolved on the CLIENT. P5c (rv, CONTRACT-P5C.md §5.3) AMENDED
// the shape: a CurrentVertexAttributeValue is one value in THREE views and GLContext
// converts between them NUMERICALLY (SetCurrentVertexAttributeFloat writes (Int32)value
// into intValue), so the four words of the written class alone were never the value -
// glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in floatValue. The
// record now carries all three views VERBATIM, as the frontend computed them, and the
// applier writes each view from its own array; ValueClass stays as the record of which
// view the application wrote directly (the verify comparator covers it, and the
// emission-side suppressor compares the three views, not it). This is the catalogue's
// second payload-size amendment after P5b's MGPCopyRegion 64 -> 72 (CONTRACT-P5C.md §7.6):
// 24 -> 56 bytes, and PipeCatalogueTest pins the new size.
struct MGPAttribValue { struct MGPAttribValue {
Uint32 Location; Uint32 Location;
Uint8 ValueClass; // Float | Int | Uint | Double Uint8 ValueClass; // Float | Int | Uint | Double
Uint8 Pad0[3]; Uint8 Pad0[3];
Uint32 Data[4]; // The frontend's three views, VERBATIM - floatValue / intValue / uintValue of the
// CurrentVertexAttributeValue, converted between each other by the CLIENT. The
// applier writes each view from its own array and never reconverts.
Uint32 FloatView[4];
Uint32 IntView[4];
Uint32 UintView[4];
}; };
MGP_ASSERT_POD(MGPAttribValue, 24); MGP_ASSERT_POD(MGPAttribValue, 56);
// Var-tail header: MGPAttribValue[popcount(Mask)] follows. // Var-tail header: MGPAttribValue[popcount(Mask)] follows.
struct MGPVertexAttribDefaults { struct MGPVertexAttribDefaults {
@@ -1634,13 +1649,16 @@ namespace MobileGL::MG_Pipe {
MGP_ASSERT_POD(MGPCopyFromFramebuffer, 48); MGP_ASSERT_POD(MGPCopyFromFramebuffer, 48);
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// P5c: the two appended rows (MG_Remote/CONTRACT-P5C.md §5). APPENDED, never inserted: // P5c: the three appended rows (MG_Remote/CONTRACT-P5C.md §5). APPENDED, never inserted:
// applier_reset is opcode 77 and object_death opcode 78, and no earlier opcode moved. // applier_reset is opcode 77, object_death opcode 78, and set_context_values (rv, §5.3)
// opcode 79 - and no earlier opcode moved.
// //
// Neither has an MGPipeApply* entry point and neither gains one. Both reach // THE TWO CONTROL RECORDS have no MGPipeApply* entry point and never gain one. Both reach
// MG_Remote::Wire::WireVerbSink like the five P5b verbs, and under monolith neither has a // MG_Remote::Wire::WireVerbSink like the five P5b verbs, and under monolith neither has a
// producer - the reset is the GL thread's direct MGPipeApplierReset() call and the death // producer - the reset is the GL thread's direct MGPipeApplierReset() call and the death
// notice's mailbox hop, byte for byte as today (G1/G2). // notice's mailbox hop, byte for byte as today (G1/G2). set_context_values is the ordinary
// shape instead: a routed set_* row with an MGPipeApply* entry point, emitted at validate
// with an active transport and never produced under monolith (G1).
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// applier_reset = tracker.FreshlyPrimed()'s server half (P5c §5.1). The one field is the // applier_reset = tracker.FreshlyPrimed()'s server half (P5c §5.1). The one field is the
@@ -1660,6 +1678,32 @@ namespace MobileGL::MG_Pipe {
// object (§5.2), so a null handle arriving is Fatal{ProtocolCorruption, // object (§5.2), so a null handle arriving is Fatal{ProtocolCorruption,
// "ObjectDeath.Handle"} at the sink. // "ObjectDeath.Handle"} at the sink.
// set_context_values = opcode 79, P5c rv (CONTRACT-P5C.md §5.3): the rv field table as
// ONE fixed-width POD. It carries every value-class field that no set_* call supplies -
// the two texture-unit counters, the per-target touched-buffer-binding high-water marks
// (indexed by BufferTarget, all 15, the four bind-point targets today and the rest
// zero), and the five transform-feedback values - and it retires their BARRIER_PULLED
// rows to RECORD_SUPPLIED. There is NO dirty mask in the payload: the record is
// whole-record hash-suppressed like every other set_* call, and a suppressed record
// means "nothing moved", never "field invalid" (§1). The three texture SHUTTERS
// (GetSamplingResolutionGeneration / GetTextureBindGeneration / GetTextureContextId)
// deliberately carry NO field here: they are APPLIER_DERIVED, answered by the accessor
// from the applier's own Serial (MG_Backend/MGPipe/PipeInputs.h).
struct MGPContextValues {
Uint32 ActiveTextureUnit; // feeds GetActiveTextureUnit
Uint32 MaxTouchedTextureUnit; // feeds GetMaxTouchedTextureUnit
// 15 = BufferTarget::BufferTargetCount (BufferObject.h:15-33). MG_Pipe may not include
// MG_State, so the pairing is asserted where both sides are visible (PipeFill.cpp).
Uint32 TouchedBufferBindingPointCount[15];
Uint8 IsTransformFeedbackActive;
Uint8 IsTransformFeedbackPaused;
Uint8 Pad0[2];
Uint64 TransformFeedbackGeneration;
Uint64 BoundTransformFeedbackLifetimeId;
Uint64 TransformFeedbackCapturedVertices;
};
MGP_ASSERT_POD(MGPContextValues, 96);
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// Reverse channel payloads (section 7.1) // Reverse channel payloads (section 7.1)
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
+71 -5
View File
@@ -213,6 +213,23 @@ namespace MobileGL::MG_Pipe {
inputs.m_patchDefaultOuterLevel = outer; inputs.m_patchDefaultOuterLevel = outer;
inputs.m_patchDefaultInnerLevel = inner; inputs.m_patchDefaultInnerLevel = inner;
} }
// P5c (rv, CONTRACT-P5C.md §5.3): set_context_values' write, one field per payload
// member, on the server side of the wire. Like PackState it is a plain store - the
// stamp is the filler/verb boundary's statement, not the applier's (see the struct's
// header comment).
static void SetContextValues(PipeInputs& inputs, const MGPContextValues& values) {
inputs.m_activeTextureUnit = static_cast<Int>(values.ActiveTextureUnit);
inputs.m_maxTouchedTextureUnit = static_cast<Int>(values.MaxTouchedTextureUnit);
for (SizeT i = 0; i < PipeInputs::kBufferTargetCount; ++i) {
inputs.m_touchedBindingPointCount[i] =
static_cast<SizeT>(values.TouchedBufferBindingPointCount[i]);
}
inputs.m_transformFeedbackActive = values.IsTransformFeedbackActive != 0;
inputs.m_transformFeedbackPaused = values.IsTransformFeedbackPaused != 0;
inputs.m_transformFeedbackGeneration = values.TransformFeedbackGeneration;
inputs.m_boundTransformFeedbackLifetimeId = values.BoundTransformFeedbackLifetimeId;
inputs.m_transformFeedbackCapturedVertices = values.TransformFeedbackCapturedVertices;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// D5: the 29 PipeInputs fields that are PURE FUNCTIONS of RenderStateParameters. // D5: the 29 PipeInputs fields that are PURE FUNCTIONS of RenderStateParameters.
@@ -1189,6 +1206,12 @@ namespace MobileGL::MG_Pipe {
MGPipeApplierState& MGPipeApplier() { return g_applier; } MGPipeApplierState& MGPipeApplier() { return g_applier; }
// P5c (rv, CONTRACT-P5C.md §5.3): the three texture shutters' server-side answers. See
// MGPipeApplierState::TextureShutterSerial / ContextSerial for the bump rule.
Uint64 MGPipeApplierTextureShutterSerial() { return g_applier.TextureShutterSerial; }
Uint64 MGPipeApplierContextSerial() { return g_applier.ContextSerial; }
void MGPipeApplierNoteTextureStateMoved() { ++g_applier.TextureShutterSerial; }
void MGPipeSetResourceOps(const MGPipeResourceOps* ops) { g_resourceOps = ops; } void MGPipeSetResourceOps(const MGPipeResourceOps* ops) { g_resourceOps = ops; }
const MGPipeResourceOps* MGPipeGetResourceOps() { return g_resourceOps; } const MGPipeResourceOps* MGPipeGetResourceOps() { return g_resourceOps; }
@@ -1306,6 +1329,13 @@ namespace MobileGL::MG_Pipe {
// the first compare after the switch is a mismatch, which is the safe direction. // the first compare after the switch is a mismatch, which is the safe direction.
++g_applier.VertexBuffersSerial; ++g_applier.VertexBuffersSerial;
++g_applier.IndexBufferSerial; ++g_applier.IndexBufferSerial;
// P5c (rv): a make-current is a fresh server, and the two shutter serials are what the
// PipeInputs texture-shutter accessors answer with - so both ADVANCE here, for the
// serials' own reason: a counter that restarts walks back through values already
// stamped into a memo that outlived the switch. ContextSerial's move is also
// GetTextureContextId's whole job (the backends key their per-context memos on it).
++g_applier.TextureShutterSerial;
++g_applier.ContextSerial;
// ---- P4a's working state, cleared for the same reason and with the same serial rule // ---- P4a's working state, cleared for the same reason and with the same serial rule
// (D-J4). The OBJECT records - texture and renderbuffer resources, sampler CSOs, // (D-J4). The OBJECT records - texture and renderbuffer resources, sampler CSOs,
@@ -1351,6 +1381,10 @@ namespace MobileGL::MG_Pipe {
g_applier.BoundVertexElements = kMGPipeNullHandle; g_applier.BoundVertexElements = kMGPipeNullHandle;
++g_applier.VertexBuffersSerial; ++g_applier.VertexBuffersSerial;
++g_applier.IndexBufferSerial; ++g_applier.IndexBufferSerial;
// P5c (rv): same rule for the shutter serials - the served context is going away, and
// a memo that outlives it must not match a value these have already answered with.
++g_applier.TextureShutterSerial;
++g_applier.ContextSerial;
// P4a's five object tables go with them, and the working handles they could name go // P4a's five object tables go with them, and the working handles they could name go
// too - a bound shader CSO whose record has just been dropped must not survive as a // too - a bound shader CSO whose record has just been dropped must not survive as a
// handle the next call resolves against. // handle the next call resolves against.
@@ -1493,6 +1527,18 @@ namespace MobileGL::MG_Pipe {
MGPipeApplyAccess::PackState(gPipeInputs) = pack.Pack; MGPipeApplyAccess::PackState(gPipeInputs) = pack.Pack;
} }
// set_context_values (P5c rv, CONTRACT-P5C.md §5.3): the residual-value record's server
// half. A plain store of the whole POD - there is no dirty mask and no field-level gate,
// because the record crosses only when one of its source values moved (the client's
// whole-record hash suppression) and a suppressed record means "nothing moved", never
// "field invalid" (§1).
void MGPipeApplySetContextValues(const MGPContextValues& values) {
static_assert(std::extent_v<decltype(MGPContextValues::TouchedBufferBindingPointCount)> ==
PipeInputs::kBufferTargetCount,
"the record's per-target array and PipeInputs' have drifted");
MGPipeApplyAccess::SetContextValues(gPipeInputs, values);
}
void MGPipeApplySetPatchState(const MGPPatchState& patch) { void MGPipeApplySetPatchState(const MGPPatchState& patch) {
PipeInputs& inputs = gPipeInputs; PipeInputs& inputs = gPipeInputs;
RenderStateParameters& working = MGPipeApplyAccess::RenderState(inputs); RenderStateParameters& working = MGPipeApplyAccess::RenderState(inputs);
@@ -1581,11 +1627,16 @@ namespace MobileGL::MG_Pipe {
continue; continue;
} }
PipeInputs::CurrentVertexAttributeValue& slot = slots[location]; PipeInputs::CurrentVertexAttributeValue& slot = slots[location];
// The three views are always populated; which one a shader input consumes is // P5c (rv, CONTRACT-P5C.md §5.3): the record carries ALL THREE VIEWS VERBATIM, as
// ClassifyVertexAttribType's answer, not the carrier's, so all three cross. // the frontend computed them, and the applier writes each view from its own array.
std::memcpy(slot.floatValue.data(), value.Data, sizeof(slot.floatValue)); // The pre-rv shape - one Data[4] memcpied into all three views - could not
std::memcpy(slot.intValue.data(), value.Data, sizeof(slot.intValue)); // reproduce the frontend's NUMERIC cross-view conversion (glVertexAttrib4f(loc,
std::memcpy(slot.uintValue.data(), value.Data, sizeof(slot.uintValue)); // 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in floatValue), which is exactly
// why the field stayed EMITTED-AND-STILL-PULLED until rv. ValueClass rides along
// for the comparator; the applier no longer needs it to rebuild anything.
std::memcpy(slot.floatValue.data(), value.FloatView, sizeof(slot.floatValue));
std::memcpy(slot.intValue.data(), value.IntView, sizeof(slot.intValue));
std::memcpy(slot.uintValue.data(), value.UintView, sizeof(slot.uintValue));
} }
if (fault == nullptr && consumed != hdr.Count) { if (fault == nullptr && consumed != hdr.Count) {
fault = "Count does not match the attributes Mask names"; fault = "Count does not match the attributes Mask names";
@@ -1767,6 +1818,9 @@ namespace MobileGL::MG_Pipe {
// the twin re-derives its storage flags from the new mask at its next sync and decides // the twin re-derives its storage flags from the new mask at its next sync and decides
// for itself whether the backend needs a recreate. // for itself whether the backend needs a recreate.
++record->Serial; ++record->Serial;
// P5c (rv): a respecify can move a texture's shape, which is exactly what the
// sampling-resolution shutter guarded - the server-side answer moves with it.
MGPipeApplierNoteTextureStateMoved();
// A RESPECIFY REDEFINES A STORE, SO THE PENDING UPLOADS AGAINST THE STORE IT REPLACES // A RESPECIFY REDEFINES A STORE, SO THE PENDING UPLOADS AGAINST THE STORE IT REPLACES
// GO WITH IT - AND ONLY THOSE. They are boxes and rects in a level's coordinate system // GO WITH IT - AND ONLY THOSE. They are boxes and rects in a level's coordinate system
@@ -2025,6 +2079,10 @@ namespace MobileGL::MG_Pipe {
const Uint32 gen = record->Gen; const Uint32 gen = record->Gen;
*record = MGPipeResourceRecord{}; *record = MGPipeResourceRecord{};
record->Gen = gen; record->Gen = gen;
// P5c (rv): a destroyed object may still be BOUND somewhere the texture shutters guard;
// the server's answer for them moves with the death rather than waiting for the next
// unit-set record to say so.
MGPipeApplierNoteTextureStateMoved();
// The applier's own state is consistent before the backend hears the news, so a hook // The applier's own state is consistent before the backend hears the news, so a hook
// that looked back at this applier could not see a resource that is already gone. The // that looked back at this applier could not see a resource that is already gone. The
@@ -2700,6 +2758,8 @@ namespace MobileGL::MG_Pipe {
// bytes are CARRIED, never cleared here: the server ORs them into its own flags and // bytes are CARRIED, never cleared here: the server ORs them into its own flags and
// clears its own copy, and the client never clears a server flag. // clears its own copy, and the client never clears a server flag.
++record->ParamsSerial; ++record->ParamsSerial;
// P5c (rv): a parameter change is what the sampling-resolution shutter guarded.
MGPipeApplierNoteTextureStateMoved();
return true; return true;
} }
@@ -2729,6 +2789,8 @@ namespace MobileGL::MG_Pipe {
return; return;
} }
++g_applier.SamplerViewsSerial; ++g_applier.SamplerViewsSerial;
// P5c (rv): a texture bind is what the bind-generation shutter guarded.
MGPipeApplierNoteTextureStateMoved();
} }
void MGPipeApplyBindSamplerStates(const MGPSamplerStates& hdr, const MGPipeHandle* tail) { void MGPipeApplyBindSamplerStates(const MGPSamplerStates& hdr, const MGPipeHandle* tail) {
@@ -2739,6 +2801,8 @@ namespace MobileGL::MG_Pipe {
return; return;
} }
++g_applier.SamplerStatesSerial; ++g_applier.SamplerStatesSerial;
// P5c (rv): a sampler bind or parameter change is what the shutters guarded.
MGPipeApplierNoteTextureStateMoved();
} }
void MGPipeApplySetShaderImages(const MGPShaderImages& hdr, const MGPImageView* tail) { void MGPipeApplySetShaderImages(const MGPShaderImages& hdr, const MGPImageView* tail) {
@@ -2754,6 +2818,8 @@ namespace MobileGL::MG_Pipe {
return; return;
} }
++g_applier.ShaderImagesSerial; ++g_applier.ShaderImagesSerial;
// P5c (rv): an image bind moves the texture bind generation's guarded set too.
MGPipeApplierNoteTextureStateMoved();
} }
// ================================================================================ // ================================================================================
+38
View File
@@ -693,6 +693,27 @@ namespace MobileGL::MG_Pipe {
MGPipeHandle BoundShaderCso = kMGPipeNullHandle; MGPipeHandle BoundShaderCso = kMGPipeNullHandle;
Uint64 ProgramBindingSerial = 0; Uint64 ProgramBindingSerial = 0;
// ---- P5c (rv, CONTRACT-P5C.md §5.3): the server-side answer for the three texture
// SHUTTERS. FieldOwnership.def moves GetSamplingResolutionGeneration /
// GetTextureBindGeneration / GetTextureContextId to APPLIER_DERIVED - "a shutter, not
// a value: the server answers from its own Serial", which their rows have said since
// P5 - and THESE are the serials, read by the PipeInputs accessors under a
// server-stamped verb instead of the client's residual-fill copy.
//
// TextureShutterSerial answers the two GENERATIONS. It is an MGGen like its seven
// siblings above: server-owned, monotone, ++ on every applied record that can move
// what the frontend's bind / sampling-resolution generations guard - the three unit
// sets, set_texture_params, a respecify or destroy of any resource, a bind_shader_image
// verb (the sink bumps it, PipeApplier.cpp) - and ADVANCED, never zeroed, by
// MGPipeApplierReset / MGPipeApplierReleaseObjectRecords. Over-firing is the safe
// direction for a memo key: a moved serial costs a re-sync, a stuck one renders stale.
//
// ContextSerial answers GetTextureContextId: stable within the served context, moved
// by every MGPipeApplierReset (a make-current is a fresh server, §5.1), which is all
// the backends' per-context memo keys need.
Uint64 TextureShutterSerial = 0;
Uint64 ContextSerial = 0;
#if MOBILEGL_BUILD_DISAGGREGATED #if MOBILEGL_BUILD_DISAGGREGATED
// ---- P5c (hd, CONTRACT-P5C §3.2): THE CURRENT VERB'S OWN HANDLES. ---------------- // ---- P5c (hd, CONTRACT-P5C §3.2): THE CURRENT VERB'S OWN HANDLES. ----------------
// //
@@ -761,6 +782,17 @@ namespace MobileGL::MG_Pipe {
// The monolith's single applier. Under split there is one per served context. // The monolith's single applier. Under split there is one per served context.
MGPipeApplierState& MGPipeApplier(); MGPipeApplierState& MGPipeApplier();
// P5c (rv): the two serials the PipeInputs texture-shutter accessors answer with under a
// server-stamped verb (FieldOwnership.def, APPLIER_DERIVED). Free functions rather than
// member reads so PipeInputs.h needs this header's DECLARATIONS only... and because the
// bump rule - advance, never zero - is stated once, beside the state.
Uint64 MGPipeApplierTextureShutterSerial();
Uint64 MGPipeApplierContextSerial();
// The one writer-side helper: every applier entry point that can move what the frontend's
// texture bind / sampling-resolution generations guard bumps the shutter serial through
// this, so the bump rule lives in exactly one place.
void MGPipeApplierNoteTextureStateMoved();
// A MAKE-CURRENT, NOT A TEARDOWN - and the distinction is the whole of this function's // A MAKE-CURRENT, NOT A TEARDOWN - and the distinction is the whole of this function's
// contract. It runs on every change of the current GLContext (MGPipeTracker::Update resets // contract. It runs on every change of the current GLContext (MGPipeTracker::Update resets
// the tracker whenever the context pointer moves, and the emitter calls this from the // the tracker whenever the context pointer moves, and the emitter calls this from the
@@ -829,6 +861,12 @@ namespace MobileGL::MG_Pipe {
// set_patch_state. The trio also travels in pipeline chunk P0, and the applier asserts // set_patch_state. The trio also travels in pipeline chunk P0, and the applier asserts
// under verify that the two carriers agree - the redundancy is a trip wire, not waste. // under verify that the two carriers agree - the redundancy is a trip wire, not waste.
void MGPipeApplySetPatchState(const MGPPatchState& patch); void MGPipeApplySetPatchState(const MGPPatchState& patch);
// set_context_values (P5c rv, CONTRACT-P5C.md §5.3): the residual-value record. Writes
// every field it carries into gPipeInputs through MGPipeApplyAccess - the server-owned
// write that retires the eight value-class BARRIER_PULLED rows. Emitted only with an
// active transport; under monolith there is no producer and the fields keep coming
// through the residual fill (G1).
void MGPipeApplySetContextValues(const MGPContextValues& values);
// set_vertex_attrib_defaults: `tail` is hdr.Count MGPAttribValues for the attributes // set_vertex_attrib_defaults: `tail` is hdr.Count MGPAttribValues for the attributes
// named by hdr.Mask, in ascending location order. // named by hdr.Mask, in ascending location order.
void MGPipeApplySetVertexAttribDefaults(const MGPVertexAttribDefaults& hdr, const MGPAttribValue* tail); void MGPipeApplySetVertexAttribDefaults(const MGPVertexAttribDefaults& hdr, const MGPAttribValue* tail);
+19 -5
View File
@@ -45,14 +45,15 @@
// appended server-side fence wait 1 and P5c's applier_reset 1 // appended server-side fence wait 1 and P5c's applier_reset 1
// kCtxQuery 8 query object namespace 6, plus the appended timestamp pair 2 // kCtxQuery 8 query object namespace 6, plus the appended timestamp pair 2
// kCtxCso 13 CSO create/bind/delete // kCtxCso 13 CSO create/bind/delete
// kCtxState 17 16 of the 17 set_* calls + the temporary set_residual_value_state // kCtxState 18 16 of the 18 set_* calls + the temporary set_residual_value_state
// + P5c rv's set_context_values
// kCtxObject 10 set_texture_params (the 17th set_*) + 8 object-scoped transfers, plus // kCtxObject 10 set_texture_params (the 17th set_*) + 8 object-scoped transfers, plus
// P5c's object_death 1 // P5c's object_death 1
// kCtxVerb 18 3 context-reading transfer calls + the 10 commands, plus the five // kCtxVerb 18 3 context-reading transfer calls + the 10 commands, plus the five
// P5b-appended verbs (bind_shader_image, patch_parameter, // P5b-appended verbs (bind_shader_image, patch_parameter,
// bind_stream_output, set_storage_block_binding, // bind_stream_output, set_storage_block_binding,
// copy_framebuffer_to_texture - MG_Remote/CONTRACT-P5B.md) // copy_framebuffer_to_texture - MG_Remote/CONTRACT-P5B.md)
// total 78 // total 79
// //
// Reconciliation with the plan's headline numbers (section 4.4 / appendix A), because they // Reconciliation with the plan's headline numbers (section 4.4 / appendix A), because they
// do not add up to a set of UNIQUE records and this file has to hold unique records: // do not add up to a set of UNIQUE records and this file has to hold unique records:
@@ -94,10 +95,15 @@
// GL-thread direct call into server memory, and object_death, the framebuffer family's // GL-thread direct call into server memory, and object_death, the framebuffer family's
// FIRST wire delete opcode and the handle-keyed replacement for the death-notice mailbox. // FIRST wire delete opcode and the handle-keyed replacement for the death-notice mailbox.
// Neither has an MGPipeApply* entry point and neither gains one; both reach WireVerbSink. // Neither has an MGPipeApply* entry point and neither gains one; both reach WireVerbSink.
// 78 unique records. // - P5c rv (§5.3) appended ONE more (opcode 79): set_context_values, the residual-value
// record that retires the value-class BARRIER_PULLED rows. Unlike the two control records
// it IS an ordinary set_* row: MGPipeApplySetContextValues writes gPipeInputs, the row is
// routed through gMGPipeContext like its seventeen siblings, and under monolith it has no
// producer at all - the fields keep coming through the residual fill, byte for byte as
// before (G1). 79 unique records.
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
#define MGP_CALL_LIST_DOCUMENTED_COUNT 78 #define MGP_CALL_LIST_DOCUMENTED_COUNT 79
// clang-format off // clang-format off
#define MGP_CALL_LIST(X) \ #define MGP_CALL_LIST(X) \
@@ -256,7 +262,15 @@
/* twin table by handle. The framebuffer family's FIRST wire delete opcode: its death used */ \ /* twin table by handle. The framebuffer family's FIRST wire delete opcode: its death used */ \
/* to hop to the apply thread through a stack-struct mailbox and a lifetime-id probe of */ \ /* to hop to the apply thread through a stack-struct mailbox and a lifetime-id probe of */ \
/* the client's allocator, both rule-E surfaces. */ \ /* the client's allocator, both rule-E surfaces. */ \
X(ObjectDeath, MGPHandleOnly, kCtxObject, kNone) X(ObjectDeath, MGPHandleOnly, kCtxObject, kNone) \
/* ---- P5c rv (CONTRACT-P5C.md §5.3), opcode 79: the residual-value record. One POD that ---- */ \
/* ---- carries every value-class BARRIER_PULLED field left after the set_* catalogue: the ---- */ \
/* ---- two texture-unit counters, the 15 per-target touched-buffer-binding counts and the ---- */ \
/* ---- five transform-feedback values. Emitted at validate when any covered value moved ---- */ \
/* ---- (whole-record hash suppression, D11's shape), ACTIVE ONLY with an active transport ---- */ \
/* ---- (PipeFill.cpp gates both the emission and the residual-fill skip on it): under ---- */ \
/* ---- monolith there is no producer and the fields keep being pulled, byte for byte (G1). ---- */ \
X(SetContextValues, MGPContextValues, kCtxState, kNone)
// clang-format on // clang-format on
// Explicitly NOT migrated (plan 4.4.6 / appendix A "explicit deletions"): // Explicitly NOT migrated (plan 4.4.6 / appendix A "explicit deletions"):
+10 -2
View File
@@ -149,7 +149,7 @@
F(ShaderCso) F(Version) F(Blob) F(ShaderCso) F(Version) F(Blob)
#define MGP_FIELDS_MGPAttribValue(F) \ #define MGP_FIELDS_MGPAttribValue(F) \
F(Location) F(ValueClass) F(Data) F(Location) F(ValueClass) F(FloatView) F(IntView) F(UintView)
#define MGP_FIELDS_MGPVertexAttribDefaults(F) \ #define MGP_FIELDS_MGPVertexAttribDefaults(F) \
F(Mask) F(Count) F(Mask) F(Count)
@@ -268,6 +268,14 @@
#define MGP_FIELDS_MGPApplierReset(F) \ #define MGP_FIELDS_MGPApplierReset(F) \
F(ContextSerial) F(ContextSerial)
// P5c rv (CONTRACT-P5C.md §5.3): set_context_values' POD, one row per member. The 15-entry
// array is one field, exactly as MGPFramebufferState's Color[8] is one.
#define MGP_FIELDS_MGPContextValues(F) \
F(ActiveTextureUnit) F(MaxTouchedTextureUnit) F(TouchedBufferBindingPointCount) \
F(IsTransformFeedbackActive) F(IsTransformFeedbackPaused) \
F(TransformFeedbackGeneration) F(BoundTransformFeedbackLifetimeId) \
F(TransformFeedbackCapturedVertices)
// ---- the value structs and the host span (P1 brief D8). Not call payloads themselves, but // ---- the value structs and the host span (P1 brief D8). Not call payloads themselves, but
// members of ones (ResidualValueBlock, MGPPixelPackState, MGPCaps) and of PipeInputs, so the // members of ones (ResidualValueBlock, MGPPixelPackState, MGPCaps) and of PipeInputs, so the
// comparator has to see INTO them: with these lists the memcmp fallback of MGPipeFieldEqual is // comparator has to see INTO them: with these lists the memcmp fallback of MGPipeFieldEqual is
@@ -385,7 +393,7 @@
P(MGPSurfaceInfo) \ P(MGPSurfaceInfo) \
P(MGPImageBind) P(MGPPatchParameter) P(MGPStreamOutputBind) P(MGPStorageBlockBinding) \ P(MGPImageBind) P(MGPPatchParameter) P(MGPStreamOutputBind) P(MGPStorageBlockBinding) \
P(MGPCopyFromFramebuffer) \ P(MGPCopyFromFramebuffer) \
P(MGPApplierReset) \ P(MGPApplierReset) P(MGPContextValues) \
P(RenderStateParameters) P(PixelStoreParameters) P(SamplerParameters) P(PerBufferBlendState) \ P(RenderStateParameters) P(PixelStoreParameters) P(SamplerParameters) P(PerBufferBlendState) \
P(StencilFaceState) \ P(StencilFaceState) \
P(DynamicBackendParameters) P(MGHostSpan) \ P(DynamicBackendParameters) P(MGHostSpan) \
+9 -3
View File
@@ -6,7 +6,7 @@
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header // End of Source File Header
// The MONOLITH arm of R-17's routing: thirty-seven adapters that unpack a generated table // The MONOLITH arm of R-17's routing: thirty-eight adapters that unpack a generated table
// row's parameters and call the MGPipeApply* entry point the call site used to call directly. // row's parameters and call the MGPipeApply* entry point the call site used to call directly.
// Owner: package c1. See PipeRoute.h for why the arm exists and what R-17 actually cost. // Owner: package c1. See PipeRoute.h for why the arm exists and what R-17 actually cost.
// //
@@ -164,11 +164,11 @@ namespace MobileGL::MG_Pipe {
Bool MGPipeTablesAreInstalled() { return g_arm != MGPipeRouteArm::kNone; } Bool MGPipeTablesAreInstalled() { return g_arm != MGPipeRouteArm::kNone; }
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// The thirty-seven monolith adapters // The thirty-eight monolith adapters
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// //
// Three shapes, so the eye can check them against PipeTables.inc in one pass rather than // Three shapes, so the eye can check them against PipeTables.inc in one pass rather than
// reading thirty-seven bodies. A row that does not fit one of the three is written out by // reading thirty-eight bodies. A row that does not fit one of the three is written out by
// hand BELOW the macros, never by widening a macro - a macro that grew a special case is // hand BELOW the macros, never by widening a macro - a macro that grew a special case is
// how one of these silently stops being a parameter shuffle. // how one of these silently stops being a parameter shuffle.
@@ -207,6 +207,11 @@ namespace MobileGL::MG_Pipe {
MGP_MONO_PLAIN(SetIndexBuffer, MGPIndexBuffer) MGP_MONO_PLAIN(SetIndexBuffer, MGPIndexBuffer)
MGP_MONO_PLAIN(SetPixelPackState, MGPPixelPackState) MGP_MONO_PLAIN(SetPixelPackState, MGPPixelPackState)
MGP_MONO_PLAIN(SetPatchState, MGPPatchState) MGP_MONO_PLAIN(SetPatchState, MGPPatchState)
// P5c (rv): dormant under monolith - PipeFill.cpp's producer is transport-gated, so
// nothing routes here without a live wire. It exists because the row has an
// MGPipeApply* entry point (unlike the two control records) and because the wire
// emitter's server-role arm forwards to this table (WireTables.cpp).
MGP_MONO_PLAIN(SetContextValues, MGPContextValues)
// -- context, blob companion -------------------------------------------------- // -- context, blob companion --------------------------------------------------
MGP_MONO_BLOB(CreateRenderState, MGPRenderStateDesc) MGP_MONO_BLOB(CreateRenderState, MGPRenderStateDesc)
@@ -341,6 +346,7 @@ namespace MobileGL::MG_Pipe {
gMGPipeContext.SetVertexAttribDefaults = &Mono_SetVertexAttribDefaults; gMGPipeContext.SetVertexAttribDefaults = &Mono_SetVertexAttribDefaults;
gMGPipeContext.SetPixelPackState = &Mono_SetPixelPackState; gMGPipeContext.SetPixelPackState = &Mono_SetPixelPackState;
gMGPipeContext.SetPatchState = &Mono_SetPatchState; gMGPipeContext.SetPatchState = &Mono_SetPatchState;
gMGPipeContext.SetContextValues = &Mono_SetContextValues;
gMGPipeContext.SetResidualValueState = &Mono_SetResidualValueState; gMGPipeContext.SetResidualValueState = &Mono_SetResidualValueState;
gMGPipeContext.SetTextureParams = &Mono_SetTextureParams; gMGPipeContext.SetTextureParams = &Mono_SetTextureParams;
gMGPipeContext.ResourceSubData = &Mono_ResourceSubData; gMGPipeContext.ResourceSubData = &Mono_ResourceSubData;
+11 -2
View File
@@ -6,7 +6,8 @@
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header // End of Source File Header
// THE CLIENT -> WIRE ROUTING OF THE 37 MGPipeApply* ENTRY POINTS (P5, integrator ruling R-17). // THE CLIENT -> WIRE ROUTING OF THE 38 MGPipeApply* ENTRY POINTS (P5, integrator ruling R-17;
// 38 since P5c rv added set_context_values, CONTRACT-P5C.md section 5.3).
// Owner: package c1. // Owner: package c1.
// //
// WHAT WAS MISSING. `gMGPipeWireRecordApply` is the DECODE hook and it has existed since w1: // WHAT WAS MISSING. `gMGPipeWireRecordApply` is the DECODE hook and it has existed since w1:
@@ -218,7 +219,7 @@ namespace MobileGL::MG_Pipe {
void MGPipeNoteInstalledArm(MGPipeRouteArm arm); void MGPipeNoteInstalledArm(MGPipeRouteArm arm);
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// The call-site names: MGPipeRoute<Name> for each of the thirty-seven // The call-site names: MGPipeRoute<Name> for each of the thirty-eight
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
// //
// EVERY ONE TAKES THE APPLIER'S OWN SIGNATURE, so converting a call site is a rename and // EVERY ONE TAKES THE APPLIER'S OWN SIGNATURE, so converting a call site is a rename and
@@ -307,6 +308,14 @@ namespace MobileGL::MG_Pipe {
MGP_SetPixelPackState(&pack); MGP_SetPixelPackState(&pack);
} }
inline void MGPipeRouteSetPatchState(const MGPPatchState& patch) { MGP_SetPatchState(&patch); } inline void MGPipeRouteSetPatchState(const MGPPatchState& patch) { MGP_SetPatchState(&patch); }
// P5c (rv, CONTRACT-P5C.md §5.3): emitted ONLY with an active transport (PipeFill.cpp
// gates on it); under monolith the row is never produced and the fields keep coming
// through the residual fill (G1). The row IS an ordinary routed set_* otherwise - the
// monolith adapter exists so the wire emitter's server-role arm has something defined to
// forward to, and the catalogue's null-row accounting (PipeCatalogueTest) counts it.
inline void MGPipeRouteSetContextValues(const MGPContextValues& values) {
MGP_SetContextValues(&values);
}
inline void MGPipeRouteSetVertexAttribDefaults(const MGPVertexAttribDefaults& hdr, inline void MGPipeRouteSetVertexAttribDefaults(const MGPVertexAttribDefaults& hdr,
const MGPAttribValue* tail) { const MGPAttribValue* tail) {
MGP_SetVertexAttribDefaults(&hdr, tail, hdr.Count); MGP_SetVertexAttribDefaults(&hdr, tail, hdr.Count);
@@ -34,7 +34,7 @@ inline constexpr const char* kMGPipeFieldOwnershipNames[] = {
}; };
inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCount] = { inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCount] = {
MGPipeFieldOwnership::kBarrierPulled, // GetActiveTextureUnit MGPipeFieldOwnership::kRecordSupplied, // GetActiveTextureUnit
MGPipeFieldOwnership::kRecordSupplied, // GetBlendColor MGPipeFieldOwnership::kRecordSupplied, // GetBlendColor
MGPipeFieldOwnership::kRecordSupplied, // GetBlendEquationIndexed MGPipeFieldOwnership::kRecordSupplied, // GetBlendEquationIndexed
MGPipeFieldOwnership::kRecordSupplied, // GetBlendFuncIndexed MGPipeFieldOwnership::kRecordSupplied, // GetBlendFuncIndexed
@@ -43,14 +43,14 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingSlot MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingSlot
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPoint MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPoint
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPointCount MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPointCount
MGPipeFieldOwnership::kBarrierPulled, // GetTouchedBufferBindingPointCount MGPipeFieldOwnership::kRecordSupplied, // GetTouchedBufferBindingPointCount
MGPipeFieldOwnership::kRecordSupplied, // GetClampReadColor MGPipeFieldOwnership::kRecordSupplied, // GetClampReadColor
MGPipeFieldOwnership::kRecordSupplied, // GetClearColor MGPipeFieldOwnership::kRecordSupplied, // GetClearColor
MGPipeFieldOwnership::kRecordSupplied, // GetClearDepth MGPipeFieldOwnership::kRecordSupplied, // GetClearDepth
MGPipeFieldOwnership::kRecordSupplied, // GetClearStencil MGPipeFieldOwnership::kRecordSupplied, // GetClearStencil
MGPipeFieldOwnership::kRecordSupplied, // GetColorMaskIndexed MGPipeFieldOwnership::kRecordSupplied, // GetColorMaskIndexed
MGPipeFieldOwnership::kRecordSupplied, // GetCullFaceMode MGPipeFieldOwnership::kRecordSupplied, // GetCullFaceMode
MGPipeFieldOwnership::kBarrierPulled, // GetCurrentVertexAttribute MGPipeFieldOwnership::kRecordSupplied, // GetCurrentVertexAttribute
MGPipeFieldOwnership::kRecordSupplied, // GetDepthFunc MGPipeFieldOwnership::kRecordSupplied, // GetDepthFunc
MGPipeFieldOwnership::kRecordSupplied, // GetDepthMask MGPipeFieldOwnership::kRecordSupplied, // GetDepthMask
MGPipeFieldOwnership::kRecordSupplied, // GetDepthRangeIndexed MGPipeFieldOwnership::kRecordSupplied, // GetDepthRangeIndexed
@@ -58,7 +58,7 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
MGPipeFieldOwnership::kBarrierPulled, // GetImageTextureBinding MGPipeFieldOwnership::kBarrierPulled, // GetImageTextureBinding
MGPipeFieldOwnership::kRecordSupplied, // GetLineWidth MGPipeFieldOwnership::kRecordSupplied, // GetLineWidth
MGPipeFieldOwnership::kRecordSupplied, // GetLogicOp MGPipeFieldOwnership::kRecordSupplied, // GetLogicOp
MGPipeFieldOwnership::kBarrierPulled, // GetMaxTouchedTextureUnit MGPipeFieldOwnership::kRecordSupplied, // GetMaxTouchedTextureUnit
MGPipeFieldOwnership::kRecordSupplied, // GetMinSampleShadingValue MGPipeFieldOwnership::kRecordSupplied, // GetMinSampleShadingValue
MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultInnerLevel MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultInnerLevel
MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultOuterLevel MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultOuterLevel
@@ -75,33 +75,33 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
MGPipeFieldOwnership::kRecordSupplied, // GetProvokingVertexMode MGPipeFieldOwnership::kRecordSupplied, // GetProvokingVertexMode
MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParameters MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParameters
MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParametersVersion MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParametersVersion
MGPipeFieldOwnership::kBarrierPulled, // GetSamplingResolutionGeneration MGPipeFieldOwnership::kApplierDerived, // GetSamplingResolutionGeneration
MGPipeFieldOwnership::kRecordSupplied, // GetScissorBox MGPipeFieldOwnership::kRecordSupplied, // GetScissorBox
MGPipeFieldOwnership::kRecordSupplied, // GetStencilState MGPipeFieldOwnership::kRecordSupplied, // GetStencilState
MGPipeFieldOwnership::kBarrierPulled, // GetTextureBindGeneration MGPipeFieldOwnership::kApplierDerived, // GetTextureBindGeneration
MGPipeFieldOwnership::kBarrierPulled, // GetTextureContextId MGPipeFieldOwnership::kApplierDerived, // GetTextureContextId
MGPipeFieldOwnership::kBarrierPulled, // GetTextureObject MGPipeFieldOwnership::kBarrierPulled, // GetTextureObject
MGPipeFieldOwnership::kBarrierPulled, // GetTextureUnitObject MGPipeFieldOwnership::kBarrierPulled, // GetTextureUnitObject
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackCapturedVertices MGPipeFieldOwnership::kRecordSupplied, // GetTransformFeedbackCapturedVertices
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackGeneration MGPipeFieldOwnership::kRecordSupplied, // GetTransformFeedbackGeneration
MGPipeFieldOwnership::kFatal, // GetTransformFeedbackPausedPrimitiveCounter MGPipeFieldOwnership::kFatal, // GetTransformFeedbackPausedPrimitiveCounter
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackProgram MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackProgram
MGPipeFieldOwnership::kRecordSupplied, // GetViewport MGPipeFieldOwnership::kRecordSupplied, // GetViewport
MGPipeFieldOwnership::kRecordSupplied, // GetViewportIndexed MGPipeFieldOwnership::kRecordSupplied, // GetViewportIndexed
MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabled MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabled
MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabledIndexed MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabledIndexed
MGPipeFieldOwnership::kBarrierPulled, // IsTransformFeedbackActive MGPipeFieldOwnership::kRecordSupplied, // IsTransformFeedbackActive
MGPipeFieldOwnership::kBarrierPulled, // IsTransformFeedbackPaused MGPipeFieldOwnership::kRecordSupplied, // IsTransformFeedbackPaused
MGPipeFieldOwnership::kFatal, // InvalidateCompileEnv MGPipeFieldOwnership::kFatal, // InvalidateCompileEnv
MGPipeFieldOwnership::kBarrierPulled, // ValidateProgramName MGPipeFieldOwnership::kBarrierPulled, // ValidateProgramName
MGPipeFieldOwnership::kBarrierPulled, // RecordError MGPipeFieldOwnership::kBarrierPulled, // RecordError
MGPipeFieldOwnership::kBarrierPulled, // GetBoundTransformFeedbackLifetimeId MGPipeFieldOwnership::kRecordSupplied, // GetBoundTransformFeedbackLifetimeId
MGPipeFieldOwnership::kBarrierPulled, // HasOpenTransformFeedbackSpan MGPipeFieldOwnership::kBarrierPulled, // HasOpenTransformFeedbackSpan
}; };
// The ROADMAP phase whose row retires the pull. "-" for every class but BARRIER-PULLED. // The ROADMAP phase whose row retires the pull. "-" for every class but BARRIER-PULLED.
inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] = { inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] = {
"P3b/P4b", // GetActiveTextureUnit "-", // GetActiveTextureUnit
"-", // GetBlendColor "-", // GetBlendColor
"-", // GetBlendEquationIndexed "-", // GetBlendEquationIndexed
"-", // GetBlendFuncIndexed "-", // GetBlendFuncIndexed
@@ -110,14 +110,14 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
"P8 (indirect), P9 (readback), P13 (transfer)", // GetBufferBindingSlot "P8 (indirect), P9 (readback), P13 (transfer)", // GetBufferBindingSlot
"P3b/P4b, P7", // GetBufferBindingPoint "P3b/P4b, P7", // GetBufferBindingPoint
"P7/P13", // GetBufferBindingPointCount "P7/P13", // GetBufferBindingPointCount
"P3b/P4b", // GetTouchedBufferBindingPointCount "-", // GetTouchedBufferBindingPointCount
"-", // GetClampReadColor "-", // GetClampReadColor
"-", // GetClearColor "-", // GetClearColor
"-", // GetClearDepth "-", // GetClearDepth
"-", // GetClearStencil "-", // GetClearStencil
"-", // GetColorMaskIndexed "-", // GetColorMaskIndexed
"-", // GetCullFaceMode "-", // GetCullFaceMode
"P3b/P4b", // GetCurrentVertexAttribute "-", // GetCurrentVertexAttribute
"-", // GetDepthFunc "-", // GetDepthFunc
"-", // GetDepthMask "-", // GetDepthMask
"-", // GetDepthRangeIndexed "-", // GetDepthRangeIndexed
@@ -125,7 +125,7 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
"P3b/P4b, P7", // GetImageTextureBinding "P3b/P4b, P7", // GetImageTextureBinding
"-", // GetLineWidth "-", // GetLineWidth
"-", // GetLogicOp "-", // GetLogicOp
"P3b/P4b", // GetMaxTouchedTextureUnit "-", // GetMaxTouchedTextureUnit
"-", // GetMinSampleShadingValue "-", // GetMinSampleShadingValue
"-", // GetPatchDefaultInnerLevel "-", // GetPatchDefaultInnerLevel
"-", // GetPatchDefaultOuterLevel "-", // GetPatchDefaultOuterLevel
@@ -142,27 +142,27 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
"-", // GetProvokingVertexMode "-", // GetProvokingVertexMode
"-", // GetRenderStateParameters "-", // GetRenderStateParameters
"-", // GetRenderStateParametersVersion "-", // GetRenderStateParametersVersion
"P3b/P4b", // GetSamplingResolutionGeneration "-", // GetSamplingResolutionGeneration
"-", // GetScissorBox "-", // GetScissorBox
"-", // GetStencilState "-", // GetStencilState
"P3b/P4b", // GetTextureBindGeneration "-", // GetTextureBindGeneration
"P3b/P4b", // GetTextureContextId "-", // GetTextureContextId
"P7", // GetTextureObject "P7", // GetTextureObject
"P3b/P4b, P7", // GetTextureUnitObject "P3b/P4b, P7", // GetTextureUnitObject
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackCapturedVertices "-", // GetTransformFeedbackCapturedVertices
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackGeneration "-", // GetTransformFeedbackGeneration
"-", // GetTransformFeedbackPausedPrimitiveCounter "-", // GetTransformFeedbackPausedPrimitiveCounter
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackProgram "P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackProgram
"-", // GetViewport "-", // GetViewport
"-", // GetViewportIndexed "-", // GetViewportIndexed
"-", // IsCapabilityEnabled "-", // IsCapabilityEnabled
"-", // IsCapabilityEnabledIndexed "-", // IsCapabilityEnabledIndexed
"P3b/P4b (Espryt), P7 (Magma)", // IsTransformFeedbackActive "-", // IsTransformFeedbackActive
"P3b/P4b (Espryt), P7 (Magma)", // IsTransformFeedbackPaused "-", // IsTransformFeedbackPaused
"-", // InvalidateCompileEnv "-", // InvalidateCompileEnv
"P9", // ValidateProgramName "P9", // ValidateProgramName
"P9", // RecordError "P9", // RecordError
"P3b/P4b (Espryt), P7 (Magma)", // GetBoundTransformFeedbackLifetimeId "-", // GetBoundTransformFeedbackLifetimeId
"P7/P9", // HasOpenTransformFeedbackSpan "P7/P9", // HasOpenTransformFeedbackSpan
}; };
@@ -301,8 +301,8 @@ inline constexpr SizeT kMGPipeVerbBoundaryOpCount = 23;
inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3; inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3;
// The class sizes, as constants a test can pin without recounting the table. // The class sizes, as constants a test can pin without recounting the table.
inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 32; inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 41;
inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 1; inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 4;
inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 27; inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 15;
inline constexpr SizeT kMGPipeFatalFieldCount = 3; inline constexpr SizeT kMGPipeFatalFieldCount = 3;
static_assert(kMGPipeRecordSuppliedFieldCount + kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount == kMGPipeInputFieldCount, "the four class sizes do not partition the field set"); static_assert(kMGPipeRecordSuppliedFieldCount + kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount == kMGPipeInputFieldCount, "the four class sizes do not partition the field set");
+11 -9
View File
@@ -308,6 +308,7 @@ enum class MGPipeFieldEmitter : Uint8 {
BindRenderState, BindRenderState,
BindVertexElements, BindVertexElements,
CreateRenderState, CreateRenderState,
SetContextValues,
SetDispatchProgram, SetDispatchProgram,
SetDrawProgram, SetDrawProgram,
SetDynamicState, SetDynamicState,
@@ -323,6 +324,7 @@ inline constexpr const char* kMGPipeFieldEmitterNames[] = {
"BindRenderState", "BindRenderState",
"BindVertexElements", "BindVertexElements",
"CreateRenderState", "CreateRenderState",
"SetContextValues",
"SetDispatchProgram", "SetDispatchProgram",
"SetDrawProgram", "SetDrawProgram",
"SetDynamicState", "SetDynamicState",
@@ -334,7 +336,7 @@ inline constexpr const char* kMGPipeFieldEmitterNames[] = {
}; };
inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount] = { inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount] = {
MGPipeFieldEmitter::kNone, // GetActiveTextureUnit MGPipeFieldEmitter::SetContextValues, // GetActiveTextureUnit
MGPipeFieldEmitter::SetDynamicState, // GetBlendColor MGPipeFieldEmitter::SetDynamicState, // GetBlendColor
MGPipeFieldEmitter::CreateRenderState, // GetBlendEquationIndexed MGPipeFieldEmitter::CreateRenderState, // GetBlendEquationIndexed
MGPipeFieldEmitter::CreateRenderState, // GetBlendFuncIndexed MGPipeFieldEmitter::CreateRenderState, // GetBlendFuncIndexed
@@ -343,7 +345,7 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
MGPipeFieldEmitter::kNone, // GetBufferBindingSlot MGPipeFieldEmitter::kNone, // GetBufferBindingSlot
MGPipeFieldEmitter::kNone, // GetBufferBindingPoint MGPipeFieldEmitter::kNone, // GetBufferBindingPoint
MGPipeFieldEmitter::kNone, // GetBufferBindingPointCount MGPipeFieldEmitter::kNone, // GetBufferBindingPointCount
MGPipeFieldEmitter::kNone, // GetTouchedBufferBindingPointCount MGPipeFieldEmitter::SetContextValues, // GetTouchedBufferBindingPointCount
MGPipeFieldEmitter::SetDynamicState, // GetClampReadColor MGPipeFieldEmitter::SetDynamicState, // GetClampReadColor
MGPipeFieldEmitter::SetDynamicState, // GetClearColor MGPipeFieldEmitter::SetDynamicState, // GetClearColor
MGPipeFieldEmitter::SetDynamicState, // GetClearDepth MGPipeFieldEmitter::SetDynamicState, // GetClearDepth
@@ -358,7 +360,7 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
MGPipeFieldEmitter::SetShaderImages, // GetImageTextureBinding MGPipeFieldEmitter::SetShaderImages, // GetImageTextureBinding
MGPipeFieldEmitter::SetDynamicState, // GetLineWidth MGPipeFieldEmitter::SetDynamicState, // GetLineWidth
MGPipeFieldEmitter::CreateRenderState, // GetLogicOp MGPipeFieldEmitter::CreateRenderState, // GetLogicOp
MGPipeFieldEmitter::SetSamplerViews, // GetMaxTouchedTextureUnit MGPipeFieldEmitter::SetContextValues, // GetMaxTouchedTextureUnit
MGPipeFieldEmitter::CreateRenderState, // GetMinSampleShadingValue MGPipeFieldEmitter::CreateRenderState, // GetMinSampleShadingValue
MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultInnerLevel MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultInnerLevel
MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultOuterLevel MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultOuterLevel
@@ -382,23 +384,23 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
MGPipeFieldEmitter::kNone, // GetTextureContextId MGPipeFieldEmitter::kNone, // GetTextureContextId
MGPipeFieldEmitter::kNone, // GetTextureObject MGPipeFieldEmitter::kNone, // GetTextureObject
MGPipeFieldEmitter::SetSamplerViews, // GetTextureUnitObject MGPipeFieldEmitter::SetSamplerViews, // GetTextureUnitObject
MGPipeFieldEmitter::kNone, // GetTransformFeedbackCapturedVertices MGPipeFieldEmitter::SetContextValues, // GetTransformFeedbackCapturedVertices
MGPipeFieldEmitter::kNone, // GetTransformFeedbackGeneration MGPipeFieldEmitter::SetContextValues, // GetTransformFeedbackGeneration
MGPipeFieldEmitter::kNone, // GetTransformFeedbackPausedPrimitiveCounter MGPipeFieldEmitter::kNone, // GetTransformFeedbackPausedPrimitiveCounter
MGPipeFieldEmitter::kNone, // GetTransformFeedbackProgram MGPipeFieldEmitter::kNone, // GetTransformFeedbackProgram
MGPipeFieldEmitter::SetDynamicState, // GetViewport MGPipeFieldEmitter::SetDynamicState, // GetViewport
MGPipeFieldEmitter::SetDynamicState, // GetViewportIndexed MGPipeFieldEmitter::SetDynamicState, // GetViewportIndexed
MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabled MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabled
MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabledIndexed MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabledIndexed
MGPipeFieldEmitter::kNone, // IsTransformFeedbackActive MGPipeFieldEmitter::SetContextValues, // IsTransformFeedbackActive
MGPipeFieldEmitter::kNone, // IsTransformFeedbackPaused MGPipeFieldEmitter::SetContextValues, // IsTransformFeedbackPaused
MGPipeFieldEmitter::kNone, // InvalidateCompileEnv MGPipeFieldEmitter::kNone, // InvalidateCompileEnv
MGPipeFieldEmitter::kNone, // ValidateProgramName MGPipeFieldEmitter::kNone, // ValidateProgramName
MGPipeFieldEmitter::kNone, // RecordError MGPipeFieldEmitter::kNone, // RecordError
MGPipeFieldEmitter::kNone, // GetBoundTransformFeedbackLifetimeId MGPipeFieldEmitter::SetContextValues, // GetBoundTransformFeedbackLifetimeId
MGPipeFieldEmitter::kNone, // HasOpenTransformFeedbackSpan MGPipeFieldEmitter::kNone, // HasOpenTransformFeedbackSpan
}; };
inline constexpr SizeT kMGPipeEmittedFieldCount = 40; inline constexpr SizeT kMGPipeEmittedFieldCount = 47;
struct MGPipeFilledState { struct MGPipeFilledState {
Uint64 CurrentVerbSerial; Uint64 CurrentVerbSerial;
+4 -3
View File
@@ -29,7 +29,7 @@ struct MGPipeScreen {
void (*ApplierReset)(const MGPApplierReset* payload); void (*ApplierReset)(const MGPApplierReset* payload);
}; };
// context: 66 calls. A null entry means the backend does not implement this // context: 67 calls. A null entry means the backend does not implement this
// call and the frontend keeps its own path (plan B section 4.1). // call and the frontend keeps its own path (plan B section 4.1).
struct MGPipeContext { struct MGPipeContext {
void (*QueryCreate)(const MGPQueryDesc* payload); void (*QueryCreate)(const MGPQueryDesc* payload);
@@ -98,11 +98,12 @@ struct MGPipeContext {
void (*SetStorageBlockBinding)(const MGPStorageBlockBinding* payload, const void* blobBytes, Uint64 blobByteCount); void (*SetStorageBlockBinding)(const MGPStorageBlockBinding* payload, const void* blobBytes, Uint64 blobByteCount);
void (*CopyFramebufferToTexture)(const MGPCopyFromFramebuffer* payload); void (*CopyFramebufferToTexture)(const MGPCopyFromFramebuffer* payload);
void (*ObjectDeath)(const MGPHandleOnly* payload); void (*ObjectDeath)(const MGPHandleOnly* payload);
void (*SetContextValues)(const MGPContextValues* payload);
}; };
inline constexpr SizeT kMGPipeScreenCallCount = 12; inline constexpr SizeT kMGPipeScreenCallCount = 12;
inline constexpr SizeT kMGPipeContextCallCount = 66; inline constexpr SizeT kMGPipeContextCallCount = 67;
inline constexpr SizeT kMGPipeCallCount = 78; inline constexpr SizeT kMGPipeCallCount = 79;
// A table that is not exactly its call count of function pointers has grown a // A table that is not exactly its call count of function pointers has grown a
// member that no generator knows about. // member that no generator knows about.
@@ -328,3 +328,7 @@ inline void MGP_ApplierReset(const MGPApplierReset* payload) {
inline void MGP_ObjectDeath(const MGPHandleOnly* payload) { inline void MGP_ObjectDeath(const MGPHandleOnly* payload) {
gMGPipeContext.ObjectDeath(payload); gMGPipeContext.ObjectDeath(payload);
} }
inline void MGP_SetContextValues(const MGPContextValues* payload) {
gMGPipeContext.SetContextValues(payload);
}
+9 -1
View File
@@ -113,6 +113,7 @@ inline Bool MGPipeVerify(const MGPStreamOutputBind& a, const MGPStreamOutputBind
inline Bool MGPipeVerify(const MGPStorageBlockBinding& a, const MGPStorageBlockBinding& b, const char** outField); inline Bool MGPipeVerify(const MGPStorageBlockBinding& a, const MGPStorageBlockBinding& b, const char** outField);
inline Bool MGPipeVerify(const MGPCopyFromFramebuffer& a, const MGPCopyFromFramebuffer& b, const char** outField); inline Bool MGPipeVerify(const MGPCopyFromFramebuffer& a, const MGPCopyFromFramebuffer& b, const char** outField);
inline Bool MGPipeVerify(const MGPApplierReset& a, const MGPApplierReset& b, const char** outField); inline Bool MGPipeVerify(const MGPApplierReset& a, const MGPApplierReset& b, const char** outField);
inline Bool MGPipeVerify(const MGPContextValues& a, const MGPContextValues& b, const char** outField);
inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField); inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField);
inline Bool MGPipeVerify(const PixelStoreParameters& a, const PixelStoreParameters& b, const char** outField); inline Bool MGPipeVerify(const PixelStoreParameters& a, const PixelStoreParameters& b, const char** outField);
inline Bool MGPipeVerify(const SamplerParameters& a, const SamplerParameters& b, const char** outField); inline Bool MGPipeVerify(const SamplerParameters& a, const SamplerParameters& b, const char** outField);
@@ -262,6 +263,8 @@ struct MGPipeHasFieldVerifier<MGPCopyFromFramebuffer> : std::true_type {};
template <> template <>
struct MGPipeHasFieldVerifier<MGPApplierReset> : std::true_type {}; struct MGPipeHasFieldVerifier<MGPApplierReset> : std::true_type {};
template <> template <>
struct MGPipeHasFieldVerifier<MGPContextValues> : std::true_type {};
template <>
struct MGPipeHasFieldVerifier<RenderStateParameters> : std::true_type {}; struct MGPipeHasFieldVerifier<RenderStateParameters> : std::true_type {};
template <> template <>
struct MGPipeHasFieldVerifier<PixelStoreParameters> : std::true_type {}; struct MGPipeHasFieldVerifier<PixelStoreParameters> : std::true_type {};
@@ -670,6 +673,11 @@ inline Bool MGPipeVerify(const MGPApplierReset& a, const MGPApplierReset& b, con
return true; return true;
} }
inline Bool MGPipeVerify(const MGPContextValues& a, const MGPContextValues& b, const char** outField) {
MGP_FIELDS_MGPContextValues(MGP_VERIFY_FIELD)
return true;
}
inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField) { inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField) {
MGP_FIELDS_RenderStateParameters(MGP_VERIFY_FIELD) MGP_FIELDS_RenderStateParameters(MGP_VERIFY_FIELD)
return true; return true;
@@ -717,4 +725,4 @@ inline Bool MGPipeVerify(const MGPVertexBindingPointWire& a, const MGPVertexBind
#undef MGP_VERIFY_FIELD #undef MGP_VERIFY_FIELD
inline constexpr SizeT kMGPipeVerifiedPayloadCount = 78; inline constexpr SizeT kMGPipeVerifiedPayloadCount = 79;
+14 -1
View File
@@ -148,7 +148,8 @@ enum class MGPWireOp : Uint16 {
CopyFramebufferToTexture = 76, CopyFramebufferToTexture = 76,
ApplierReset = 77, ApplierReset = 77,
ObjectDeath = 78, ObjectDeath = 78,
kOpCount = 79, SetContextValues = 79,
kOpCount = 80,
}; };
// THE FLAGS, EXPORTED ONCE, INDEXED BY OPCODE (P5 R-13.4). MGPWireRecHeader::Flags is // THE FLAGS, EXPORTED ONCE, INDEXED BY OPCODE (P5 R-13.4). MGPWireRecHeader::Flags is
@@ -248,6 +249,7 @@ inline constexpr Uint32 kMGPipeCallFlags[static_cast<SizeT>(MGPWireOp::kOpCount)
/* 76 CopyFramebufferToTexture*/ static_cast<Uint32>(kNone), /* 76 CopyFramebufferToTexture*/ static_cast<Uint32>(kNone),
/* 77 ApplierReset */ static_cast<Uint32>(kNone), /* 77 ApplierReset */ static_cast<Uint32>(kNone),
/* 78 ObjectDeath */ static_cast<Uint32>(kNone), /* 78 ObjectDeath */ static_cast<Uint32>(kNone),
/* 79 SetContextValues */ static_cast<Uint32>(kNone),
}; };
static_assert(sizeof(kMGPipeCallFlags) / sizeof(kMGPipeCallFlags[0]) == static_assert(sizeof(kMGPipeCallFlags) / sizeof(kMGPipeCallFlags[0]) ==
static_cast<SizeT>(MGPWireOp::kOpCount), static_cast<SizeT>(MGPWireOp::kOpCount),
@@ -925,6 +927,14 @@ static_assert(sizeof(MGPWireRec_ObjectDeath) ==
((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)),
"MGPWireRec_ObjectDeath gained padding; the wire format moved"); "MGPWireRec_ObjectDeath gained padding; the wire format moved");
struct alignas(8) MGPWireRec_SetContextValues {
MGPWireRecHeader Header;
MGPContextValues Payload;
};
static_assert(sizeof(MGPWireRec_SetContextValues) ==
((sizeof(MGPWireRecHeader) + sizeof(MGPContextValues) + 7u) & ~SizeT(7u)),
"MGPWireRec_SetContextValues gained padding; the wire format moved");
[[noreturn]] inline void MGPipeWireProtocolFatal(const char* call, Uint64 size, Uint64 remaining) { [[noreturn]] inline void MGPipeWireProtocolFatal(const char* call, Uint64 size, Uint64 remaining) {
MGLOG_F("MGPipe: protocol corruption applying %s: size=%llu remaining=%llu", call, MGLOG_F("MGPipe: protocol corruption applying %s: size=%llu remaining=%llu", call,
static_cast<unsigned long long>(size), static_cast<unsigned long long>(remaining)); static_cast<unsigned long long>(size), static_cast<unsigned long long>(remaining));
@@ -1208,6 +1218,9 @@ inline Bool MGPipeApplyWireRecord(MGPWireOp op, const void* record, Uint64 size,
case MGPWireOp::ObjectDeath: case MGPWireOp::ObjectDeath:
MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ObjectDeath, "ObjectDeath"); MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ObjectDeath, "ObjectDeath");
break; break;
case MGPWireOp::SetContextValues:
MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetContextValues, "SetContextValues");
break;
case MGPWireOp::kInvalid: case MGPWireOp::kInvalid:
case MGPWireOp::kOpCount: case MGPWireOp::kOpCount:
default: default:
+20 -3
View File
@@ -6,12 +6,12 @@
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header // End of Source File Header
// The ENCODE TWIN of gMGPipeWireRecordApply: thirty-seven emitters that turn a table call into // The ENCODE TWIN of gMGPipeWireRecordApply: thirty-eight emitters that turn a table call into
// a wire record. Owner: package c1 (P5 ruling R-17). See WireTables.h for the install order // a wire record. Owner: package c1 (P5 ruling R-17). See WireTables.h for the install order
// and MG_Pipe/PipeRoute.h for what R-17 actually cost. // and MG_Pipe/PipeRoute.h for what R-17 actually cost.
// //
// EVERY EMITTER IS THE SAME FOUR STEPS, and the macros below exist so that a reader can check // EVERY EMITTER IS THE SAME FOUR STEPS, and the macros below exist so that a reader can check
// thirty-seven rows against PipeTables.inc in one pass instead of reading thirty-seven bodies: // thirty-eight rows against PipeTables.inc in one pass instead of reading thirty-eight bodies:
// //
// 1. require a session - a slot that fell through to a driver this role does not have is // 1. require a session - a slot that fell through to a driver this role does not have is
// the failure R-4 exists to prevent, and there is no fall-through here either; // the failure R-4 exists to prevent, and there is no fall-through here either;
@@ -22,7 +22,7 @@
// 4. post the answer, for the rows that have one, into MG_Pipe's reply mailbox. // 4. post the answer, for the rows that have one, into MG_Pipe's reply mailbox.
// //
// WHAT IS DELIBERATELY NOT HERE. b1's `PushPersistentMapsBeforeVerb` / `MarkGpuWritesFor*` are // WHAT IS DELIBERATELY NOT HERE. b1's `PushPersistentMapsBeforeVerb` / `MarkGpuWritesFor*` are
// NOT called from these thirty-seven. They are pre-VERB hooks and these are not verbs: they // NOT called from these thirty-eight. They are pre-VERB hooks and these are not verbs: they
// are the resource, CSO and state records that a verb is later drawn against. The five class-B // are the resource, CSO and state records that a verb is later drawn against. The five class-B
// verbs in EmitTables.cpp call them, once each, immediately before their record, which is the // verbs in EmitTables.cpp call them, once each, immediately before their record, which is the
// ordering b1's B-1 fix depends on. Calling them here as well would push a persistent map // ordering b1's B-1 fix depends on. Calling them here as well would push a persistent map
@@ -196,6 +196,11 @@ namespace MobileGL::MG_Remote::Client {
MGP_WIRE_PLAIN(SetIndexBuffer, MGPIndexBuffer, Context) MGP_WIRE_PLAIN(SetIndexBuffer, MGPIndexBuffer, Context)
MGP_WIRE_PLAIN(SetPixelPackState, MGPPixelPackState, Context) MGP_WIRE_PLAIN(SetPixelPackState, MGPPixelPackState, Context)
MGP_WIRE_PLAIN(SetPatchState, MGPPatchState, Context) MGP_WIRE_PLAIN(SetPatchState, MGPPatchState, Context)
// P5c (rv), CONTRACT-P5C.md §5.3: the residual-value record is an ordinary routed
// set_* row - a fixed POD, no blob, no tail, no reply. The PRODUCER is transport-gated
// (PipeFill.cpp's EmitContextValues), so under monolith this wrapper is never reached;
// the server-role arm forwards to the monolith adapter exactly like its siblings.
MGP_WIRE_PLAIN(SetContextValues, MGPContextValues, Context)
// -- context, mandatory blob -------------------------------------------------- // -- context, mandatory blob --------------------------------------------------
MGP_WIRE_BLOB(CreateRenderState, MGPRenderStateDesc, Blob) MGP_WIRE_BLOB(CreateRenderState, MGPRenderStateDesc, Blob)
@@ -541,6 +546,17 @@ namespace MobileGL::MG_Remote::Client {
return true; return true;
} }
// P5c (rv), CONTRACT-P5C.md §5.3. Whether set_context_values can cross RIGHT NOW: a live,
// started session whose tables are not being torn down. PipeFill.cpp gates BOTH halves of
// the row on this - the emission and the residual-fill skip - so a configured-but-wireless
// transport (the bring-up window, a server-role-only fixture) keeps the pull, and the two
// can never disagree about who supplies the eight fields.
Bool ContextValuesWireLive() {
ClientSession* session = ClientSession::Active();
return session != nullptr && session->Started() &&
!g_clientTablesUninstalled.load(std::memory_order_acquire);
}
ObjectDeathEmit EmitObjectDeathRecord(MG_Pipe::MGPipeKind kind, Uint64 lifetimeId) { ObjectDeathEmit EmitObjectDeathRecord(MG_Pipe::MGPipeKind kind, Uint64 lifetimeId) {
using MG_Pipe::MGPipeHandle; using MG_Pipe::MGPipeHandle;
@@ -628,6 +644,7 @@ namespace MobileGL::MG_Remote::Client {
gMGPipeContext.SetVertexAttribDefaults = &Wire_SetVertexAttribDefaults; gMGPipeContext.SetVertexAttribDefaults = &Wire_SetVertexAttribDefaults;
gMGPipeContext.SetPixelPackState = &Wire_SetPixelPackState; gMGPipeContext.SetPixelPackState = &Wire_SetPixelPackState;
gMGPipeContext.SetPatchState = &Wire_SetPatchState; gMGPipeContext.SetPatchState = &Wire_SetPatchState;
gMGPipeContext.SetContextValues = &Wire_SetContextValues;
gMGPipeContext.SetResidualValueState = &Wire_SetResidualValueState; gMGPipeContext.SetResidualValueState = &Wire_SetResidualValueState;
gMGPipeContext.SetTextureParams = &Wire_SetTextureParams; gMGPipeContext.SetTextureParams = &Wire_SetTextureParams;
gMGPipeContext.ResourceSubData = &Wire_ResourceSubData; gMGPipeContext.ResourceSubData = &Wire_ResourceSubData;
+9 -3
View File
@@ -8,7 +8,7 @@
// THE CLIENT ARM OF R-17's ROUTING: the encode twin of `gMGPipeWireRecordApply`. Owner: c1. // THE CLIENT ARM OF R-17's ROUTING: the encode twin of `gMGPipeWireRecordApply`. Owner: c1.
// //
// Thirty-seven thin emitters over `ClientSession::EmitAndWait`, installed over the two // Thirty-eight thin emitters over `ClientSession::EmitAndWait`, installed over the two
// generated tables and the escape table that `MG_Pipe/PipeRoute.h` declares, so that under // generated tables and the escape table that `MG_Pipe/PipeRoute.h` declares, so that under
// split every resource, CSO, texture and program record leaves the GL thread as a WIRE RECORD // split every resource, CSO, texture and program record leaves the GL thread as a WIRE RECORD
// instead of executing synchronously against a context the apply thread now owns. // instead of executing synchronously against a context the apply thread now owns.
@@ -41,7 +41,7 @@
namespace MobileGL::MG_Remote::Client { namespace MobileGL::MG_Remote::Client {
// Installs the thirty-seven wire emitters over gMGPipeScreen / gMGPipeContext / // Installs the thirty-eight wire emitters over gMGPipeScreen / gMGPipeContext /
// gMGPipeRouteEscapes and records the arm. Idempotent. // gMGPipeRouteEscapes and records the arm. Idempotent.
void InstallClientWireTables(); void InstallClientWireTables();
@@ -59,7 +59,7 @@ namespace MobileGL::MG_Remote::Client {
// deletes that reach a process with no session run the applier as they do under monolith. // deletes that reach a process with no session run the applier as they do under monolith.
void ReinstallMonolithAfterTeardown(); void ReinstallMonolithAfterTeardown();
// How many records the thirty-seven emitters have published. It counts the ROUTED rows // How many records the thirty-eight emitters have published. It counts the ROUTED rows
// only - a resource_create, a set_vertex_buffers, a create_shader_state - and never the // only - a resource_create, a set_vertex_buffers, a create_shader_state - and never the
// five class-B verbs, so it is the one number that says "the resource/CSO/state path really // five class-B verbs, so it is the one number that says "the resource/CSO/state path really
// ran" as opposed to "a Clear crossed". // ran" as opposed to "a Clear crossed".
@@ -106,6 +106,12 @@ namespace MobileGL::MG_Remote::Client {
// caller then makes the direct call the record replaced. // caller then makes the direct call the record replaced.
Bool EmitApplierResetRecord(); Bool EmitApplierResetRecord();
// P5c (rv), CONTRACT-P5C.md §5.3: whether set_context_values can cross right now (a live,
// started session, tables not being torn down). PipeFill.cpp gates the record's emission
// AND the residual-fill skip for its eight fields on this one answer, so the two halves
// can never disagree about who supplies them.
Bool ContextValuesWireLive();
// The three answers a death notice can produce (§5.2): the record crossed; the client's // The three answers a death notice can produce (§5.2): the record crossed; the client's
// own allocator cannot resolve the dying object (the server never saw it, so there is no // own allocator cannot resolve the dying object (the server never saw it, so there is no
// twin to kill and NOTHING crossed); or no live session could carry the record. The // twin to kill and NOTHING crossed); or no live session could carry the record. The
@@ -783,6 +783,11 @@ namespace MobileGL::MG_Remote::Server {
static_cast<GLenum>(bind.Access), static_cast<GLenum>(bind.Access),
static_cast<GLenum>(bind.Format)); static_cast<GLenum>(bind.Format));
++m_imageBinds; ++m_imageBinds;
// P5c (rv): an image bind moves the frontend's texture bind generation, and this verb
// carries no set_shader_images alongside it - so the server-side shutter serial the
// accessors now answer with (CONTRACT-P5C.md §5.3) moves here, at the one place the
// event reaches the applier's side.
MG_Pipe::MGPipeApplierNoteTextureStateMoved();
return true; return true;
} }
+13 -1
View File
@@ -241,7 +241,8 @@ namespace MobileGL::MG_Remote::Wire {
X(SetStorageBlockBinding, MGPStorageBlockBinding) \ X(SetStorageBlockBinding, MGPStorageBlockBinding) \
X(CopyFramebufferToTexture, MGPCopyFromFramebuffer) \ X(CopyFramebufferToTexture, MGPCopyFromFramebuffer) \
X(ApplierReset, MGPApplierReset) \ X(ApplierReset, MGPApplierReset) \
X(ObjectDeath, MGPHandleOnly) X(ObjectDeath, MGPHandleOnly) \
X(SetContextValues, MGPContextValues)
namespace { namespace {
@@ -2101,6 +2102,17 @@ namespace MobileGL::MG_Remote::Wire {
return m_verbs != nullptr && return m_verbs != nullptr &&
m_verbs->OnObjectDeath(*static_cast<const MGPHandleOnly*>(payload)); m_verbs->OnObjectDeath(*static_cast<const MGPHandleOnly*>(payload));
// ---- P5c rv (CONTRACT-P5C.md §5.3): the residual-value record, opcode 79 -------------
//
// An ordinary set_* row, unlike the two control records beside it: a fixed-width POD
// with no blob, no tail and no reply, so the bounds gate is the whole validation and
// the arm is the applier entry point - the same shape as set_pixel_pack_state beside
// it. There is no sink half and no acceptance answer: the write into gPipeInputs is
// the whole effect.
case MGPWireOp::SetContextValues:
MGPipeApplySetContextValues(*static_cast<const MGPContextValues*>(payload));
return true;
case MGPWireOp::SetSwapInterval: case MGPWireOp::SetSwapInterval:
// Class C, wave 3 (census-classC.md "static cross"); not a verb (FillPoints.def:21). // Class C, wave 3 (census-classC.md "static cross"); not a verb (FillPoints.def:21).
return false; return false;
+3 -2
View File
@@ -256,8 +256,9 @@ namespace MobileGL {
// four words on a wire are not the value unless the class travels with them: // 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 // glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in
// floatValue. set_vertex_attrib_defaults carries this as MGPAttribValue's // floatValue. set_vertex_attrib_defaults carries this as MGPAttribValue's
// ValueClass so the applier can redo the conversion instead of memcpying one // ValueClass AND, since P5c rv (CONTRACT-P5C.md §5.3), all three views
// view into all three. // verbatim - the applier writes each view from its own array rather than
// redoing the conversion.
// //
// It is kept BESIDE the array rather than inside CurrentVertexAttributeValue // It is kept BESIDE the array rather than inside CurrentVertexAttributeValue
// because that struct is mirrored into PipeInputs and compared there by a // because that struct is mirrored into PipeInputs and compared there by a
+152 -37
View File
@@ -197,11 +197,15 @@ TEST_F(FieldOwnershipTest, TheClassSizesPartitionTheFieldSet) {
EXPECT_EQ(counted[static_cast<SizeT>(MGPipeFieldOwnership::kBarrierPulled)], EXPECT_EQ(counted[static_cast<SizeT>(MGPipeFieldOwnership::kBarrierPulled)],
kMGPipeBarrierPulledFieldCount); kMGPipeBarrierPulledFieldCount);
EXPECT_EQ(counted[static_cast<SizeT>(MGPipeFieldOwnership::kFatal)], kMGPipeFatalFieldCount); EXPECT_EQ(counted[static_cast<SizeT>(MGPipeFieldOwnership::kFatal)], kMGPipeFatalFieldCount);
// The census's own arithmetic (scout-unmigrated-census section 2.1): 31 of the 63 fields // The census's own arithmetic (scout-unmigrated-census section 2.1), updated by P5c rv
// are served by NO pushed record - 24 non-sticky plus the seven sticky - so 32 are. // (CONTRACT-P5C.md §5.3): rv moved the NINE value-class rows to RECORD-SUPPLIED through
EXPECT_EQ(kMGPipeRecordSuppliedFieldCount, SizeT{32}); // set_context_values / the amended set_vertex_attrib_defaults and the three texture
// shutters to APPLIER-DERIVED, so 22 of the 63 fields are served by NO pushed record -
// 15 BARRIER-PULLED (the object class: nine non-sticky rows plus six of the seven sticky
// forwards), four APPLIER-DERIVED and three FATAL - and 41 are.
EXPECT_EQ(kMGPipeRecordSuppliedFieldCount, SizeT{41});
EXPECT_EQ(kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount, EXPECT_EQ(kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount,
SizeT{31}); SizeT{22});
} }
TEST_F(FieldOwnershipTest, EveryBarrierPulledRowNamesTheRetiringPhase) { TEST_F(FieldOwnershipTest, EveryBarrierPulledRowNamesTheRetiringPhase) {
@@ -217,36 +221,56 @@ TEST_F(FieldOwnershipTest, EveryBarrierPulledRowNamesTheRetiringPhase) {
} }
// The 21 the reduced path actually reads (scout-unmigrated-census section 3: the union of // The 21 the reduced path actually reads (scout-unmigrated-census section 3: the union of
// kClear's 7, kDraw's 19 and kReadback's 12). Twenty of them are BARRIER-PULLED; the // kClear's 7, kDraw's 19 and kReadback's 12) - AS P5c rv LEFT THEM (CONTRACT-P5C.md §5.3):
// twenty-first is GetPixelStoreParameters, whose PACK half the applier writes and whose UNPACK // NINE moved to RECORD-SUPPLIED through set_context_values (the two texture-unit counters,
// half has no carrier and no backend reader at all. // the touched-count array, the five XFB values) plus GetCurrentVertexAttribute through the
// amended set_vertex_attrib_defaults payload, and the three texture shutters moved to
// APPLIER-DERIVED ("a shutter, not a value: the server answers from its own Serial"). What
// remains BARRIER-PULLED is EXACTLY the object class - nine non-sticky fields whose storage
// is a frontend heap reference no record can carry - plus GetPixelStoreParameters, whose
// PACK half the applier writes and whose UNPACK half has no carrier and no backend reader at
// all.
TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) { TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
const MGPipeInputField pulled[] = { const MGPipeInputField pulled[] = {
MGPipeInputField::GetActiveTextureUnit,
MGPipeInputField::GetBoundVertexArray, MGPipeInputField::GetBoundVertexArray,
MGPipeInputField::GetBufferBindingSlot, MGPipeInputField::GetBufferBindingSlot,
MGPipeInputField::GetBufferBindingPoint, MGPipeInputField::GetBufferBindingPoint,
MGPipeInputField::GetTouchedBufferBindingPointCount,
MGPipeInputField::GetCurrentVertexAttribute,
MGPipeInputField::GetFramebufferBindingSlot, MGPipeInputField::GetFramebufferBindingSlot,
MGPipeInputField::GetImageTextureBinding, MGPipeInputField::GetImageTextureBinding,
MGPipeInputField::GetMaxTouchedTextureUnit,
MGPipeInputField::GetProgramForDraw,
MGPipeInputField::GetSamplingResolutionGeneration,
MGPipeInputField::GetTextureBindGeneration,
MGPipeInputField::GetTextureContextId,
MGPipeInputField::GetTextureUnitObject, MGPipeInputField::GetTextureUnitObject,
MGPipeInputField::GetTransformFeedbackCapturedVertices, MGPipeInputField::GetProgramForDraw,
MGPipeInputField::GetTransformFeedbackGeneration, MGPipeInputField::GetProgramForDispatch,
MGPipeInputField::GetTransformFeedbackProgram, MGPipeInputField::GetTransformFeedbackProgram,
MGPipeInputField::GetBoundTransformFeedbackLifetimeId,
MGPipeInputField::IsTransformFeedbackActive,
MGPipeInputField::IsTransformFeedbackPaused,
}; };
for (const auto field : pulled) { for (const auto field : pulled) {
EXPECT_EQ(MGPipeFieldOwnershipOf(field), MGPipeFieldOwnership::kBarrierPulled) EXPECT_EQ(MGPipeFieldOwnershipOf(field), MGPipeFieldOwnership::kBarrierPulled)
<< kMGPipeInputFieldNames[Index(field)] << " left the reduced path's debt"; << kMGPipeInputFieldNames[Index(field)] << " left the reduced path's debt";
} }
// rv's exit line, pinned as a SET and not only as nine rows: the non-sticky
// BARRIER-PULLED list above is ALL the non-sticky debt - value-class membership is zero
// (CONTRACT-P5C.md §7 table 2) - and the only other BARRIER-PULLED rows are six of the
// seven sticky forwards (the seventh, InvalidateCompileEnv, is FATAL since P5c ev).
const MGPipeInputField pulledSticky[] = {
MGPipeInputField::GetBufferBindingPointCount,
MGPipeInputField::GetProgramObject,
MGPipeInputField::GetTextureObject,
MGPipeInputField::HasOpenTransformFeedbackSpan,
MGPipeInputField::ValidateProgramName,
MGPipeInputField::RecordError,
};
SizeT pulledCount = 0;
for (SizeT i = 0; i < kMGPipeInputFieldCount; ++i) {
const auto field = static_cast<MGPipeInputField>(i);
if (kMGPipeFieldOwnership[i] != MGPipeFieldOwnership::kBarrierPulled) continue;
++pulledCount;
Bool named = false;
for (const auto f : pulled) named = named || field == f;
for (const auto f : pulledSticky) named = named || field == f;
EXPECT_TRUE(named) << kMGPipeInputFieldNames[i]
<< " is BARRIER-PULLED and not in the pinned object-class list";
}
EXPECT_EQ(pulledCount, 15u) << "9 non-sticky object rows + 6 sticky forwards";
EXPECT_EQ(pulledCount, kMGPipeBarrierPulledFieldCount);
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters), EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters),
MGPipeFieldOwnership::kApplierDerived); MGPipeFieldOwnership::kApplierDerived);
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters, 0u), EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters, 0u),
@@ -271,6 +295,36 @@ TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch), EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch),
MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDraw)) MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDraw))
<< "GetProgramForDispatch is GetProgramForDraw's twin and must share its class"; << "GetProgramForDispatch is GetProgramForDraw's twin and must share its class";
// AND THE NINE rv RETIRED, asserted in their NEW classes rather than deleted: a row that
// quietly fell back to BARRIER-PULLED is exactly what this list is for.
const MGPipeInputField suppliedByContextValues[] = {
MGPipeInputField::GetActiveTextureUnit,
MGPipeInputField::GetMaxTouchedTextureUnit,
MGPipeInputField::GetTouchedBufferBindingPointCount,
MGPipeInputField::IsTransformFeedbackActive,
MGPipeInputField::IsTransformFeedbackPaused,
MGPipeInputField::GetTransformFeedbackGeneration,
MGPipeInputField::GetBoundTransformFeedbackLifetimeId,
MGPipeInputField::GetTransformFeedbackCapturedVertices,
};
for (const auto field : suppliedByContextValues) {
EXPECT_EQ(MGPipeFieldOwnershipOf(field), MGPipeFieldOwnership::kRecordSupplied)
<< kMGPipeInputFieldNames[Index(field)] << " no longer rides set_context_values";
}
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetCurrentVertexAttribute),
MGPipeFieldOwnership::kRecordSupplied)
<< "the amended set_vertex_attrib_defaults payload carries all three views";
const MGPipeInputField shutters[] = {
MGPipeInputField::GetSamplingResolutionGeneration,
MGPipeInputField::GetTextureBindGeneration,
MGPipeInputField::GetTextureContextId,
};
for (const auto field : shutters) {
EXPECT_EQ(MGPipeFieldOwnershipOf(field), MGPipeFieldOwnership::kApplierDerived)
<< kMGPipeInputFieldNames[Index(field)]
<< " is a shutter: the server answers from its own Serial";
}
} }
TEST_F(FieldOwnershipTest, TheSevenStickyForwardsAgreeWithTheirFieldRows) { TEST_F(FieldOwnershipTest, TheSevenStickyForwardsAgreeWithTheirFieldRows) {
@@ -395,19 +449,76 @@ TEST_F(FieldOwnershipTest, ARecordSuppliedFieldIsReadableAfterAServerStamp) {
(void)gPipeInputs.GetRenderStateParameters(); (void)gPipeInputs.GetRenderStateParameters();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0}); EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
// The pixel store is in kReadback's class, not kClear's, so its readable half is exercised // The pixel store is in kReadback's class, not kClear's, so its readable half is exercised
// under the verb that actually reads it. // under the verb that actually reads it. And P5c rv's record-supplied rows join it there:
// GetActiveTextureUnit is exactly the read that used to count into `rsp`.
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels); MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
(void)gPipeInputs.GetPixelStoreParameters(false); // the half that has a carrier (void)gPipeInputs.GetPixelStoreParameters(false); // the half that has a carrier
(void)gPipeInputs.GetActiveTextureUnit(); // P5c rv: set_context_values carries it
(void)gPipeInputs.GetMaxTouchedTextureUnit();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
// And a shutter answers the applier's own Serial under a server stamp (APPLIER-DERIVED),
// not the client's residual-fill copy: still zero pulls, and the answer MOVES when the
// applier's texture state does.
(void)gPipeInputs.GetTextureBindGeneration();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
}
// P5c rv (CONTRACT-P5C.md §5.3): the residual-value record's applier write, read back through
// the accessors a backend uses, under the stamps that publish them - and the three texture
// shutters answering the applier's own serials rather than the client's fill. kReadback's
// class carries the two texture-unit counters, kDraw's the rest.
TEST_F(FieldOwnershipTest, SetContextValuesLandsInPipeInputsAndTheShuttersAnswerTheApplier) {
MGPContextValues values{};
values.ActiveTextureUnit = 5;
values.MaxTouchedTextureUnit = 23;
values.TouchedBufferBindingPointCount[static_cast<Uint32>(BufferTarget::Uniform)] = 7;
values.IsTransformFeedbackActive = 1;
values.IsTransformFeedbackPaused = 0;
values.TransformFeedbackGeneration = 0x1112131415161718ull;
values.BoundTransformFeedbackLifetimeId = 0x2122232425262728ull;
values.TransformFeedbackCapturedVertices = 0x3132333435363738ull;
MGPipeApplySetContextValues(values);
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
EXPECT_EQ(gPipeInputs.GetActiveTextureUnit(), 5);
EXPECT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), 23);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(BufferTarget::Uniform), SizeT{7});
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(BufferTarget::Vertex), SizeT{0});
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackActive());
EXPECT_FALSE(gPipeInputs.IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs.GetTransformFeedbackGeneration(), 0x1112131415161718ull);
EXPECT_EQ(gPipeInputs.GetBoundTransformFeedbackLifetimeId(), 0x2122232425262728ull);
EXPECT_EQ(gPipeInputs.GetTransformFeedbackCapturedVertices(), 0x3132333435363738ull);
// Eight record-supplied reads and not one residual pull.
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
// The three shutters answer the applier's own serials under a stamped verb. They are
// SHUTTERS - the value matters only in that it MOVES when the server's texture state does
// and never walks backwards - so the pin is the identity with the applier's counters, not
// any particular number.
EXPECT_EQ(gPipeInputs.GetTextureBindGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs.GetSamplingResolutionGeneration(), MGPipeApplierTextureShutterSerial());
EXPECT_EQ(gPipeInputs.GetTextureContextId(), MGPipeApplierContextSerial());
const Uint64 before = MGPipeApplierTextureShutterSerial();
MGPipeApplierNoteTextureStateMoved();
EXPECT_EQ(gPipeInputs.GetTextureBindGeneration(), before + 1)
<< "a texture-state apply moved the serial but the shutter did not answer with it";
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0}); EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
} }
TEST_F(FieldOwnershipTest, ABarrierPulledReadAfterAServerStampIsCountedNotFatal) { TEST_F(FieldOwnershipTest, ABarrierPulledReadAfterAServerStampIsCountedNotFatal) {
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels); // P5c rv: the exemplars are OBJECT-class now - the value rows this case used to read
// (GetActiveTextureUnit / GetTextureContextId / GetMaxTouchedTextureUnit) are
// RECORD-SUPPLIED / APPLIER-DERIVED since rv, and reading them here would count nothing.
// The three below are all of kDraw's class, all SharedPtr reads, and all safe on
// never-filled storage.
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
ASSERT_EQ(MGPipeResidualPullCount(), Uint64{0}); ASSERT_EQ(MGPipeResidualPullCount(), Uint64{0});
(void)gPipeInputs.GetActiveTextureUnit(); (void)gPipeInputs.GetBoundVertexArray();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{1}); EXPECT_EQ(MGPipeResidualPullCount(), Uint64{1});
(void)gPipeInputs.GetTextureContextId(); (void)gPipeInputs.GetProgramForDraw();
(void)gPipeInputs.GetMaxTouchedTextureUnit(); (void)gPipeInputs.GetTransformFeedbackProgram();
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{3}); EXPECT_EQ(MGPipeResidualPullCount(), Uint64{3});
} }
@@ -452,16 +563,18 @@ TEST_F(FieldOwnershipTest, TheAppliersOwnClearDisarmsTheStampWithoutTheClientsFi
} }
// The verb's own may-read table still holds on the server: kClear does not read // The verb's own may-read table still holds on the server: kClear does not read
// GetActiveTextureUnit, so reading it there is a stale answer rather than a residual pull, // GetProgramForDraw, so reading it there is a stale answer rather than a residual pull,
// and it stays Fatal. Counting it would trade a loud staleness for a quiet one. // and it stays Fatal. Counting it would trade a loud staleness for a quiet one.
// (P5c rv: the exemplar moved - GetActiveTextureUnit is RECORD-SUPPLIED since rv and would
// say nothing about the pulled set here.)
TEST_F(FieldOwnershipTest, ABarrierPulledFieldOutsideTheVerbsClassIsStillFatal) { TEST_F(FieldOwnershipTest, ABarrierPulledFieldOutsideTheVerbsClassIsStillFatal) {
#if MGTEST_HAVE_FORK #if MGTEST_HAVE_FORK
const ChildResult r = RunInChild([] { const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::Clear); MGPipeServerStampVerbBoundary(MGPipeVerb::Clear);
(void)gPipeInputs.GetActiveTextureUnit(); // BARRIER-PULLED, but not in kClear's class (void)gPipeInputs.GetProgramForDraw(); // BARRIER-PULLED, but not in kClear's class
}); });
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log; ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetActiveTextureUnit@Clear\"}"), std::string::npos) EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetProgramForDraw@Clear\"}"), std::string::npos)
<< r.Log; << r.Log;
EXPECT_EQ(r.Log.find("BARRIER-PULLED"), std::string::npos) << r.Log; EXPECT_EQ(r.Log.find("BARRIER-PULLED"), std::string::npos) << r.Log;
#else #else
@@ -473,8 +586,8 @@ TEST_F(FieldOwnershipTest, ResidualPullsReachThePublishedPerFrameCounter) {
namespace PS = MG_Util::PipeStats; namespace PS = MG_Util::PipeStats;
PS::SetEnabledForTesting(true); PS::SetEnabledForTesting(true);
PS::ResetForTesting(); PS::ResetForTesting();
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels); MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetActiveTextureUnit(); (void)gPipeInputs.GetBoundVertexArray();
EXPECT_EQ(PS::FrameCalls(PS::CallClass::ResidualPulls), Uint64{1}); EXPECT_EQ(PS::FrameCalls(PS::CallClass::ResidualPulls), Uint64{1});
EXPECT_NE(PS::FormatWindowLine().find("rsp="), std::string::npos) << PS::FormatWindowLine(); EXPECT_NE(PS::FormatWindowLine().find("rsp="), std::string::npos) << PS::FormatWindowLine();
PS::ResetForTesting(); PS::ResetForTesting();
@@ -484,28 +597,30 @@ TEST_F(FieldOwnershipTest, ResidualPullsReachThePublishedPerFrameCounter) {
#if MGTEST_HAVE_FORK #if MGTEST_HAVE_FORK
// R-7.3's proof that the instrumentation can go red. An instrumentation that cannot is // R-7.3's proof that the instrumentation can go red. An instrumentation that cannot is
// decoration, and the set it counts is not empty. // decoration, and the set it counts is not empty. (P5c rv: the exemplar is object-class now -
// the value rows this case was written against ride set_context_values and answer
// RECORD-SUPPLIED.)
TEST_F(FieldOwnershipTest, StrictErrorsTurnsABarrierPulledReadIntoANamedAbort) { TEST_F(FieldOwnershipTest, StrictErrorsTurnsABarrierPulledReadIntoANamedAbort) {
const ChildResult r = RunInChild([] { const ChildResult r = RunInChild([] {
MG_Config::Ipc.StrictErrors = true; MG_Config::Ipc.StrictErrors = true;
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels); MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetActiveTextureUnit(); (void)gPipeInputs.GetBoundVertexArray();
}); });
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log; ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetActiveTextureUnit@ReadPixels\"}"), EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"),
std::string::npos) std::string::npos)
<< r.Log; << r.Log;
EXPECT_NE(r.Log.find("BARRIER-PULLED"), std::string::npos) << r.Log; EXPECT_NE(r.Log.find("BARRIER-PULLED"), std::string::npos) << r.Log;
EXPECT_NE(r.Log.find("MOBILEGL_IPC_STRICT_ERRORS=1"), std::string::npos) << r.Log; EXPECT_NE(r.Log.find("MOBILEGL_IPC_STRICT_ERRORS=1"), std::string::npos) << r.Log;
// The strict line names the phase that owes the answer; a strict abort that did not would // The strict line names the phase that owes the answer; a strict abort that did not would
// leave the reader exactly where the gate found them. // leave the reader exactly where the gate found them.
EXPECT_NE(r.Log.find("retires in P3b/P4b"), std::string::npos) << r.Log; EXPECT_NE(r.Log.find("retires in P8]"), std::string::npos) << r.Log;
} }
TEST_F(FieldOwnershipTest, TheSameReadWithoutStrictErrorsSurvivesAndIsCounted) { TEST_F(FieldOwnershipTest, TheSameReadWithoutStrictErrorsSurvivesAndIsCounted) {
const ChildResult r = RunInChild([] { const ChildResult r = RunInChild([] {
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels); MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetActiveTextureUnit(); (void)gPipeInputs.GetBoundVertexArray();
if (MGPipeResidualPullCount() != 1) ::_exit(7); if (MGPipeResidualPullCount() != 1) ::_exit(7);
}); });
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log; ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
+30 -12
View File
@@ -127,10 +127,11 @@ TEST(PipeCatalogue, GeneratedTablesHoldTheWholeCatalogue) {
// The per-class counts PipeCalls.def documents in its header. // The per-class counts PipeCalls.def documents in its header.
// kScreen is 11 + P5c's applier_reset (MG_Remote/CONTRACT-P5C.md §5.1); kCtxObject is // kScreen is 11 + P5c's applier_reset (MG_Remote/CONTRACT-P5C.md §5.1); kCtxObject is
// 9 + P5c's object_death (§5.2), the framebuffer family's first wire delete opcode. // 9 + P5c's object_death (§5.2), the framebuffer family's first wire delete opcode.
// kCtxState is 17 + P5c rv's set_context_values (§5.3), the residual-value record.
EXPECT_EQ(ClassCount<kScreen>(), 12u); EXPECT_EQ(ClassCount<kScreen>(), 12u);
EXPECT_EQ(ClassCount<kCtxQuery>(), 8u); EXPECT_EQ(ClassCount<kCtxQuery>(), 8u);
EXPECT_EQ(ClassCount<kCtxCso>(), 13u); EXPECT_EQ(ClassCount<kCtxCso>(), 13u);
EXPECT_EQ(ClassCount<kCtxState>(), 17u); EXPECT_EQ(ClassCount<kCtxState>(), 18u);
EXPECT_EQ(ClassCount<kCtxObject>(), 10u); EXPECT_EQ(ClassCount<kCtxObject>(), 10u);
// 13 + the five P5b-appended verbs (MG_Remote/CONTRACT-P5B.md): bind_shader_image, // 13 + the five P5b-appended verbs (MG_Remote/CONTRACT-P5B.md): bind_shader_image,
// patch_parameter, bind_stream_output, set_storage_block_binding, // patch_parameter, bind_stream_output, set_storage_block_binding,
@@ -142,11 +143,12 @@ TEST(PipeCatalogue, GeneratedTablesHoldTheWholeCatalogue) {
// migrated, keep pulling" means (plan B section 4.1). // migrated, keep pulling" means (plan B section 4.1).
// //
// UNTIL P5 R-17 THAT WAS EVERY ROW, and this case said so. It is now EXACTLY THE 41 ROWS WITH // UNTIL P5 R-17 THAT WAS EVERY ROW, and this case said so. It is now EXACTLY THE 41 ROWS WITH
// NO MGPipeApply* ENTRY POINT (78 - the 37 that have one; the number was 34 at P5, 39 after // NO MGPipeApply* ENTRY POINT (79 - the 38 that have one; the number was 34 at P5, 39 after
// P5b's five sink-only verbs): the other 37 have an applier, R-17 installs adapters over them, // P5b's five sink-only verbs, and rv's set_context_values grew BOTH sides of the difference):
// the other 38 have an applier, R-17 installs adapters over them,
// and a null there would no longer mean "keep pulling" - `MG_Impl/Pipe`'s call sites go through // and a null there would no longer mean "keep pulling" - `MG_Impl/Pipe`'s call sites go through
// the thunks, so a null would mean "call through a null pointer". The number is asserted rather // the thunks, so a null would mean "call through a null pointer". The number is asserted rather
// than the emptiness, because "37 installed" and "34 still null" are the two halves of a // than the emptiness, because "38 installed" and "41 still null" are the two halves of a
// partition and a case that checked only one of them would pass an installer that had // partition and a case that checked only one of them would pass an installer that had
// overwritten rows it does not own. // overwritten rows it does not own.
// THE NAME IS KEPT, AND SO IS THE STATEMENT IT MAKES - only the ROWS it makes it about have // THE NAME IS KEPT, AND SO IS THE STATEMENT IT MAKES - only the ROWS it makes it about have
@@ -209,8 +211,9 @@ TEST(PipeCatalogue, ExactlyTheRoutedRowsAreInstalledAndTheRestAreStillNull) {
EXPECT_EQ(installed + nulls, static_cast<SizeT>(kMGPipeCallCount)); EXPECT_EQ(installed + nulls, static_cast<SizeT>(kMGPipeCallCount));
#if MOBILEGL_PIPE_PUSH #if MOBILEGL_PIPE_PUSH
// 33 + 4 = 37, and the split is the honest shape of R-17 rather than an implementation // 34 + 4 = 38, and the split is the honest shape of R-17 rather than an implementation
// detail: 37 is the number of MGPipeApply* entry points PipeApply.h declares, 33 of them // detail: 38 is the number of MGPipeApply* entry points PipeApply.h declares (37 at P5, and
// P5c rv's set_context_values - CONTRACT-P5C.md §5.3 - is the 38th), 34 of them
// fit a GENERATED row and go in the two tables, and FOUR cannot be expressed by any // fit a GENERATED row and go in the two tables, and FOUR cannot be expressed by any
// generated signature and go in the hand-written escape table beside them // generated signature and go in the hand-written escape table beside them
// (ResourceRespecify's uncarried initialBytes, ResourceFlushRange's likewise, // (ResourceRespecify's uncarried initialBytes, ResourceFlushRange's likewise,
@@ -220,8 +223,8 @@ TEST(PipeCatalogue, ExactlyTheRoutedRowsAreInstalledAndTheRestAreStillNull) {
// BOTH NUMBERS ARE ASSERTED. If the escape table were left out of this case, moving a row // BOTH NUMBERS ARE ASSERTED. If the escape table were left out of this case, moving a row
// out of the generated tables and forgetting to install its escape would read as a smaller // out of the generated tables and forgetting to install its escape would read as a smaller
// "installed" count and nothing else - and the call site would take a null. // "installed" count and nothing else - and the call site would take a null.
EXPECT_EQ(installed, 33u) << "the routed rows and the applier's entry points disagree"; EXPECT_EQ(installed, 34u) << "the routed rows and the applier's entry points disagree";
EXPECT_EQ(nulls, static_cast<SizeT>(kMGPipeCallCount) - 33u); EXPECT_EQ(nulls, static_cast<SizeT>(kMGPipeCallCount) - 34u);
const void* const* escapes = reinterpret_cast<const void* const*>(&gMGPipeRouteEscapes); const void* const* escapes = reinterpret_cast<const void* const*>(&gMGPipeRouteEscapes);
SizeT escapesInstalled = 0; SizeT escapesInstalled = 0;
for (SizeT i = 0; i < sizeof(MGPipeRouteEscapes) / sizeof(void*); ++i) { for (SizeT i = 0; i < sizeof(MGPipeRouteEscapes) / sizeof(void*); ++i) {
@@ -229,7 +232,7 @@ TEST(PipeCatalogue, ExactlyTheRoutedRowsAreInstalledAndTheRestAreStillNull) {
} }
EXPECT_EQ(escapesInstalled, 4u) << "an escape row is null; its call site would take a null " EXPECT_EQ(escapesInstalled, 4u) << "an escape row is null; its call site would take a null "
"pointer rather than fall back to anything"; "pointer rather than fall back to anything";
EXPECT_EQ(installed + escapesInstalled, 37u) EXPECT_EQ(installed + escapesInstalled, 38u)
<< "the two tables plus the escapes must be exactly PipeApply.h's entry points"; << "the two tables plus the escapes must be exactly PipeApply.h's entry points";
// And the rows that MUST still be null, named rather than counted: these are calls with no // And the rows that MUST still be null, named rather than counted: these are calls with no
@@ -576,9 +579,15 @@ TEST(PipeCatalogue, LateArrivalsAreAppendedWithoutRenumbering) {
// and object_death a kCtxObject one; both carry no blob, no reply and no tail. // and object_death a kCtxObject one; both carry no blob, no reply and no tail.
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::ApplierReset), 77); EXPECT_EQ(static_cast<Uint16>(MGPWireOp::ApplierReset), 77);
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::ObjectDeath), 78); EXPECT_EQ(static_cast<Uint16>(MGPWireOp::ObjectDeath), 78);
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::kOpCount), 79); // P5c rv (§5.3) appended the residual-value record AFTER the two control records, by the
// same rule: opcode 79, and nothing before it moved. It is an ordinary kCtxState set_* row
// - fixed POD, no blob, no tail, no reply - with an MGPipeApply* entry point, which the two
// control records deliberately do not have.
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::SetContextValues), 79);
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::kOpCount), 80);
EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::ApplierReset), static_cast<Uint32>(kNone)); EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::ApplierReset), static_cast<Uint32>(kNone));
EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::ObjectDeath), static_cast<Uint32>(kNone)); EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::ObjectDeath), static_cast<Uint32>(kNone));
EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::SetContextValues), static_cast<Uint32>(kNone));
// And the P5b rows carry what their contract says: one blob (the block name) and nothing // And the P5b rows carry what their contract says: one blob (the block name) and nothing
// else, and the extended draw row keeps its two flags. // else, and the extended draw row keeps its two flags.
EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::SetStorageBlockBinding), static_cast<Uint32>(kHasBlob)); EXPECT_EQ(MGPipeCallFlagsFor(MGPWireOp::SetStorageBlockBinding), static_cast<Uint32>(kHasBlob));
@@ -601,6 +610,14 @@ TEST(PipeCatalogue, LateArrivalsAreAppendedWithoutRenumbering) {
// struct to pin - the 16 bytes are pinned above with the handle family. // struct to pin - the 16 bytes are pinned above with the handle family.
EXPECT_EQ(sizeof(MGPApplierReset), 8u); EXPECT_EQ(sizeof(MGPApplierReset), 8u);
EXPECT_EQ(sizeof(MGPHandleOnly), 16u); EXPECT_EQ(sizeof(MGPHandleOnly), 16u);
// rv's two (§5.3/§7.6): the residual-value POD - 2 + 15 Uint32s, 2 Uint8s and 2 pad bytes,
// then the three Uint64s - and the AMENDED attribute carrier, which grew 24 -> 56 to carry
// all three views verbatim (the frontend's cross-view conversion is the authoritative
// answer; the applier no longer reconverts).
EXPECT_EQ(sizeof(MGPContextValues), 96u);
EXPECT_EQ(sizeof(MGPContextValues::TouchedBufferBindingPointCount), 60u);
EXPECT_EQ(sizeof(MGPAttribValue), 56u);
EXPECT_EQ(sizeof(MGPVertexAttribDefaults), 8u);
// The two draw-flag bits P5b's d1 arms are exclusive by contract and distinct by value. // The two draw-flag bits P5b's d1 arms are exclusive by contract and distinct by value.
EXPECT_EQ(static_cast<Uint32>(kDrawIsIndirect), 1u << 5); EXPECT_EQ(static_cast<Uint32>(kDrawIsIndirect), 1u << 5);
EXPECT_EQ(static_cast<Uint32>(kDrawHasUserIndices) & static_cast<Uint32>(kDrawIsIndirect), 0u); EXPECT_EQ(static_cast<Uint32>(kDrawHasUserIndices) & static_cast<Uint32>(kDrawIsIndirect), 0u);
@@ -799,8 +816,9 @@ TEST(PipeCatalogue, SixValueStructsHaveFieldLists) {
// MGPPatchParameter, MGPStreamOutputBind, MGPStorageBlockBinding, MGPCopyFromFramebuffer), // MGPPatchParameter, MGPStreamOutputBind, MGPStorageBlockBinding, MGPCopyFromFramebuffer),
// each with its own field list, so the comparator sees every one of them: 77. P5c appended // each with its own field list, so the comparator sees every one of them: 77. P5c appended
// applier_reset's MGPApplierReset (CONTRACT-P5C.md §5.1) - object_death reuses // applier_reset's MGPApplierReset (CONTRACT-P5C.md §5.1) - object_death reuses
// MGPHandleOnly, which has had a list since P0 - so: 78. // MGPHandleOnly, which has had a list since P0 - and rv added set_context_values'
EXPECT_EQ(kMGPipeVerifiedPayloadCount, 78u); // MGPContextValues (§5.3): 79.
EXPECT_EQ(kMGPipeVerifiedPayloadCount, 79u);
static_assert(MGPipeHasFieldVerifier<RenderStateParameters>::value); static_assert(MGPipeHasFieldVerifier<RenderStateParameters>::value);
static_assert(MGPipeHasFieldVerifier<PixelStoreParameters>::value); static_assert(MGPipeHasFieldVerifier<PixelStoreParameters>::value);
static_assert(MGPipeHasFieldVerifier<PerBufferBlendState>::value); static_assert(MGPipeHasFieldVerifier<PerBufferBlendState>::value);
+15 -2
View File
@@ -1304,6 +1304,9 @@ namespace {
// set_vertex_attrib_defaults: a var-tail call, and the one consumer of the set-hash // set_vertex_attrib_defaults: a var-tail call, and the one consumer of the set-hash
// suppressor. The tail is in ascending location order and Count matches Mask, which // suppressor. The tail is in ascending location order and Count matches Mask, which
// is the contract the applier now enforces in every build rather than in a debug one. // is the contract the applier now enforces in every build rather than in a debug one.
// P5c rv: each entry carries all THREE views verbatim (CONTRACT-P5C.md §5.3) and the
// applier writes each view from its own array - a float-written attribute keeps the
// frontend's converted int/uint words, which is what the pre-rv shape could not do.
{ {
MG_Test::ScopedPipeVerb draw(MGPipeVerb::DrawArrays); MG_Test::ScopedPipeVerb draw(MGPipeVerb::DrawArrays);
MGPVertexAttribDefaults hdr{}; MGPVertexAttribDefaults hdr{};
@@ -1311,16 +1314,26 @@ namespace {
hdr.Count = 2; hdr.Count = 2;
MGPAttribValue tail[2]{}; MGPAttribValue tail[2]{};
tail[0].Location = 2; tail[0].Location = 2;
tail[0].ValueClass = MG_State::GLState::kVertexAttribValueClassFloat;
const float first[4] = {1.5f, 2.5f, 3.5f, 4.5f}; const float first[4] = {1.5f, 2.5f, 3.5f, 4.5f};
std::memcpy(tail[0].Data, first, sizeof(first)); std::memcpy(tail[0].FloatView, first, sizeof(first));
const Int32 firstInt[4] = {1, 2, 3, 4}; // the frontend's conversion of 1.5f & co
std::memcpy(tail[0].IntView, firstInt, sizeof(firstInt));
tail[1].Location = 9; tail[1].Location = 9;
tail[1].ValueClass = MG_State::GLState::kVertexAttribValueClassFloat;
const float second[4] = {-1.f, 0.f, 0.5f, 1.f}; const float second[4] = {-1.f, 0.f, 0.5f, 1.f};
std::memcpy(tail[1].Data, second, sizeof(second)); std::memcpy(tail[1].FloatView, second, sizeof(second));
const Int32 secondInt[4] = {-1, 0, 0, 1};
std::memcpy(tail[1].IntView, secondInt, sizeof(secondInt));
MGPipeApplySetVertexAttribDefaults(hdr, tail); MGPipeApplySetVertexAttribDefaults(hdr, tail);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[0], 1.5f); EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[0], 1.5f);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[3], 4.5f); EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[3], 4.5f);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(9).floatValue[2], 0.5f); EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(9).floatValue[2], 0.5f);
// ... and the CONVERTED views are the record's, not a memcpy of the float bits:
// 1.5f's bits are 0x3FC00000, and the frontend's int view of it is 1.
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).intValue[0], 1);
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(9).intValue[0], -1);
} }
// delete_render_state: the record stops being live and a bound handle stops being // delete_render_state: the record stops being live and a bound handle stops being
+20 -15
View File
@@ -892,30 +892,35 @@ namespace {
const MGPAttribValue value = PayloadFor(3); const MGPAttribValue value = PayloadFor(3);
EXPECT_EQ(value.Location, 3u); EXPECT_EQ(value.Location, 3u);
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassFloat); EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
EXPECT_EQ(value.Data[0], Word(1.5f)); EXPECT_EQ(value.FloatView[0], Word(1.5f));
EXPECT_EQ(value.Data[1], Word(-2.5f)); EXPECT_EQ(value.FloatView[1], Word(-2.5f));
// The defect this exists to stop: 1.5f's int VIEW is 1, and a carrier that sent the // P5c rv: the record carries ALL THREE VIEWS VERBATIM (CONTRACT-P5C.md §5.3), so the
// float bits while calling them class 0 for every attribute would be sending // frontend's converted int view travels too - 1.5f's int VIEW is 1, and the carrier
// 0x3FC00000 where the frontend holds 1. // that used to send the float bits into all three views would be sending 0x3FC00000
EXPECT_NE(value.Data[0], static_cast<Uint32>(Ctx().GetCurrentVertexAttribute(3).intValue[0])); // where the frontend holds 1.
EXPECT_EQ(value.IntView[0],
static_cast<Uint32>(Ctx().GetCurrentVertexAttribute(3).intValue[0]));
EXPECT_NE(value.FloatView[0], value.IntView[0]);
} }
TEST_F(TrackerAttribPayload, AnIntWriteCarriesTheIntWordsAndNamesItsClass) { TEST_F(TrackerAttribPayload, AnIntWriteCarriesTheIntWordsAndNamesItsClass) {
Ctx().SetCurrentVertexAttributeInt(5, Array<Int32, 4>{7, -9, 11, 13}); Ctx().SetCurrentVertexAttributeInt(5, Array<Int32, 4>{7, -9, 11, 13});
const MGPAttribValue value = PayloadFor(5); const MGPAttribValue value = PayloadFor(5);
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassInt); EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassInt);
EXPECT_EQ(static_cast<Int32>(value.Data[0]), 7); EXPECT_EQ(static_cast<Int32>(value.IntView[0]), 7);
EXPECT_EQ(static_cast<Int32>(value.Data[1]), -9); EXPECT_EQ(static_cast<Int32>(value.IntView[1]), -9);
// and NOT the float view the frontend converted it into // and the float view is the frontend's CONVERSION of it, not the int bits
EXPECT_NE(value.Data[0], Word(7.0f)); EXPECT_EQ(value.FloatView[0], Word(7.0f));
EXPECT_NE(value.IntView[0], Word(7.0f));
} }
TEST_F(TrackerAttribPayload, AUintWriteCarriesTheUintWordsAndNamesItsClass) { TEST_F(TrackerAttribPayload, AUintWriteCarriesTheUintWordsAndNamesItsClass) {
Ctx().SetCurrentVertexAttributeUint(6, Array<Uint32, 4>{4000000000u, 2u, 3u, 4u}); Ctx().SetCurrentVertexAttributeUint(6, Array<Uint32, 4>{4000000000u, 2u, 3u, 4u});
const MGPAttribValue value = PayloadFor(6); const MGPAttribValue value = PayloadFor(6);
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassUint); EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassUint);
EXPECT_EQ(value.Data[0], 4000000000u); EXPECT_EQ(value.UintView[0], 4000000000u);
EXPECT_NE(value.Data[0], Word(4000000000.0f)); EXPECT_EQ(value.FloatView[0], Word(4000000000.0f));
EXPECT_NE(value.UintView[0], Word(4000000000.0f));
} }
// The class is PER ATTRIBUTE and it is the last writer's, not the context's - a payload // The class is PER ATTRIBUTE and it is the last writer's, not the context's - a payload
@@ -925,9 +930,9 @@ namespace {
Ctx().SetCurrentVertexAttributeInt(2, Array<Int32, 4>{1, 2, 3, 4}); Ctx().SetCurrentVertexAttributeInt(2, Array<Int32, 4>{1, 2, 3, 4});
EXPECT_EQ(PayloadFor(1).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat); EXPECT_EQ(PayloadFor(1).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
EXPECT_EQ(PayloadFor(2).ValueClass, MG_State::GLState::kVertexAttribValueClassInt); EXPECT_EQ(PayloadFor(2).ValueClass, MG_State::GLState::kVertexAttribValueClassInt);
// Same numbers, different classes, so the same four words mean different things: // Same numbers, different classes, so the WRITTEN views differ: 1.0f is 0x3F800000
// 1.0f is 0x3F800000 and the integer 1 is 0x00000001. // and the integer 1 is 0x00000001.
EXPECT_NE(PayloadFor(1).Data[0], PayloadFor(2).Data[0]); EXPECT_NE(PayloadFor(1).FloatView[0], PayloadFor(2).IntView[0]);
// An attribute nobody wrote answers Float, which is what the GL default (0,0,0,1) is. // An attribute nobody wrote answers Float, which is what the GL default (0,0,0,1) is.
EXPECT_EQ(PayloadFor(7).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat); EXPECT_EQ(PayloadFor(7).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
// and a later write of the other class moves the class of THAT attribute only // and a later write of the other class moves the class of THAT attribute only
@@ -38,6 +38,10 @@
#include <Config.h> #include <Config.h>
#include <MG_Remote/Server/PipeApplier.h> #include <MG_Remote/Server/PipeApplier.h>
#include <MG_Backend/DirectGLES/BackendObject_DirectGLES.h> #include <MG_Backend/DirectGLES/BackendObject_DirectGLES.h>
// P5c rv (CONTRACT-P5C.md §5.3): set_context_values' round trip reads the applied record back
// out of gPipeInputs, which needs the stamp machinery (a RECORD-SUPPLIED read outside a
// server-stamped verb is the monolith answer's business, and this fixture has no client fill).
#include <MG_Backend/MGPipe/PipeInputs.h>
// P5c ct: object_death's round trip releases REAL Espryt twin-table entries, so the suite // P5c ct: object_death's round trip releases REAL Espryt twin-table entries, so the suite
// drives the same registries the sink dispatches to (Managers.h). A suite that substituted a // drives the same registries the sink dispatches to (Managers.h). A suite that substituted a
// mock here would pin the dispatch and nothing about the release (R-16). // mock here would pin the dispatch and nothing about the release (R-16).
@@ -1326,6 +1330,52 @@ TEST_F(PipeWireCodecTest, ObjectDeathReachesTheSinkWithItsHandleAndKind) {
EXPECT_EQ(wire.Decoder().AppliedSeq(), static_cast<Uint64>(kKindCount)); EXPECT_EQ(wire.Decoder().AppliedSeq(), static_cast<Uint64>(kKindCount));
} }
// =====================================================================================
// P5c rv (MG_Remote/CONTRACT-P5C.md §5.3): the residual-value record round-trips
// =====================================================================================
//
// Not a sink row: the decoder's arm runs the REAL applier (MGPipeApplySetContextValues), so
// the round trip is read back out of gPipeInputs - under a server stamp, because a
// RECORD-SUPPLIED read outside one is the pre-stamp behaviour this build still tests
// elsewhere, and the fields' own poison would otherwise (correctly) refuse the read.
TEST_F(PipeWireCodecTest, SetContextValuesRoundTripsIntoPipeInputs) {
Wire2 wire;
MGPContextValues values{};
values.ActiveTextureUnit = 9;
values.MaxTouchedTextureUnit = 41;
for (Uint32 t = 0; t < 15; ++t) values.TouchedBufferBindingPointCount[t] = 100 + t;
values.IsTransformFeedbackActive = 1;
values.IsTransformFeedbackPaused = 1;
values.TransformFeedbackGeneration = 0xA1A2A3A4A5A6A7A8ull;
values.BoundTransformFeedbackLifetimeId = 0xB1B2B3B4B5B6B7B8ull;
values.TransformFeedbackCapturedVertices = 0xC1C2C3C4C5C6C7C8ull;
ASSERT_NE(wire.Encoder().EncodeRecord(MGPWireOp::SetContextValues, &values, sizeof(values)),
kInvalidSeq);
bool applied = false;
ASSERT_TRUE(wire.PumpOne(&applied));
EXPECT_TRUE(applied);
EXPECT_EQ(wire.Decoder().AppliedSeq(), 1u);
// kReadback's class carries the two texture-unit counters, kDraw's the rest
// (FillPoints.def) - the stamp is what publishes a RECORD-SUPPLIED field for the read.
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
EXPECT_EQ(gPipeInputs.GetActiveTextureUnit(), 9);
EXPECT_EQ(gPipeInputs.GetMaxTouchedTextureUnit(), 41);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
for (Uint32 t = 0; t < 15; ++t) {
EXPECT_EQ(gPipeInputs.GetTouchedBufferBindingPointCount(static_cast<BufferTarget>(t)),
SizeT{100 + t})
<< "target " << t;
}
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackActive());
EXPECT_TRUE(gPipeInputs.IsTransformFeedbackPaused());
EXPECT_EQ(gPipeInputs.GetTransformFeedbackGeneration(), 0xA1A2A3A4A5A6A7A8ull);
EXPECT_EQ(gPipeInputs.GetBoundTransformFeedbackLifetimeId(), 0xB1B2B3B4B5B6B7B8ull);
EXPECT_EQ(gPipeInputs.GetTransformFeedbackCapturedVertices(), 0xC1C2C3C4C5C6C7C8ull);
MGPipeServerClearVerbBoundary();
}
// ===================================================================================== // =====================================================================================
// P5b t2 (MG_Remote/CONTRACT-P5B.md §2 t2). c0b's cases above round-trip each row once; these // P5b t2 (MG_Remote/CONTRACT-P5B.md §2 t2). c0b's cases above round-trip each row once; these
// pin the fields t2's EMITTERS actually fill and the one ordering property the span family has. // pin the fields t2's EMITTERS actually fill and the one ordering property the span family has.
+9 -3
View File
@@ -1064,7 +1064,7 @@ TEST(PipeRouting, AnErrorStatusIsNotFoldedIntoAcceptedOrRefused) {
#endif // MGTEST_HAVE_FORK #endif // MGTEST_HAVE_FORK
// ===================================================================================== // =====================================================================================
// B3 / codex 9: the CLIENT arm's 37 rows are observed, not just the monolith install // B3 / codex 9: the CLIENT arm's 38 rows are observed, not just the monolith install
// ===================================================================================== // =====================================================================================
namespace { namespace {
@@ -1127,6 +1127,11 @@ TEST(PipeRouting, TheInstalledClientArmIsWireAndNotMonolithAndEveryRoutedRowMove
C1F_MOVED(Context, SetVertexAttribDefaults); C1F_MOVED(Context, SetVertexAttribDefaults);
C1F_MOVED(Context, SetPixelPackState); C1F_MOVED(Context, SetPixelPackState);
C1F_MOVED(Context, SetPatchState); C1F_MOVED(Context, SetPatchState);
// P5c rv (CONTRACT-P5C.md §5.3): the residual-value record is the 34th routed row - an
// ordinary set_* row with an MGPipeApply* entry point, so it rides BOTH tables like its
// siblings (its producer is transport-gated in PipeFill.cpp, which is a different gate's
// business).
C1F_MOVED(Context, SetContextValues);
C1F_MOVED(Context, SetResidualValueState); C1F_MOVED(Context, SetResidualValueState);
C1F_MOVED(Context, SetTextureParams); C1F_MOVED(Context, SetTextureParams);
C1F_MOVED(Context, ResourceSubData); C1F_MOVED(Context, ResourceSubData);
@@ -1140,8 +1145,9 @@ TEST(PipeRouting, TheInstalledClientArmIsWireAndNotMonolithAndEveryRoutedRowMove
C1F_ESCAPE(CreateShaderState); C1F_ESCAPE(CreateShaderState);
#undef C1F_ESCAPE #undef C1F_ESCAPE
const SizeT movedContext = CountDifferingCells(gMGPipeContext, MGPipeMonolithContext()); const SizeT movedContext = CountDifferingCells(gMGPipeContext, MGPipeMonolithContext());
EXPECT_EQ(movedScreen + movedContext, 33u) EXPECT_EQ(movedScreen + movedContext, 34u)
<< "exactly the 33 generated routed rows must differ from the monolith adapters; " << "exactly the 34 generated routed rows must differ from the monolith adapters "
"(33 at P5, + set_context_values at P5c rv); "
<< movedScreen + movedContext << movedScreen + movedContext
<< " did, so a row was left on the monolith adapter (it would run the applier on the GL " << " did, so a row was left on the monolith adapter (it would run the applier on the GL "
"thread under split) or an unrouted row was overwritten"; "thread under split) or an unrouted row was overwritten";
+20 -1
View File
@@ -462,6 +462,25 @@ def check_field_lists_cover_struct_members(field_lists, payloads, header_texts=N
ACCESSOR_READ_RE = re.compile(r"\b(?:MGB_CTX|pGLContext)\s*->\s*(\w+)") ACCESSOR_READ_RE = re.compile(r"\b(?:MGB_CTX|pGLContext)\s*->\s*(\w+)")
# The scanner's NAMED exemptions: accessors a backend reads through MGB_CTX-> / pGLContext->
# that are NOT PipeInputs fields and never will be, each with the reason written down. A name
# here is a DEBT ENTRY, not a silence: it exists so the gate can tell "scoped, named, phased"
# from "forgot the row".
#
# P5c (the tx/ev/hd merge): the G6 frontend-keyed registry's framebuffer arm - two probes a
# backend makes inside MGPipeFrontendKeyedRegistryScope (CONTRACT-P5C.md section 3.1's second
# named exemption: the registry probes ride the scope, an unwrapped probe still aborts, and
# the scope retires with the twin tables at P3b/P4b). They are object-registry lookups, not
# PipeInputs state reads, so no Coverage.def row can ever describe them - a row there is a
# field in the fill table, and these have no storage to fill.
SCAN_EXEMPT_ACCESSORS = {
"GetFramebufferObject": "P5c G6 registry probe (the default framebuffer's object), inside "
"MGPipeFrontendKeyedRegistryScope; retires with the twin tables (P3b/P4b)",
"FindFramebufferObjectByLifetimeId": "P5c G6 registry probe, inside "
"MGPipeFrontendKeyedRegistryScope; retires with the twin tables (P3b/P4b)",
}
def scan_live_accessors(accessors, backend_dir=None, verbose=True): def scan_live_accessors(accessors, backend_dir=None, verbose=True):
"""Every accessor a backend reads through MGB_CTX-> or pGLContext-> (comments and """Every accessor a backend reads through MGB_CTX-> or pGLContext-> (comments and
strings masked) must have a Coverage.def row - a read without a row is a PipeInputs strings masked) must have a Coverage.def row - a read without a row is a PipeInputs
@@ -479,7 +498,7 @@ def scan_live_accessors(accessors, backend_dir=None, verbose=True):
masked = mask_comments_and_strings(read(path)) masked = mask_comments_and_strings(read(path))
for match in ACCESSOR_READ_RE.finditer(masked): for match in ACCESSOR_READ_RE.finditer(masked):
read_names.setdefault(match.group(1), set()).add(os.path.relpath(path, REPO_ROOT)) read_names.setdefault(match.group(1), set()).add(os.path.relpath(path, REPO_ROOT))
unknown = sorted(n for n in read_names if n not in known) unknown = sorted(n for n in read_names if n not in known and n not in SCAN_EXEMPT_ACCESSORS)
if unknown: if unknown:
sys.exit("Coverage.def: accessor(s) read by a backend with no row: %s" sys.exit("Coverage.def: accessor(s) read by a backend with no row: %s"
% ", ".join("%s (%s)" % (n, ", ".join(sorted(read_names[n]))) for n in unknown)) % ", ".join("%s (%s)" % (n, ", ".join(sorted(read_names[n]))) for n in unknown))
+16 -13
View File
@@ -558,10 +558,13 @@ def self_test():
# substring is not decoration: see expect_trip. # substring is not decoration: see expect_trip.
# 1. THE HEADLINE CONTROL (exit gate E4): take one field out of the table. It is in no # 1. THE HEADLINE CONTROL (exit gate E4): take one field out of the table. It is in no
# class, and that is a build failure rather than a silent default. # class, and that is a build failure rather than a silent default. GetBoundVertexArray is
dropped = edit(r"X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED," + GAP + r"\"[^\"]*\"," # the control's field since P5c rv (it keeps a hand-written BARRIER_PULLED row; the value
+ GAP + r"\"[^\"]*\"\)", "", "remove GetActiveTextureUnit's row") # rows the first version used are RECORD_SUPPLIED-derived now, so they have no row to
controls = [("a field in NO class (GetActiveTextureUnit's row removed)", # edit).
dropped = edit(r"X\(GetBoundVertexArray," + GAP + r"BARRIER_PULLED," + GAP + r"\"[^\"]*\","
+ GAP + r"\"[^\"]*\"\)", "", "remove GetBoundVertexArray's row")
controls = [("a field in NO class (GetBoundVertexArray's row removed)",
"field(s) in NO class", "field(s) in NO class",
lambda: run(own_text=dropped))] lambda: run(own_text=dropped))]
@@ -575,9 +578,9 @@ def self_test():
lambda: run(own_text=doubled))) lambda: run(own_text=doubled)))
# 3. A row naming something that is not a field at all. # 3. A row naming something that is not a field at all.
typo = edit(r"X\(GetActiveTextureUnit,", "X(GetActiveTextureUnitt,", "misspell a field name") typo = edit(r"X\(GetBoundVertexArray,", "X(GetBoundVertexArrayy,", "misspell a field name")
controls.append(("a row naming a non-field", controls.append(("a row naming a non-field",
"GetActiveTextureUnitt, which is not a PipeInputs field", "GetBoundVertexArrayy, which is not a PipeInputs field",
lambda: run(own_text=typo))) lambda: run(own_text=typo)))
# 4. A BARRIER_PULLED row with no retiring phase: the debt is only sized if every row # 4. A BARRIER_PULLED row with no retiring phase: the debt is only sized if every row
@@ -585,17 +588,17 @@ def self_test():
# THE REPLACEMENT IS NOT A RAW STRING. It was, and the backslashes survived into the # THE REPLACEMENT IS NOT A RAW STRING. It was, and the backslashes survived into the
# substitution, mangled the row past ROW_RE's reach and made this control a silent # substitution, mangled the row past ROW_RE's reach and made this control a silent
# duplicate of #1 for a whole round. # duplicate of #1 for a whole round.
unphased = edit(r"(X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED,)" + GAP + r"\"[^\"]*\",", unphased = edit(r"(X\(GetBoundVertexArray," + GAP + r"BARRIER_PULLED,)" + GAP + r"\"[^\"]*\",",
"\\1 \"-\",", "blank a BARRIER_PULLED row's retiring phase") "\\1 \"-\",", "blank a BARRIER_PULLED row's retiring phase")
controls.append(("a BARRIER_PULLED row with no retiring phase", controls.append(("a BARRIER_PULLED row with no retiring phase",
"GetActiveTextureUnit is BARRIER_PULLED and names no retiring phase", "GetBoundVertexArray is BARRIER_PULLED and names no retiring phase",
lambda: run(own_text=unphased))) lambda: run(own_text=unphased)))
# 5. A class that is not one of the four. # 5. A class that is not one of the four.
bogus = edit(r"(X\(GetActiveTextureUnit,)" + GAP + r"BARRIER_PULLED,", bogus = edit(r"(X\(GetBoundVertexArray,)" + GAP + r"BARRIER_PULLED,",
"\\1 SOMEHOW_FINE,", "introduce a fifth class") "\\1 SOMEHOW_FINE,", "introduce a fifth class")
controls.append(("a fifth class", controls.append(("a fifth class",
"GetActiveTextureUnit is in class SOMEHOW_FINE", "GetBoundVertexArray is in class SOMEHOW_FINE",
lambda: run(own_text=bogus))) lambda: run(own_text=bogus)))
# 6. A sticky forward that lost its own row - the seven most dangerous fields are exactly # 6. A sticky forward that lost its own row - the seven most dangerous fields are exactly
@@ -699,9 +702,9 @@ def self_test():
"the field set (%d of %d)" % (total, len(accessors))) "the field set (%d of %d)" % (total, len(accessors)))
if len(sticky) != 7: if len(sticky) != 7:
sys.exit("gen_pipe_field_ownership: self-test: Coverage.def no longer has seven sticky fields") sys.exit("gen_pipe_field_ownership: self-test: Coverage.def no longer has seven sticky fields")
if len(refused) != 9: if len(refused) != 7:
sys.exit("gen_pipe_field_ownership: self-test: EmittedCallSuppliesTheWholeField refuses %d " sys.exit("gen_pipe_field_ownership: self-test: EmittedCallSuppliesTheWholeField refuses %d "
"fields, not the nine the contract's derivation is written against" % len(refused)) "fields, not the seven the contract's derivation is written against" % len(refused))
if trips == 0: if trips == 0:
sys.exit("gen_pipe_field_ownership: self-test: no negative control tripped - the gates are " sys.exit("gen_pipe_field_ownership: self-test: no negative control tripped - the gates are "
"not checking anything") "not checking anything")
@@ -710,7 +713,7 @@ def self_test():
% (len(controls) - trips, len(controls))) % (len(controls) - trips, len(controls)))
print("gen_pipe_field_ownership: self-test: %d negative-control trip(s), each asserted against " print("gen_pipe_field_ownership: self-test: %d negative-control trip(s), each asserted against "
"its OWN message; harness control OK; positive control OK " "its OWN message; harness control OK; positive control OK "
"(%d fields partitioned, 7 sticky forwards, 9 refusals)" % (trips, total)) "(%d fields partitioned, 7 sticky forwards, 7 refusals)" % (trips, total))
return 0 return 0