mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-18 09:08:31 +09:00
[MG_Pipe, MG_Impl, MG_Remote, MG_Backend] (Disaggregated): P5c rv - set_context_values retires the value-class BARRIER_PULLED rows
set_context_values (opcode 79, MGPContextValues 96B, kCtxState) carries the active texture unit, the max touched unit, the fifteen touched-binding-point counts and the five value-class XFB rows, emitted at validate under the subsystem bit and hash-suppressed as a whole; the emit gate and the residual-skip gate read the same answer so they can never disagree. MGPipeApplySetContextValues writes gPipeInputs (the PackState shape). MGPAttribValue grows 24->56B to carry the frontend's converted three views, and MGPipeApplySetVertexAttribDefaults finally writes them (the 'ignores MGPAttribValue::ValueClass' warning dies), so GetCurrentVertexAttribute leaves the refusal set. The three texture shutters answer the applier's own serials under a server-stamped verb (APPLIER_DERIVED), storage answer kept verbatim elsewhere. FieldOwnership.def: nine value-class rows become derived RECORD_SUPPLIED, the three shutters APPLIER_DERIVED, the nine object-class rows keep their phases; FieldOwnershipTest pins that the remaining pulls are exactly the object-class list. gen_pipe.py gains SCAN_EXEMPT_ACCESSORS with per-name reasons and retiring phases (it repairs the --check gate the hd merge's two G6 probes had left red). Evidence: unit 2175/2175; integration-split 111/111; both generators' --check and --self-test green; rsp on the Xfb capture scenario 36 -> 22; red-once - gating the emission off turns seven named scenarios red, restoring returns 111/111. CONTRACT-P5C section 5.3.
This commit is contained in:
@@ -13,6 +13,15 @@
|
||||
// the live context happens on the client side, in MG_Impl/Pipe/PipeFill.cpp.
|
||||
#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
|
||||
// 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
|
||||
@@ -471,9 +480,19 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetRenderStateParameters, 0, 0);
|
||||
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 {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetSamplingResolutionGeneration);
|
||||
MGP_INPUT_VERIFY_READ(MGPipeInputField::GetSamplingResolutionGeneration, 0, 0);
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (m_serverStampedVerb) return MGPipeApplierTextureShutterSerial();
|
||||
#endif
|
||||
return m_samplingResolutionGeneration;
|
||||
}
|
||||
const IntVec4& GetScissorBox() const {
|
||||
@@ -489,11 +508,21 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 GetTextureBindGeneration() const {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetTextureBindGeneration);
|
||||
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;
|
||||
}
|
||||
Uint64 GetTextureContextId() const {
|
||||
MGP_INPUT_CHECK(MGPipeInputField::GetTextureContextId);
|
||||
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;
|
||||
}
|
||||
Uint64 GetTransformFeedbackCapturedVertices() const {
|
||||
|
||||
@@ -1886,6 +1886,11 @@ namespace MobileGL::MG_Pipe {
|
||||
case MGPipeFieldEmitter::SetDrawProgram:
|
||||
case MGPipeFieldEmitter::SetDispatchProgram:
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@@ -2062,10 +2067,15 @@ namespace MobileGL::MG_Pipe {
|
||||
// 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.
|
||||
// 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 |
|
||||
kMGPipeSubsystemPixelPack |
|
||||
kMGPipeSubsystemPatchState |
|
||||
kMGPipeSubsystemVertexAttribDefaults |
|
||||
kMGPipeSubsystemResidualValues |
|
||||
kMGPipeSubsystemResources |
|
||||
kMGPipeSubsystemVertexInput |
|
||||
kMGPipeWiredFramebufferSubsystem |
|
||||
@@ -2097,17 +2107,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// (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.
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
@@ -2137,25 +2136,21 @@ namespace MobileGL::MG_Pipe {
|
||||
// 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.
|
||||
//
|
||||
// GetMaxTouchedTextureUnit is the sixth and its argument is different, which is
|
||||
// why it is written out: it is a plain Int, and set_sampler_views' Count IS that
|
||||
// value plus one. But the set is SUPPRESSED on an unchanged content hash and is
|
||||
// emitted only when NEW_SAMPLER_VIEWS fires, and that bit's shutter -
|
||||
// Mix(textureContent, GetTextureBindGeneration()) - does NOT move on a redundant
|
||||
// re-bind of the object a unit already holds, while the high-water mark DOES. So
|
||||
// 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.
|
||||
// GetMaxTouchedTextureUnit was the sixth and its argument was different - a plain
|
||||
// Int whose carrier (set_sampler_views' Count) is hash-suppressed while the
|
||||
// high-water mark still moves on a redundant re-bind. P5c rv RETIRED it from this
|
||||
// list (CONTRACT-P5C.md §5.3): set_context_values carries the mark as a VALUE of
|
||||
// its own, whole-record suppressed, so the lag the suppressor could introduce is
|
||||
// gone and the derivation's RECORD_SUPPLIED answer is honest.
|
||||
constexpr Bool EmittedCallSuppliesTheWholeField(MGPipeInputField field) {
|
||||
switch (field) {
|
||||
case MGPipeInputField::GetPixelStoreParameters:
|
||||
case MGPipeInputField::GetCurrentVertexAttribute:
|
||||
case MGPipeInputField::GetBoundVertexArray:
|
||||
case MGPipeInputField::GetFramebufferBindingSlot:
|
||||
case MGPipeInputField::GetImageTextureBinding:
|
||||
case MGPipeInputField::GetTextureUnitObject:
|
||||
case MGPipeInputField::GetProgramForDraw:
|
||||
case MGPipeInputField::GetProgramForDispatch:
|
||||
case MGPipeInputField::GetMaxTouchedTextureUnit:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
@@ -2175,6 +2170,16 @@ namespace MobileGL::MG_Pipe {
|
||||
case MGPipeInputField::GetPatchDefaultOuterLevel:
|
||||
case MGPipeInputField::GetPatchDefaultInnerLevel:
|
||||
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;
|
||||
default:
|
||||
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
|
||||
// consumer in P2.
|
||||
//
|
||||
// THE PAYLOAD AND ITS ONE MISSING HALF. A CurrentVertexAttributeValue is one value in
|
||||
// three views, and GLContext CONVERTS between them numerically, so "the bytes of one
|
||||
// view" is not the value: glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and
|
||||
// 0x3FC00000 in floatValue, and every glVertexAttribI4i/ui is a different pair again.
|
||||
// MGPAttribValue carries ValueClass for exactly this reason, so the client sends the
|
||||
// class the frontend actually wrote (GLContext::GetCurrentVertexAttributeClass) and
|
||||
// THAT class's own four words. What is still missing is the other half:
|
||||
// MGPipeApplySetVertexAttribDefaults (package A's file) memcpys the four words into
|
||||
// all three views regardless of ValueClass, which cannot reproduce the conversion.
|
||||
// THE PAYLOAD, SINCE P5c rv (CONTRACT-P5C.md §5.3). A CurrentVertexAttributeValue is
|
||||
// one value in three views, and GLContext CONVERTS between them numerically, so "the
|
||||
// bytes of one view" is not the value: glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in
|
||||
// intValue and 0x3FC00000 in floatValue, and every glVertexAttribI4i/ui is a different
|
||||
// pair again. MGPAttribValue now carries all three views VERBATIM
|
||||
// (FloatView/IntView/UintView) plus the class the frontend actually wrote
|
||||
// (GLContext::GetCurrentVertexAttributeClass), and MGPipeApplySetVertexAttribDefaults
|
||||
// writes each view from its own array - the cross-view conversion's authoritative
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
// The day the applier honours ValueClass the compare stops failing and the repair
|
||||
// stops happening, with no edit here.
|
||||
// Since rv the applier writes all three views verbatim, so the compare below is
|
||||
// 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;
|
||||
|
||||
// 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) {
|
||||
++g_attribDefaultRepairs;
|
||||
MGLOG_W_ONCE("MGPipe: MGPipeApplySetVertexAttribDefaults does not reproduce the "
|
||||
"carried value on this build (it ignores MGPAttribValue::ValueClass) "
|
||||
"- the client is keeping m_currentVertexAttribute authoritative");
|
||||
MGLOG_W_ONCE("MGPipe: MGPipeApplySetVertexAttribDefaults did not reproduce the "
|
||||
"carried three views on this build - the client is keeping "
|
||||
"m_currentVertexAttribute authoritative");
|
||||
MGPipeFillAccess::CopyField(gPipeInputs, ctx, MGPipeInputField::GetCurrentVertexAttribute);
|
||||
}
|
||||
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).
|
||||
//
|
||||
@@ -2669,6 +2737,19 @@ namespace MobileGL::MG_Pipe {
|
||||
// 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.
|
||||
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 Uint64 subsystem = MGPipeSubsystemForDirty(bit);
|
||||
return subsystem != 0 && (pushMask & subsystem) != 0 &&
|
||||
@@ -2808,6 +2889,19 @@ namespace MobileGL::MG_Pipe {
|
||||
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
|
||||
// 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
|
||||
@@ -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
|
||||
// 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.
|
||||
//
|
||||
// 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 &&
|
||||
(pushMask & subsystem) != 0 &&
|
||||
P4aFamilyHasItsConsumer(subsystem) &&
|
||||
P4aFamilyDependenciesAreSet(subsystem, pushMask) &&
|
||||
EmittedCallSuppliesTheWholeField(field) &&
|
||||
(applierDerives || AppliedWithoutDerivation(field));
|
||||
(applierDerives || AppliedWithoutDerivation(field)) &&
|
||||
(emitter != MGPipeFieldEmitter::SetContextValues ||
|
||||
contextValuesWireLive);
|
||||
if (!supplied) MGPipeFillAccess::CopyField(inputs, *ctx, field);
|
||||
#if MOBILEGL_PIPE_POISON
|
||||
// The value is copied either way; only the stamp is withheld for the omitted pair.
|
||||
|
||||
@@ -113,8 +113,10 @@ namespace MobileGL::MG_Pipe {
|
||||
// (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
|
||||
// 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
|
||||
// switches on MGPAttribValue::ValueClass the counter stops moving.
|
||||
// TrackerShippedEmitter asserts on this counter instead. Since P5c rv the record carries
|
||||
// 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
|
||||
// runs only when the call actually went out, which is only when an attribute default
|
||||
|
||||
@@ -45,7 +45,9 @@ namespace MobileGL::MG_Pipe {
|
||||
// One slot per kVarTail set_* (ARCHITECTURE.md 5.1's call list), PLUS
|
||||
// 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
|
||||
// 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.
|
||||
enum class MGPipeSuppressorSlot : Uint32 {
|
||||
SetVertexBuffers = 0, // P3a - wired, and its hash includes BaseInstance
|
||||
@@ -64,6 +66,12 @@ namespace MobileGL::MG_Pipe {
|
||||
SetStreamOutputTargets, // P4b
|
||||
SetVertexAttribDefaults, // P2 - the one consumer that is 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,
|
||||
};
|
||||
|
||||
|
||||
@@ -821,31 +821,30 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 m_walks[kMGPipeVerbClassCount]{};
|
||||
};
|
||||
|
||||
// ONE attribute default, flattened onto the wire (P2 brief D10). A named function rather
|
||||
// than four lines inside the emitter because this flattening is the whole correctness
|
||||
// question of set_vertex_attrib_defaults: a CurrentVertexAttributeValue is one value in
|
||||
// three views and GLContext converts NUMERICALLY between them, so four words alone are
|
||||
// not the value - glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in
|
||||
// floatValue. MGPAttribValue::ValueClass is what makes the four words readable again, and
|
||||
// TrackerAttribPayload pins that here instead of leaving it to the emitter's shape.
|
||||
// ONE attribute default, flattened onto the wire (P2 brief D10, AMENDED at P5c rv). A
|
||||
// named function rather than four lines inside the emitter because this flattening is the
|
||||
// whole correctness question of set_vertex_attrib_defaults: a CurrentVertexAttributeValue
|
||||
// is one value in three views and GLContext converts NUMERICALLY between them, so four
|
||||
// words alone are not the value - glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue
|
||||
// and 0x3FC00000 in floatValue. Since rv the record carries ALL THREE VIEWS VERBATIM
|
||||
// (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,
|
||||
const MG_State::GLState::CurrentVertexAttributeValue& value,
|
||||
Uint32 writtenClass, MGPAttribValue& out) {
|
||||
out = MGPAttribValue{};
|
||||
out.Location = location;
|
||||
out.ValueClass = static_cast<Uint8>(writtenClass);
|
||||
static_assert(sizeof(out.Data) == sizeof(value.floatValue), "MGPAttribValue::Data is four words");
|
||||
switch (writtenClass) {
|
||||
case MG_State::GLState::kVertexAttribValueClassInt:
|
||||
std::memcpy(out.Data, value.intValue.data(), sizeof(out.Data));
|
||||
break;
|
||||
case MG_State::GLState::kVertexAttribValueClassUint:
|
||||
std::memcpy(out.Data, value.uintValue.data(), sizeof(out.Data));
|
||||
break;
|
||||
default:
|
||||
std::memcpy(out.Data, value.floatValue.data(), sizeof(out.Data));
|
||||
break;
|
||||
}
|
||||
static_assert(sizeof(out.FloatView) == sizeof(value.floatValue),
|
||||
"MGPAttribValue's views are four words each");
|
||||
static_assert(sizeof(out.IntView) == sizeof(value.intValue) &&
|
||||
sizeof(out.UintView) == sizeof(value.uintValue),
|
||||
"MGPAttribValue's views are four words each");
|
||||
std::memcpy(out.FloatView, value.floatValue.data(), sizeof(out.FloatView));
|
||||
std::memcpy(out.IntView, value.intValue.data(), sizeof(out.IntView));
|
||||
std::memcpy(out.UintView, value.uintValue.data(), sizeof(out.UintView));
|
||||
}
|
||||
|
||||
// The monolith's one tracker. Under split there is one per client context; the context
|
||||
|
||||
@@ -212,33 +212,36 @@
|
||||
// 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.
|
||||
//
|
||||
// THE SIXTH IS A DIFFERENT ARGUMENT AND IT IS WORTH WRITING DOWN, because it looks like the
|
||||
// easy one. GetMaxTouchedTextureUnit is a plain Int, and set_sampler_views' Count IS that
|
||||
// value plus one (the second merge rule: a high-water mark is directly the count argument).
|
||||
// But the set is SUPPRESSED on an unchanged content hash and is emitted only when bit 12 fires,
|
||||
// and bit 12's shutter is Mix(textureContent, GetTextureBindGeneration()) - which does NOT
|
||||
// move on a redundant re-bind of the object a unit already holds, while the high-water mark
|
||||
// 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.
|
||||
// THE SIXTH left that arm at P5c rv: GetMaxTouchedTextureUnit is a plain Int whose problem was
|
||||
// never shape, it was the CARRIER - set_sampler_views' Count is the set's window, and the set
|
||||
// is suppressed on an unchanged content hash while the high-water mark still moves on a
|
||||
// redundant re-bind. set_context_values (CONTRACT-P5C.md §5.3) carries the mark as a VALUE of
|
||||
// its own, whole-record suppressed, so the mark rides an unsuppressed field and the row is
|
||||
// RECORD_SUPPLIED outright.
|
||||
//
|
||||
// FOUR ACCESSORS THAT MAP TO A P4a CALL ARE DELIBERATELY NOT HERE, for GetPixelStoreParameters'
|
||||
// reason - a row here says "this field is supplied", and for these it would be a half-truth:
|
||||
// GetActiveTextureUnit - glActiveTexture's selector. set_sampler_views carries the RESOLVED
|
||||
// per-unit set and no active-unit selector at all; nothing on the wire carries it.
|
||||
// GetTextureContextId - a context identity the backend keys its own tables on. No call
|
||||
// carries it and none should: it is the server's question about the client, not state.
|
||||
// GetTextureBindGeneration / GetSamplingResolutionGeneration - frontend SHUTTERS. What
|
||||
// replaces them server-side is the applier's own Serial, which is a different value with a
|
||||
// different owner; claiming the sets supply the generations would make the fill loop skip
|
||||
// two counters no record carries.
|
||||
// P5c rv ADDS EIGHT ROWS, and they are the whole of the value-class debt that was left
|
||||
// (CONTRACT-P5C.md §5.3): seven residual values no set_* call carried - the two texture-unit
|
||||
// counters, the 15-target touched-count array and the five transform-feedback values - plus
|
||||
// GetMaxTouchedTextureUnit re-homed from SetSamplerViews. All eight name SetContextValues, one
|
||||
// POD emitted at validate when any covered value moved, hash-suppressed whole. The OBJECT rows
|
||||
// (GetTextureUnitObject and its four P4a siblings above) stay BARRIER_PULLED with their
|
||||
// retiring phases; the three texture shutters (below) take no row here either - they carry no
|
||||
// wire value at all, the accessors answer the applier's own Serial.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
#define MGP_COVERAGE_EMITTED_LIST(X) \
|
||||
X(GetActiveTextureUnit, SetContextValues) \
|
||||
X(GetBlendColor, SetDynamicState) \
|
||||
X(GetBlendEquationIndexed, CreateRenderState) \
|
||||
X(GetBlendFuncIndexed, CreateRenderState) \
|
||||
X(GetBoundTransformFeedbackLifetimeId, SetContextValues) \
|
||||
X(GetBoundVertexArray, BindVertexElements) \
|
||||
X(GetClampReadColor, SetDynamicState) \
|
||||
X(GetClearColor, SetDynamicState) \
|
||||
@@ -254,7 +257,7 @@
|
||||
X(GetImageTextureBinding, SetShaderImages) \
|
||||
X(GetLineWidth, SetDynamicState) \
|
||||
X(GetLogicOp, CreateRenderState) \
|
||||
X(GetMaxTouchedTextureUnit, SetSamplerViews) \
|
||||
X(GetMaxTouchedTextureUnit, SetContextValues) \
|
||||
X(GetMinSampleShadingValue, CreateRenderState) \
|
||||
X(GetPatchDefaultInnerLevel, SetPatchState) \
|
||||
X(GetPatchDefaultOuterLevel, SetPatchState) \
|
||||
@@ -272,9 +275,14 @@
|
||||
X(GetScissorBox, SetDynamicState) \
|
||||
X(GetStencilState, CreateRenderState) \
|
||||
X(GetTextureUnitObject, SetSamplerViews) \
|
||||
X(GetTouchedBufferBindingPointCount, SetContextValues) \
|
||||
X(GetTransformFeedbackCapturedVertices, SetContextValues) \
|
||||
X(GetTransformFeedbackGeneration, SetContextValues) \
|
||||
X(GetViewport, SetDynamicState) \
|
||||
X(GetViewportIndexed, SetDynamicState) \
|
||||
X(IsCapabilityEnabled, CreateRenderState) \
|
||||
X(IsCapabilityEnabledIndexed, CreateRenderState)
|
||||
X(IsCapabilityEnabledIndexed, CreateRenderState) \
|
||||
X(IsTransformFeedbackActive, SetContextValues) \
|
||||
X(IsTransformFeedbackPaused, SetContextValues)
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
// the client for it. THIS CLASS IS DERIVED, NOT LISTED: it is
|
||||
// kMGPipeFieldEmittedBy != kNone (Coverage.def's MGP_COVERAGE_EMITTED_LIST)
|
||||
// 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
|
||||
// the generator - which is the only way this file can stay true as the
|
||||
// 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
|
||||
// phases for the two backends and flattening that would lose the half that matters.
|
||||
#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 */ \
|
||||
/* 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 */ \
|
||||
/* APPLIER_DERIVED below, for the reason written there. OpenRA adds no field to this set - */ \
|
||||
/* it widens the SITE set, not the field set. */ \
|
||||
X(GetActiveTextureUnit, BARRIER_PULLED, "P3b/P4b", \
|
||||
"the server answers from its own state; Coverage.def:215-219 says no call carries it") \
|
||||
/* 17). That union was 21 fields at P5; P5c rv (CONTRACT-P5C.md §5.3) retired NINE of them */ \
|
||||
/* to RECORD_SUPPLIED through set_context_values (the two texture-unit counters, the */ \
|
||||
/* touched-count array, the five XFB values) plus GetCurrentVertexAttribute (the amended */ \
|
||||
/* set_vertex_attrib_defaults payload), and moved the three texture SHUTTERS to */ \
|
||||
/* 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, */ \
|
||||
/* 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 */ \
|
||||
@@ -72,14 +79,6 @@
|
||||
"7 of 15 BufferTargets have no call; the field is one array over all 15") \
|
||||
X(GetBufferBindingPoint, BARRIER_PULLED, "P3b/P4b, P7", \
|
||||
"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 */ \
|
||||
/* (: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. */ \
|
||||
@@ -87,19 +86,6 @@
|
||||
"frontend BindingSlot<FramebufferObject> pointer") \
|
||||
X(GetImageTextureBinding, BARRIER_PULLED, "P3b/P4b, P7", \
|
||||
"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", \
|
||||
"frontend TextureUnit base pointer; 13 Espryt + 8 Magma sites") \
|
||||
/* 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. */ \
|
||||
X(GetProgramForDispatch, BARRIER_PULLED, "P7 (Magma), P8 (Espryt)", \
|
||||
"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 */ \
|
||||
/* six and the draw walk reads them regardless - which is exactly the case a field census */ \
|
||||
/* taken from "what the scenario does" rather than from the mask would miss. */ \
|
||||
X(IsTransformFeedbackActive, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
|
||||
"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") \
|
||||
/* The XFB family's one OBJECT row. XFB itself is off the reduced path, but kDraw's */ \
|
||||
/* may-read mask carries it and the draw walk reads it regardless - which is exactly the */ \
|
||||
/* case a field census taken from "what the scenario does" rather than from the mask would */ \
|
||||
/* miss. The family's five VALUE rows rode set_context_values out of this list at P5c rv. */ \
|
||||
X(GetTransformFeedbackProgram, BARRIER_PULLED, "P3b/P4b (Espryt), P7 (Magma)", \
|
||||
"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 ---- */ \
|
||||
/* 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 */ \
|
||||
/* 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 */ \
|
||||
@@ -139,6 +116,19 @@
|
||||
/* half of it). */ \
|
||||
X(GetPixelStoreParameters, APPLIER_DERIVED, "-", \
|
||||
"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 ----- */ \
|
||||
/* Three until P5b: GetProgramForDispatch moved up to BARRIER_PULLED when package i1 put */ \
|
||||
|
||||
@@ -936,14 +936,29 @@ namespace MobileGL::MG_Pipe {
|
||||
};
|
||||
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 {
|
||||
Uint32 Location;
|
||||
Uint8 ValueClass; // Float | Int | Uint | Double
|
||||
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.
|
||||
struct MGPVertexAttribDefaults {
|
||||
@@ -1634,13 +1649,16 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_ASSERT_POD(MGPCopyFromFramebuffer, 48);
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// P5c: the two 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.
|
||||
// P5c: the three appended rows (MG_Remote/CONTRACT-P5C.md §5). APPENDED, never inserted:
|
||||
// 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
|
||||
// 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
|
||||
@@ -1660,6 +1678,32 @@ namespace MobileGL::MG_Pipe {
|
||||
// object (§5.2), so a null handle arriving is Fatal{ProtocolCorruption,
|
||||
// "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)
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
@@ -213,6 +213,23 @@ namespace MobileGL::MG_Pipe {
|
||||
inputs.m_patchDefaultOuterLevel = outer;
|
||||
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.
|
||||
@@ -1189,6 +1206,12 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
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; }
|
||||
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.
|
||||
++g_applier.VertexBuffersSerial;
|
||||
++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
|
||||
// (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.VertexBuffersSerial;
|
||||
++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
|
||||
// too - a bound shader CSO whose record has just been dropped must not survive as a
|
||||
// handle the next call resolves against.
|
||||
@@ -1493,6 +1527,18 @@ namespace MobileGL::MG_Pipe {
|
||||
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) {
|
||||
PipeInputs& inputs = gPipeInputs;
|
||||
RenderStateParameters& working = MGPipeApplyAccess::RenderState(inputs);
|
||||
@@ -1581,11 +1627,16 @@ namespace MobileGL::MG_Pipe {
|
||||
continue;
|
||||
}
|
||||
PipeInputs::CurrentVertexAttributeValue& slot = slots[location];
|
||||
// The three views are always populated; which one a shader input consumes is
|
||||
// ClassifyVertexAttribType's answer, not the carrier's, so all three cross.
|
||||
std::memcpy(slot.floatValue.data(), value.Data, sizeof(slot.floatValue));
|
||||
std::memcpy(slot.intValue.data(), value.Data, sizeof(slot.intValue));
|
||||
std::memcpy(slot.uintValue.data(), value.Data, sizeof(slot.uintValue));
|
||||
// P5c (rv, CONTRACT-P5C.md §5.3): the record carries ALL THREE VIEWS VERBATIM, as
|
||||
// the frontend computed them, and the applier writes each view from its own array.
|
||||
// The pre-rv shape - one Data[4] memcpied into all three views - could not
|
||||
// reproduce the frontend's NUMERIC cross-view conversion (glVertexAttrib4f(loc,
|
||||
// 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) {
|
||||
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
|
||||
// for itself whether the backend needs a recreate.
|
||||
++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
|
||||
// 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;
|
||||
*record = MGPipeResourceRecord{};
|
||||
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
|
||||
// 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
|
||||
// clears its own copy, and the client never clears a server flag.
|
||||
++record->ParamsSerial;
|
||||
// P5c (rv): a parameter change is what the sampling-resolution shutter guarded.
|
||||
MGPipeApplierNoteTextureStateMoved();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2729,6 +2789,8 @@ namespace MobileGL::MG_Pipe {
|
||||
return;
|
||||
}
|
||||
++g_applier.SamplerViewsSerial;
|
||||
// P5c (rv): a texture bind is what the bind-generation shutter guarded.
|
||||
MGPipeApplierNoteTextureStateMoved();
|
||||
}
|
||||
|
||||
void MGPipeApplyBindSamplerStates(const MGPSamplerStates& hdr, const MGPipeHandle* tail) {
|
||||
@@ -2739,6 +2801,8 @@ namespace MobileGL::MG_Pipe {
|
||||
return;
|
||||
}
|
||||
++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) {
|
||||
@@ -2754,6 +2818,8 @@ namespace MobileGL::MG_Pipe {
|
||||
return;
|
||||
}
|
||||
++g_applier.ShaderImagesSerial;
|
||||
// P5c (rv): an image bind moves the texture bind generation's guarded set too.
|
||||
MGPipeApplierNoteTextureStateMoved();
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
|
||||
@@ -693,6 +693,27 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeHandle BoundShaderCso = kMGPipeNullHandle;
|
||||
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
|
||||
// ---- 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.
|
||||
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
|
||||
// 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
|
||||
@@ -829,6 +861,12 @@ namespace MobileGL::MG_Pipe {
|
||||
// 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.
|
||||
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
|
||||
// named by hdr.Mask, in ascending location order.
|
||||
void MGPipeApplySetVertexAttribDefaults(const MGPVertexAttribDefaults& hdr, const MGPAttribValue* tail);
|
||||
|
||||
@@ -45,14 +45,15 @@
|
||||
// appended server-side fence wait 1 and P5c's applier_reset 1
|
||||
// kCtxQuery 8 query object namespace 6, plus the appended timestamp pair 2
|
||||
// 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
|
||||
// P5c's object_death 1
|
||||
// kCtxVerb 18 3 context-reading transfer calls + the 10 commands, plus the five
|
||||
// P5b-appended verbs (bind_shader_image, patch_parameter,
|
||||
// bind_stream_output, set_storage_block_binding,
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
#define MGP_CALL_LIST(X) \
|
||||
@@ -256,7 +262,15 @@
|
||||
/* 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 */ \
|
||||
/* 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
|
||||
|
||||
// Explicitly NOT migrated (plan 4.4.6 / appendix A "explicit deletions"):
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
F(ShaderCso) F(Version) F(Blob)
|
||||
|
||||
#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) \
|
||||
F(Mask) F(Count)
|
||||
@@ -268,6 +268,14 @@
|
||||
#define MGP_FIELDS_MGPApplierReset(F) \
|
||||
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
|
||||
// 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
|
||||
@@ -385,7 +393,7 @@
|
||||
P(MGPSurfaceInfo) \
|
||||
P(MGPImageBind) P(MGPPatchParameter) P(MGPStreamOutputBind) P(MGPStorageBlockBinding) \
|
||||
P(MGPCopyFromFramebuffer) \
|
||||
P(MGPApplierReset) \
|
||||
P(MGPApplierReset) P(MGPContextValues) \
|
||||
P(RenderStateParameters) P(PixelStoreParameters) P(SamplerParameters) P(PerBufferBlendState) \
|
||||
P(StencilFaceState) \
|
||||
P(DynamicBackendParameters) P(MGHostSpan) \
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// 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.
|
||||
// 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; }
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// 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
|
||||
// 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
|
||||
// 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(SetPixelPackState, MGPPixelPackState)
|
||||
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 --------------------------------------------------
|
||||
MGP_MONO_BLOB(CreateRenderState, MGPRenderStateDesc)
|
||||
@@ -341,6 +346,7 @@ namespace MobileGL::MG_Pipe {
|
||||
gMGPipeContext.SetVertexAttribDefaults = &Mono_SetVertexAttribDefaults;
|
||||
gMGPipeContext.SetPixelPackState = &Mono_SetPixelPackState;
|
||||
gMGPipeContext.SetPatchState = &Mono_SetPatchState;
|
||||
gMGPipeContext.SetContextValues = &Mono_SetContextValues;
|
||||
gMGPipeContext.SetResidualValueState = &Mono_SetResidualValueState;
|
||||
gMGPipeContext.SetTextureParams = &Mono_SetTextureParams;
|
||||
gMGPipeContext.ResourceSubData = &Mono_ResourceSubData;
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// 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.
|
||||
//
|
||||
// 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);
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// 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
|
||||
@@ -307,6 +308,14 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_SetPixelPackState(&pack);
|
||||
}
|
||||
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,
|
||||
const MGPAttribValue* tail) {
|
||||
MGP_SetVertexAttribDefaults(&hdr, tail, hdr.Count);
|
||||
|
||||
@@ -34,7 +34,7 @@ inline constexpr const char* kMGPipeFieldOwnershipNames[] = {
|
||||
};
|
||||
|
||||
inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCount] = {
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetActiveTextureUnit
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetActiveTextureUnit
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetBlendColor
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetBlendEquationIndexed
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetBlendFuncIndexed
|
||||
@@ -43,14 +43,14 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingSlot
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPoint
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetBufferBindingPointCount
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTouchedBufferBindingPointCount
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetTouchedBufferBindingPointCount
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetClampReadColor
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetClearColor
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetClearDepth
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetClearStencil
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetColorMaskIndexed
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetCullFaceMode
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetCurrentVertexAttribute
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetCurrentVertexAttribute
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetDepthFunc
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetDepthMask
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetDepthRangeIndexed
|
||||
@@ -58,7 +58,7 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetImageTextureBinding
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetLineWidth
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetLogicOp
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetMaxTouchedTextureUnit
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetMaxTouchedTextureUnit
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetMinSampleShadingValue
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultInnerLevel
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetPatchDefaultOuterLevel
|
||||
@@ -75,33 +75,33 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetProvokingVertexMode
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParameters
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetRenderStateParametersVersion
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetSamplingResolutionGeneration
|
||||
MGPipeFieldOwnership::kApplierDerived, // GetSamplingResolutionGeneration
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetScissorBox
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetStencilState
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTextureBindGeneration
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTextureContextId
|
||||
MGPipeFieldOwnership::kApplierDerived, // GetTextureBindGeneration
|
||||
MGPipeFieldOwnership::kApplierDerived, // GetTextureContextId
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTextureObject
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTextureUnitObject
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackCapturedVertices
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackGeneration
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetTransformFeedbackCapturedVertices
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetTransformFeedbackGeneration
|
||||
MGPipeFieldOwnership::kFatal, // GetTransformFeedbackPausedPrimitiveCounter
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetTransformFeedbackProgram
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetViewport
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetViewportIndexed
|
||||
MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabled
|
||||
MGPipeFieldOwnership::kRecordSupplied, // IsCapabilityEnabledIndexed
|
||||
MGPipeFieldOwnership::kBarrierPulled, // IsTransformFeedbackActive
|
||||
MGPipeFieldOwnership::kBarrierPulled, // IsTransformFeedbackPaused
|
||||
MGPipeFieldOwnership::kRecordSupplied, // IsTransformFeedbackActive
|
||||
MGPipeFieldOwnership::kRecordSupplied, // IsTransformFeedbackPaused
|
||||
MGPipeFieldOwnership::kFatal, // InvalidateCompileEnv
|
||||
MGPipeFieldOwnership::kBarrierPulled, // ValidateProgramName
|
||||
MGPipeFieldOwnership::kBarrierPulled, // RecordError
|
||||
MGPipeFieldOwnership::kBarrierPulled, // GetBoundTransformFeedbackLifetimeId
|
||||
MGPipeFieldOwnership::kRecordSupplied, // GetBoundTransformFeedbackLifetimeId
|
||||
MGPipeFieldOwnership::kBarrierPulled, // HasOpenTransformFeedbackSpan
|
||||
};
|
||||
|
||||
// The ROADMAP phase whose row retires the pull. "-" for every class but BARRIER-PULLED.
|
||||
inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] = {
|
||||
"P3b/P4b", // GetActiveTextureUnit
|
||||
"-", // GetActiveTextureUnit
|
||||
"-", // GetBlendColor
|
||||
"-", // GetBlendEquationIndexed
|
||||
"-", // GetBlendFuncIndexed
|
||||
@@ -110,14 +110,14 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
|
||||
"P8 (indirect), P9 (readback), P13 (transfer)", // GetBufferBindingSlot
|
||||
"P3b/P4b, P7", // GetBufferBindingPoint
|
||||
"P7/P13", // GetBufferBindingPointCount
|
||||
"P3b/P4b", // GetTouchedBufferBindingPointCount
|
||||
"-", // GetTouchedBufferBindingPointCount
|
||||
"-", // GetClampReadColor
|
||||
"-", // GetClearColor
|
||||
"-", // GetClearDepth
|
||||
"-", // GetClearStencil
|
||||
"-", // GetColorMaskIndexed
|
||||
"-", // GetCullFaceMode
|
||||
"P3b/P4b", // GetCurrentVertexAttribute
|
||||
"-", // GetCurrentVertexAttribute
|
||||
"-", // GetDepthFunc
|
||||
"-", // GetDepthMask
|
||||
"-", // GetDepthRangeIndexed
|
||||
@@ -125,7 +125,7 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
|
||||
"P3b/P4b, P7", // GetImageTextureBinding
|
||||
"-", // GetLineWidth
|
||||
"-", // GetLogicOp
|
||||
"P3b/P4b", // GetMaxTouchedTextureUnit
|
||||
"-", // GetMaxTouchedTextureUnit
|
||||
"-", // GetMinSampleShadingValue
|
||||
"-", // GetPatchDefaultInnerLevel
|
||||
"-", // GetPatchDefaultOuterLevel
|
||||
@@ -142,27 +142,27 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] =
|
||||
"-", // GetProvokingVertexMode
|
||||
"-", // GetRenderStateParameters
|
||||
"-", // GetRenderStateParametersVersion
|
||||
"P3b/P4b", // GetSamplingResolutionGeneration
|
||||
"-", // GetSamplingResolutionGeneration
|
||||
"-", // GetScissorBox
|
||||
"-", // GetStencilState
|
||||
"P3b/P4b", // GetTextureBindGeneration
|
||||
"P3b/P4b", // GetTextureContextId
|
||||
"-", // GetTextureBindGeneration
|
||||
"-", // GetTextureContextId
|
||||
"P7", // GetTextureObject
|
||||
"P3b/P4b, P7", // GetTextureUnitObject
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackCapturedVertices
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackGeneration
|
||||
"-", // GetTransformFeedbackCapturedVertices
|
||||
"-", // GetTransformFeedbackGeneration
|
||||
"-", // GetTransformFeedbackPausedPrimitiveCounter
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // GetTransformFeedbackProgram
|
||||
"-", // GetViewport
|
||||
"-", // GetViewportIndexed
|
||||
"-", // IsCapabilityEnabled
|
||||
"-", // IsCapabilityEnabledIndexed
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // IsTransformFeedbackActive
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // IsTransformFeedbackPaused
|
||||
"-", // IsTransformFeedbackActive
|
||||
"-", // IsTransformFeedbackPaused
|
||||
"-", // InvalidateCompileEnv
|
||||
"P9", // ValidateProgramName
|
||||
"P9", // RecordError
|
||||
"P3b/P4b (Espryt), P7 (Magma)", // GetBoundTransformFeedbackLifetimeId
|
||||
"-", // GetBoundTransformFeedbackLifetimeId
|
||||
"P7/P9", // HasOpenTransformFeedbackSpan
|
||||
};
|
||||
|
||||
@@ -301,8 +301,8 @@ inline constexpr SizeT kMGPipeVerbBoundaryOpCount = 23;
|
||||
inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3;
|
||||
|
||||
// The class sizes, as constants a test can pin without recounting the table.
|
||||
inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 32;
|
||||
inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 1;
|
||||
inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 27;
|
||||
inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 41;
|
||||
inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 4;
|
||||
inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 15;
|
||||
inline constexpr SizeT kMGPipeFatalFieldCount = 3;
|
||||
static_assert(kMGPipeRecordSuppliedFieldCount + kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount == kMGPipeInputFieldCount, "the four class sizes do not partition the field set");
|
||||
|
||||
@@ -308,6 +308,7 @@ enum class MGPipeFieldEmitter : Uint8 {
|
||||
BindRenderState,
|
||||
BindVertexElements,
|
||||
CreateRenderState,
|
||||
SetContextValues,
|
||||
SetDispatchProgram,
|
||||
SetDrawProgram,
|
||||
SetDynamicState,
|
||||
@@ -323,6 +324,7 @@ inline constexpr const char* kMGPipeFieldEmitterNames[] = {
|
||||
"BindRenderState",
|
||||
"BindVertexElements",
|
||||
"CreateRenderState",
|
||||
"SetContextValues",
|
||||
"SetDispatchProgram",
|
||||
"SetDrawProgram",
|
||||
"SetDynamicState",
|
||||
@@ -334,7 +336,7 @@ inline constexpr const char* kMGPipeFieldEmitterNames[] = {
|
||||
};
|
||||
|
||||
inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount] = {
|
||||
MGPipeFieldEmitter::kNone, // GetActiveTextureUnit
|
||||
MGPipeFieldEmitter::SetContextValues, // GetActiveTextureUnit
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetBlendColor
|
||||
MGPipeFieldEmitter::CreateRenderState, // GetBlendEquationIndexed
|
||||
MGPipeFieldEmitter::CreateRenderState, // GetBlendFuncIndexed
|
||||
@@ -343,7 +345,7 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
|
||||
MGPipeFieldEmitter::kNone, // GetBufferBindingSlot
|
||||
MGPipeFieldEmitter::kNone, // GetBufferBindingPoint
|
||||
MGPipeFieldEmitter::kNone, // GetBufferBindingPointCount
|
||||
MGPipeFieldEmitter::kNone, // GetTouchedBufferBindingPointCount
|
||||
MGPipeFieldEmitter::SetContextValues, // GetTouchedBufferBindingPointCount
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetClampReadColor
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetClearColor
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetClearDepth
|
||||
@@ -358,7 +360,7 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
|
||||
MGPipeFieldEmitter::SetShaderImages, // GetImageTextureBinding
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetLineWidth
|
||||
MGPipeFieldEmitter::CreateRenderState, // GetLogicOp
|
||||
MGPipeFieldEmitter::SetSamplerViews, // GetMaxTouchedTextureUnit
|
||||
MGPipeFieldEmitter::SetContextValues, // GetMaxTouchedTextureUnit
|
||||
MGPipeFieldEmitter::CreateRenderState, // GetMinSampleShadingValue
|
||||
MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultInnerLevel
|
||||
MGPipeFieldEmitter::SetPatchState, // GetPatchDefaultOuterLevel
|
||||
@@ -382,23 +384,23 @@ inline constexpr MGPipeFieldEmitter kMGPipeFieldEmittedBy[kMGPipeInputFieldCount
|
||||
MGPipeFieldEmitter::kNone, // GetTextureContextId
|
||||
MGPipeFieldEmitter::kNone, // GetTextureObject
|
||||
MGPipeFieldEmitter::SetSamplerViews, // GetTextureUnitObject
|
||||
MGPipeFieldEmitter::kNone, // GetTransformFeedbackCapturedVertices
|
||||
MGPipeFieldEmitter::kNone, // GetTransformFeedbackGeneration
|
||||
MGPipeFieldEmitter::SetContextValues, // GetTransformFeedbackCapturedVertices
|
||||
MGPipeFieldEmitter::SetContextValues, // GetTransformFeedbackGeneration
|
||||
MGPipeFieldEmitter::kNone, // GetTransformFeedbackPausedPrimitiveCounter
|
||||
MGPipeFieldEmitter::kNone, // GetTransformFeedbackProgram
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetViewport
|
||||
MGPipeFieldEmitter::SetDynamicState, // GetViewportIndexed
|
||||
MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabled
|
||||
MGPipeFieldEmitter::CreateRenderState, // IsCapabilityEnabledIndexed
|
||||
MGPipeFieldEmitter::kNone, // IsTransformFeedbackActive
|
||||
MGPipeFieldEmitter::kNone, // IsTransformFeedbackPaused
|
||||
MGPipeFieldEmitter::SetContextValues, // IsTransformFeedbackActive
|
||||
MGPipeFieldEmitter::SetContextValues, // IsTransformFeedbackPaused
|
||||
MGPipeFieldEmitter::kNone, // InvalidateCompileEnv
|
||||
MGPipeFieldEmitter::kNone, // ValidateProgramName
|
||||
MGPipeFieldEmitter::kNone, // RecordError
|
||||
MGPipeFieldEmitter::kNone, // GetBoundTransformFeedbackLifetimeId
|
||||
MGPipeFieldEmitter::SetContextValues, // GetBoundTransformFeedbackLifetimeId
|
||||
MGPipeFieldEmitter::kNone, // HasOpenTransformFeedbackSpan
|
||||
};
|
||||
inline constexpr SizeT kMGPipeEmittedFieldCount = 40;
|
||||
inline constexpr SizeT kMGPipeEmittedFieldCount = 47;
|
||||
|
||||
struct MGPipeFilledState {
|
||||
Uint64 CurrentVerbSerial;
|
||||
|
||||
@@ -29,7 +29,7 @@ struct MGPipeScreen {
|
||||
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).
|
||||
struct MGPipeContext {
|
||||
void (*QueryCreate)(const MGPQueryDesc* payload);
|
||||
@@ -98,11 +98,12 @@ struct MGPipeContext {
|
||||
void (*SetStorageBlockBinding)(const MGPStorageBlockBinding* payload, const void* blobBytes, Uint64 blobByteCount);
|
||||
void (*CopyFramebufferToTexture)(const MGPCopyFromFramebuffer* payload);
|
||||
void (*ObjectDeath)(const MGPHandleOnly* payload);
|
||||
void (*SetContextValues)(const MGPContextValues* payload);
|
||||
};
|
||||
|
||||
inline constexpr SizeT kMGPipeScreenCallCount = 12;
|
||||
inline constexpr SizeT kMGPipeContextCallCount = 66;
|
||||
inline constexpr SizeT kMGPipeCallCount = 78;
|
||||
inline constexpr SizeT kMGPipeContextCallCount = 67;
|
||||
inline constexpr SizeT kMGPipeCallCount = 79;
|
||||
|
||||
// A table that is not exactly its call count of function pointers has grown a
|
||||
// member that no generator knows about.
|
||||
|
||||
@@ -328,3 +328,7 @@ inline void MGP_ApplierReset(const MGPApplierReset* payload) {
|
||||
inline void MGP_ObjectDeath(const MGPHandleOnly* payload) {
|
||||
gMGPipeContext.ObjectDeath(payload);
|
||||
}
|
||||
|
||||
inline void MGP_SetContextValues(const MGPContextValues* payload) {
|
||||
gMGPipeContext.SetContextValues(payload);
|
||||
}
|
||||
|
||||
@@ -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 MGPCopyFromFramebuffer& a, const MGPCopyFromFramebuffer& 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 PixelStoreParameters& a, const PixelStoreParameters& 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 <>
|
||||
struct MGPipeHasFieldVerifier<MGPApplierReset> : std::true_type {};
|
||||
template <>
|
||||
struct MGPipeHasFieldVerifier<MGPContextValues> : std::true_type {};
|
||||
template <>
|
||||
struct MGPipeHasFieldVerifier<RenderStateParameters> : std::true_type {};
|
||||
template <>
|
||||
struct MGPipeHasFieldVerifier<PixelStoreParameters> : std::true_type {};
|
||||
@@ -670,6 +673,11 @@ inline Bool MGPipeVerify(const MGPApplierReset& a, const MGPApplierReset& b, con
|
||||
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) {
|
||||
MGP_FIELDS_RenderStateParameters(MGP_VERIFY_FIELD)
|
||||
return true;
|
||||
@@ -717,4 +725,4 @@ inline Bool MGPipeVerify(const MGPVertexBindingPointWire& a, const MGPVertexBind
|
||||
|
||||
#undef MGP_VERIFY_FIELD
|
||||
|
||||
inline constexpr SizeT kMGPipeVerifiedPayloadCount = 78;
|
||||
inline constexpr SizeT kMGPipeVerifiedPayloadCount = 79;
|
||||
|
||||
@@ -148,7 +148,8 @@ enum class MGPWireOp : Uint16 {
|
||||
CopyFramebufferToTexture = 76,
|
||||
ApplierReset = 77,
|
||||
ObjectDeath = 78,
|
||||
kOpCount = 79,
|
||||
SetContextValues = 79,
|
||||
kOpCount = 80,
|
||||
};
|
||||
|
||||
// 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),
|
||||
/* 77 ApplierReset */ static_cast<Uint32>(kNone),
|
||||
/* 78 ObjectDeath */ static_cast<Uint32>(kNone),
|
||||
/* 79 SetContextValues */ static_cast<Uint32>(kNone),
|
||||
};
|
||||
static_assert(sizeof(kMGPipeCallFlags) / sizeof(kMGPipeCallFlags[0]) ==
|
||||
static_cast<SizeT>(MGPWireOp::kOpCount),
|
||||
@@ -925,6 +927,14 @@ static_assert(sizeof(MGPWireRec_ObjectDeath) ==
|
||||
((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)),
|
||||
"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) {
|
||||
MGLOG_F("MGPipe: protocol corruption applying %s: size=%llu remaining=%llu", call,
|
||||
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:
|
||||
MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ObjectDeath, "ObjectDeath");
|
||||
break;
|
||||
case MGPWireOp::SetContextValues:
|
||||
MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetContextValues, "SetContextValues");
|
||||
break;
|
||||
case MGPWireOp::kInvalid:
|
||||
case MGPWireOp::kOpCount:
|
||||
default:
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
@@ -196,6 +196,11 @@ namespace MobileGL::MG_Remote::Client {
|
||||
MGP_WIRE_PLAIN(SetIndexBuffer, MGPIndexBuffer, Context)
|
||||
MGP_WIRE_PLAIN(SetPixelPackState, MGPPixelPackState, 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 --------------------------------------------------
|
||||
MGP_WIRE_BLOB(CreateRenderState, MGPRenderStateDesc, Blob)
|
||||
@@ -541,6 +546,17 @@ namespace MobileGL::MG_Remote::Client {
|
||||
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) {
|
||||
using MG_Pipe::MGPipeHandle;
|
||||
|
||||
@@ -628,6 +644,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
gMGPipeContext.SetVertexAttribDefaults = &Wire_SetVertexAttribDefaults;
|
||||
gMGPipeContext.SetPixelPackState = &Wire_SetPixelPackState;
|
||||
gMGPipeContext.SetPatchState = &Wire_SetPatchState;
|
||||
gMGPipeContext.SetContextValues = &Wire_SetContextValues;
|
||||
gMGPipeContext.SetResidualValueState = &Wire_SetResidualValueState;
|
||||
gMGPipeContext.SetTextureParams = &Wire_SetTextureParams;
|
||||
gMGPipeContext.ResourceSubData = &Wire_ResourceSubData;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
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.
|
||||
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.
|
||||
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
|
||||
// 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".
|
||||
@@ -106,6 +106,12 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// caller then makes the direct call the record replaced.
|
||||
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
|
||||
// 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
|
||||
|
||||
@@ -783,6 +783,11 @@ namespace MobileGL::MG_Remote::Server {
|
||||
static_cast<GLenum>(bind.Access),
|
||||
static_cast<GLenum>(bind.Format));
|
||||
++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;
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +241,8 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
X(SetStorageBlockBinding, MGPStorageBlockBinding) \
|
||||
X(CopyFramebufferToTexture, MGPCopyFromFramebuffer) \
|
||||
X(ApplierReset, MGPApplierReset) \
|
||||
X(ObjectDeath, MGPHandleOnly)
|
||||
X(ObjectDeath, MGPHandleOnly) \
|
||||
X(SetContextValues, MGPContextValues)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -2101,6 +2102,17 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
return m_verbs != nullptr &&
|
||||
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:
|
||||
// Class C, wave 3 (census-classC.md "static cross"); not a verb (FillPoints.def:21).
|
||||
return false;
|
||||
|
||||
@@ -256,8 +256,9 @@ namespace MobileGL {
|
||||
// four words on a wire are not the value unless the class travels with them:
|
||||
// glVertexAttrib4f(loc, 1.5f, ...) leaves 1 in intValue and 0x3FC00000 in
|
||||
// floatValue. set_vertex_attrib_defaults carries this as MGPAttribValue's
|
||||
// ValueClass so the applier can redo the conversion instead of memcpying one
|
||||
// view into all three.
|
||||
// ValueClass AND, since P5c rv (CONTRACT-P5C.md §5.3), all three views
|
||||
// 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
|
||||
// because that struct is mirrored into PipeInputs and compared there by a
|
||||
|
||||
@@ -197,11 +197,15 @@ TEST_F(FieldOwnershipTest, TheClassSizesPartitionTheFieldSet) {
|
||||
EXPECT_EQ(counted[static_cast<SizeT>(MGPipeFieldOwnership::kBarrierPulled)],
|
||||
kMGPipeBarrierPulledFieldCount);
|
||||
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
|
||||
// are served by NO pushed record - 24 non-sticky plus the seven sticky - so 32 are.
|
||||
EXPECT_EQ(kMGPipeRecordSuppliedFieldCount, SizeT{32});
|
||||
// The census's own arithmetic (scout-unmigrated-census section 2.1), updated by P5c rv
|
||||
// (CONTRACT-P5C.md §5.3): rv moved the NINE value-class rows to RECORD-SUPPLIED through
|
||||
// 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,
|
||||
SizeT{31});
|
||||
SizeT{22});
|
||||
}
|
||||
|
||||
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
|
||||
// kClear's 7, kDraw's 19 and kReadback's 12). Twenty of them are BARRIER-PULLED; the
|
||||
// twenty-first is GetPixelStoreParameters, whose PACK half the applier writes and whose UNPACK
|
||||
// half has no carrier and no backend reader at all.
|
||||
// kClear's 7, kDraw's 19 and kReadback's 12) - AS P5c rv LEFT THEM (CONTRACT-P5C.md §5.3):
|
||||
// NINE moved to RECORD-SUPPLIED through set_context_values (the two texture-unit counters,
|
||||
// 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) {
|
||||
const MGPipeInputField pulled[] = {
|
||||
MGPipeInputField::GetActiveTextureUnit,
|
||||
MGPipeInputField::GetBoundVertexArray,
|
||||
MGPipeInputField::GetBufferBindingSlot,
|
||||
MGPipeInputField::GetBufferBindingPoint,
|
||||
MGPipeInputField::GetTouchedBufferBindingPointCount,
|
||||
MGPipeInputField::GetCurrentVertexAttribute,
|
||||
MGPipeInputField::GetFramebufferBindingSlot,
|
||||
MGPipeInputField::GetImageTextureBinding,
|
||||
MGPipeInputField::GetMaxTouchedTextureUnit,
|
||||
MGPipeInputField::GetProgramForDraw,
|
||||
MGPipeInputField::GetSamplingResolutionGeneration,
|
||||
MGPipeInputField::GetTextureBindGeneration,
|
||||
MGPipeInputField::GetTextureContextId,
|
||||
MGPipeInputField::GetTextureUnitObject,
|
||||
MGPipeInputField::GetTransformFeedbackCapturedVertices,
|
||||
MGPipeInputField::GetTransformFeedbackGeneration,
|
||||
MGPipeInputField::GetProgramForDraw,
|
||||
MGPipeInputField::GetProgramForDispatch,
|
||||
MGPipeInputField::GetTransformFeedbackProgram,
|
||||
MGPipeInputField::GetBoundTransformFeedbackLifetimeId,
|
||||
MGPipeInputField::IsTransformFeedbackActive,
|
||||
MGPipeInputField::IsTransformFeedbackPaused,
|
||||
};
|
||||
for (const auto field : pulled) {
|
||||
EXPECT_EQ(MGPipeFieldOwnershipOf(field), MGPipeFieldOwnership::kBarrierPulled)
|
||||
<< 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),
|
||||
MGPipeFieldOwnership::kApplierDerived);
|
||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters, 0u),
|
||||
@@ -271,6 +295,36 @@ TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
|
||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch),
|
||||
MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDraw))
|
||||
<< "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) {
|
||||
@@ -395,19 +449,76 @@ TEST_F(FieldOwnershipTest, ARecordSuppliedFieldIsReadableAfterAServerStamp) {
|
||||
(void)gPipeInputs.GetRenderStateParameters();
|
||||
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{0});
|
||||
// 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);
|
||||
(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});
|
||||
}
|
||||
|
||||
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});
|
||||
(void)gPipeInputs.GetActiveTextureUnit();
|
||||
(void)gPipeInputs.GetBoundVertexArray();
|
||||
EXPECT_EQ(MGPipeResidualPullCount(), Uint64{1});
|
||||
(void)gPipeInputs.GetTextureContextId();
|
||||
(void)gPipeInputs.GetMaxTouchedTextureUnit();
|
||||
(void)gPipeInputs.GetProgramForDraw();
|
||||
(void)gPipeInputs.GetTransformFeedbackProgram();
|
||||
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
|
||||
// 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.
|
||||
// (P5c rv: the exemplar moved - GetActiveTextureUnit is RECORD-SUPPLIED since rv and would
|
||||
// say nothing about the pulled set here.)
|
||||
TEST_F(FieldOwnershipTest, ABarrierPulledFieldOutsideTheVerbsClassIsStillFatal) {
|
||||
#if MGTEST_HAVE_FORK
|
||||
const ChildResult r = RunInChild([] {
|
||||
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;
|
||||
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;
|
||||
EXPECT_EQ(r.Log.find("BARRIER-PULLED"), std::string::npos) << r.Log;
|
||||
#else
|
||||
@@ -473,8 +586,8 @@ TEST_F(FieldOwnershipTest, ResidualPullsReachThePublishedPerFrameCounter) {
|
||||
namespace PS = MG_Util::PipeStats;
|
||||
PS::SetEnabledForTesting(true);
|
||||
PS::ResetForTesting();
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
|
||||
(void)gPipeInputs.GetActiveTextureUnit();
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
|
||||
(void)gPipeInputs.GetBoundVertexArray();
|
||||
EXPECT_EQ(PS::FrameCalls(PS::CallClass::ResidualPulls), Uint64{1});
|
||||
EXPECT_NE(PS::FormatWindowLine().find("rsp="), std::string::npos) << PS::FormatWindowLine();
|
||||
PS::ResetForTesting();
|
||||
@@ -484,28 +597,30 @@ TEST_F(FieldOwnershipTest, ResidualPullsReachThePublishedPerFrameCounter) {
|
||||
#if MGTEST_HAVE_FORK
|
||||
|
||||
// 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) {
|
||||
const ChildResult r = RunInChild([] {
|
||||
MG_Config::Ipc.StrictErrors = true;
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
|
||||
(void)gPipeInputs.GetActiveTextureUnit();
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
|
||||
(void)gPipeInputs.GetBoundVertexArray();
|
||||
});
|
||||
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)
|
||||
<< 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;
|
||||
// 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.
|
||||
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) {
|
||||
const ChildResult r = RunInChild([] {
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::ReadPixels);
|
||||
(void)gPipeInputs.GetActiveTextureUnit();
|
||||
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
|
||||
(void)gPipeInputs.GetBoundVertexArray();
|
||||
if (MGPipeResidualPullCount() != 1) ::_exit(7);
|
||||
});
|
||||
ASSERT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
|
||||
|
||||
@@ -127,10 +127,11 @@ TEST(PipeCatalogue, GeneratedTablesHoldTheWholeCatalogue) {
|
||||
// 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
|
||||
// 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<kCtxQuery>(), 8u);
|
||||
EXPECT_EQ(ClassCount<kCtxCso>(), 13u);
|
||||
EXPECT_EQ(ClassCount<kCtxState>(), 17u);
|
||||
EXPECT_EQ(ClassCount<kCtxState>(), 18u);
|
||||
EXPECT_EQ(ClassCount<kCtxObject>(), 10u);
|
||||
// 13 + the five P5b-appended verbs (MG_Remote/CONTRACT-P5B.md): bind_shader_image,
|
||||
// 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).
|
||||
//
|
||||
// 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
|
||||
// P5b's five sink-only verbs): the other 37 have an applier, R-17 installs adapters over them,
|
||||
// NO MGPipeApply* ENTRY POINT (79 - the 38 that have one; the number was 34 at P5, 39 after
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
@@ -209,8 +211,9 @@ TEST(PipeCatalogue, ExactlyTheRoutedRowsAreInstalledAndTheRestAreStillNull) {
|
||||
EXPECT_EQ(installed + nulls, static_cast<SizeT>(kMGPipeCallCount));
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// 33 + 4 = 37, 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
|
||||
// 34 + 4 = 38, and the split is the honest shape of R-17 rather than an implementation
|
||||
// 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
|
||||
// generated signature and go in the hand-written escape table beside them
|
||||
// (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
|
||||
// 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.
|
||||
EXPECT_EQ(installed, 33u) << "the routed rows and the applier's entry points disagree";
|
||||
EXPECT_EQ(nulls, static_cast<SizeT>(kMGPipeCallCount) - 33u);
|
||||
EXPECT_EQ(installed, 34u) << "the routed rows and the applier's entry points disagree";
|
||||
EXPECT_EQ(nulls, static_cast<SizeT>(kMGPipeCallCount) - 34u);
|
||||
const void* const* escapes = reinterpret_cast<const void* const*>(&gMGPipeRouteEscapes);
|
||||
SizeT escapesInstalled = 0;
|
||||
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 "
|
||||
"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";
|
||||
|
||||
// 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.
|
||||
EXPECT_EQ(static_cast<Uint16>(MGPWireOp::ApplierReset), 77);
|
||||
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::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
|
||||
// else, and the extended draw row keeps its two flags.
|
||||
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.
|
||||
EXPECT_EQ(sizeof(MGPApplierReset), 8u);
|
||||
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.
|
||||
EXPECT_EQ(static_cast<Uint32>(kDrawIsIndirect), 1u << 5);
|
||||
EXPECT_EQ(static_cast<Uint32>(kDrawHasUserIndices) & static_cast<Uint32>(kDrawIsIndirect), 0u);
|
||||
@@ -799,8 +816,9 @@ TEST(PipeCatalogue, SixValueStructsHaveFieldLists) {
|
||||
// MGPPatchParameter, MGPStreamOutputBind, MGPStorageBlockBinding, MGPCopyFromFramebuffer),
|
||||
// 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
|
||||
// MGPHandleOnly, which has had a list since P0 - so: 78.
|
||||
EXPECT_EQ(kMGPipeVerifiedPayloadCount, 78u);
|
||||
// MGPHandleOnly, which has had a list since P0 - and rv added set_context_values'
|
||||
// MGPContextValues (§5.3): 79.
|
||||
EXPECT_EQ(kMGPipeVerifiedPayloadCount, 79u);
|
||||
static_assert(MGPipeHasFieldVerifier<RenderStateParameters>::value);
|
||||
static_assert(MGPipeHasFieldVerifier<PixelStoreParameters>::value);
|
||||
static_assert(MGPipeHasFieldVerifier<PerBufferBlendState>::value);
|
||||
|
||||
@@ -1304,6 +1304,9 @@ namespace {
|
||||
// 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
|
||||
// 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);
|
||||
MGPVertexAttribDefaults hdr{};
|
||||
@@ -1311,16 +1314,26 @@ namespace {
|
||||
hdr.Count = 2;
|
||||
MGPAttribValue tail[2]{};
|
||||
tail[0].Location = 2;
|
||||
tail[0].ValueClass = MG_State::GLState::kVertexAttribValueClassFloat;
|
||||
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].ValueClass = MG_State::GLState::kVertexAttribValueClassFloat;
|
||||
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);
|
||||
|
||||
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[0], 1.5f);
|
||||
EXPECT_EQ(gPipeInputs.GetCurrentVertexAttribute(2).floatValue[3], 4.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
|
||||
|
||||
@@ -892,30 +892,35 @@ namespace {
|
||||
const MGPAttribValue value = PayloadFor(3);
|
||||
EXPECT_EQ(value.Location, 3u);
|
||||
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
|
||||
EXPECT_EQ(value.Data[0], Word(1.5f));
|
||||
EXPECT_EQ(value.Data[1], Word(-2.5f));
|
||||
// The defect this exists to stop: 1.5f's int VIEW is 1, and a carrier that sent the
|
||||
// float bits while calling them class 0 for every attribute would be sending
|
||||
// 0x3FC00000 where the frontend holds 1.
|
||||
EXPECT_NE(value.Data[0], static_cast<Uint32>(Ctx().GetCurrentVertexAttribute(3).intValue[0]));
|
||||
EXPECT_EQ(value.FloatView[0], Word(1.5f));
|
||||
EXPECT_EQ(value.FloatView[1], Word(-2.5f));
|
||||
// P5c rv: the record carries ALL THREE VIEWS VERBATIM (CONTRACT-P5C.md §5.3), so the
|
||||
// frontend's converted int view travels too - 1.5f's int VIEW is 1, and the carrier
|
||||
// that used to send the float bits into all three views would be sending 0x3FC00000
|
||||
// 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) {
|
||||
Ctx().SetCurrentVertexAttributeInt(5, Array<Int32, 4>{7, -9, 11, 13});
|
||||
const MGPAttribValue value = PayloadFor(5);
|
||||
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassInt);
|
||||
EXPECT_EQ(static_cast<Int32>(value.Data[0]), 7);
|
||||
EXPECT_EQ(static_cast<Int32>(value.Data[1]), -9);
|
||||
// and NOT the float view the frontend converted it into
|
||||
EXPECT_NE(value.Data[0], Word(7.0f));
|
||||
EXPECT_EQ(static_cast<Int32>(value.IntView[0]), 7);
|
||||
EXPECT_EQ(static_cast<Int32>(value.IntView[1]), -9);
|
||||
// and the float view is the frontend's CONVERSION of it, not the int bits
|
||||
EXPECT_EQ(value.FloatView[0], Word(7.0f));
|
||||
EXPECT_NE(value.IntView[0], Word(7.0f));
|
||||
}
|
||||
|
||||
TEST_F(TrackerAttribPayload, AUintWriteCarriesTheUintWordsAndNamesItsClass) {
|
||||
Ctx().SetCurrentVertexAttributeUint(6, Array<Uint32, 4>{4000000000u, 2u, 3u, 4u});
|
||||
const MGPAttribValue value = PayloadFor(6);
|
||||
EXPECT_EQ(value.ValueClass, MG_State::GLState::kVertexAttribValueClassUint);
|
||||
EXPECT_EQ(value.Data[0], 4000000000u);
|
||||
EXPECT_NE(value.Data[0], Word(4000000000.0f));
|
||||
EXPECT_EQ(value.UintView[0], 4000000000u);
|
||||
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
|
||||
@@ -925,9 +930,9 @@ namespace {
|
||||
Ctx().SetCurrentVertexAttributeInt(2, Array<Int32, 4>{1, 2, 3, 4});
|
||||
EXPECT_EQ(PayloadFor(1).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
|
||||
EXPECT_EQ(PayloadFor(2).ValueClass, MG_State::GLState::kVertexAttribValueClassInt);
|
||||
// Same numbers, different classes, so the same four words mean different things:
|
||||
// 1.0f is 0x3F800000 and the integer 1 is 0x00000001.
|
||||
EXPECT_NE(PayloadFor(1).Data[0], PayloadFor(2).Data[0]);
|
||||
// Same numbers, different classes, so the WRITTEN views differ: 1.0f is 0x3F800000
|
||||
// and the integer 1 is 0x00000001.
|
||||
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.
|
||||
EXPECT_EQ(PayloadFor(7).ValueClass, MG_State::GLState::kVertexAttribValueClassFloat);
|
||||
// and a later write of the other class moves the class of THAT attribute only
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
#include <Config.h>
|
||||
#include <MG_Remote/Server/PipeApplier.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
|
||||
// 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).
|
||||
@@ -1326,6 +1330,52 @@ TEST_F(PipeWireCodecTest, ObjectDeathReachesTheSinkWithItsHandleAndKind) {
|
||||
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
|
||||
// pin the fields t2's EMITTERS actually fill and the one ordering property the span family has.
|
||||
|
||||
@@ -1064,7 +1064,7 @@ TEST(PipeRouting, AnErrorStatusIsNotFoldedIntoAcceptedOrRefused) {
|
||||
#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 {
|
||||
@@ -1127,6 +1127,11 @@ TEST(PipeRouting, TheInstalledClientArmIsWireAndNotMonolithAndEveryRoutedRowMove
|
||||
C1F_MOVED(Context, SetVertexAttribDefaults);
|
||||
C1F_MOVED(Context, SetPixelPackState);
|
||||
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, SetTextureParams);
|
||||
C1F_MOVED(Context, ResourceSubData);
|
||||
@@ -1140,8 +1145,9 @@ TEST(PipeRouting, TheInstalledClientArmIsWireAndNotMonolithAndEveryRoutedRowMove
|
||||
C1F_ESCAPE(CreateShaderState);
|
||||
#undef C1F_ESCAPE
|
||||
const SizeT movedContext = CountDifferingCells(gMGPipeContext, MGPipeMonolithContext());
|
||||
EXPECT_EQ(movedScreen + movedContext, 33u)
|
||||
<< "exactly the 33 generated routed rows must differ from the monolith adapters; "
|
||||
EXPECT_EQ(movedScreen + movedContext, 34u)
|
||||
<< "exactly the 34 generated routed rows must differ from the monolith adapters "
|
||||
"(33 at P5, + set_context_values at P5c rv); "
|
||||
<< movedScreen + movedContext
|
||||
<< " 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";
|
||||
|
||||
+20
-1
@@ -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+)")
|
||||
|
||||
|
||||
# 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):
|
||||
"""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
|
||||
@@ -479,7 +498,7 @@ def scan_live_accessors(accessors, backend_dir=None, verbose=True):
|
||||
masked = mask_comments_and_strings(read(path))
|
||||
for match in ACCESSOR_READ_RE.finditer(masked):
|
||||
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:
|
||||
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))
|
||||
|
||||
@@ -558,10 +558,13 @@ def self_test():
|
||||
# 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
|
||||
# class, and that is a build failure rather than a silent default.
|
||||
dropped = edit(r"X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED," + GAP + r"\"[^\"]*\","
|
||||
+ GAP + r"\"[^\"]*\"\)", "", "remove GetActiveTextureUnit's row")
|
||||
controls = [("a field in NO class (GetActiveTextureUnit's row removed)",
|
||||
# class, and that is a build failure rather than a silent default. GetBoundVertexArray is
|
||||
# the control's field since P5c rv (it keeps a hand-written BARRIER_PULLED row; the value
|
||||
# rows the first version used are RECORD_SUPPLIED-derived now, so they have no row to
|
||||
# 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",
|
||||
lambda: run(own_text=dropped))]
|
||||
|
||||
@@ -575,9 +578,9 @@ def self_test():
|
||||
lambda: run(own_text=doubled)))
|
||||
|
||||
# 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",
|
||||
"GetActiveTextureUnitt, which is not a PipeInputs field",
|
||||
"GetBoundVertexArrayy, which is not a PipeInputs field",
|
||||
lambda: run(own_text=typo)))
|
||||
|
||||
# 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
|
||||
# substitution, mangled the row past ROW_RE's reach and made this control a silent
|
||||
# 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")
|
||||
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)))
|
||||
|
||||
# 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")
|
||||
controls.append(("a fifth class",
|
||||
"GetActiveTextureUnit is in class SOMEHOW_FINE",
|
||||
"GetBoundVertexArray is in class SOMEHOW_FINE",
|
||||
lambda: run(own_text=bogus)))
|
||||
|
||||
# 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)))
|
||||
if len(sticky) != 7:
|
||||
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 "
|
||||
"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:
|
||||
sys.exit("gen_pipe_field_ownership: self-test: no negative control tripped - the gates are "
|
||||
"not checking anything")
|
||||
@@ -710,7 +713,7 @@ def self_test():
|
||||
% (len(controls) - trips, len(controls)))
|
||||
print("gen_pipe_field_ownership: self-test: %d negative-control trip(s), each asserted against "
|
||||
"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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user