mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 20:58:31 +09:00
152 lines
9.8 KiB
C++
152 lines
9.8 KiB
C++
// MobileGL - MobileGL/MG_Impl/Pipe/PipeFill.h
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#pragma once
|
|
// The fill point (ARCHITECTURE.md 9.2, P1 brief D7). MG_Impl spells MGP_FILL(Verb); as the
|
|
// statement immediately before every call through gBackendFunctionsTable.GL - after every
|
|
// early return the call is behind, inside the loop body for a call made in a loop - so the
|
|
// frontend fills the PipeInputs block for exactly the verbs that reach a backend. In the
|
|
// pull build the macro is ((void)0) and the pull build is byte-identical to a tree without
|
|
// it.
|
|
#if MOBILEGL_PIPE_PUSH
|
|
#include <MG_Pipe/MGPipe.h>
|
|
namespace MobileGL::MG_Pipe {
|
|
struct PipeInputs;
|
|
|
|
// PipeFill.cpp. THE VALIDATE POINT (ARCHITECTURE.md 5.1, P2 brief D1). In order:
|
|
// 1. bump the per-verb serial, record the verb and the context identity;
|
|
// 2. run the tracker's DIRTY WALK for this verb's class (MG_Impl/Pipe/Tracker.h);
|
|
// 3. EMIT, for each set dirty bit whose subsystem bit is on in the runtime
|
|
// MOBILEGL_PIPE_PUSH bitmask, the P2 call that carries it;
|
|
// 4. run the P1 residual fill for every field an emitted call did NOT supply,
|
|
// stamping each with the new serial exactly as before;
|
|
// 5. in a verify build, the entry compare against a second snapshot (P1 brief D8) -
|
|
// which stops being a tautology the moment step 3 supplies a field step 4 skips.
|
|
//
|
|
// It was MGPipeFillForVerb through P1, when steps 2 and 3 did not exist. The macro
|
|
// spelling, the 83 call sites and the verb enum are unchanged: the dispatch is
|
|
// kMGPipeVerbClass's nine classes, which is the same code as nine named ValidateFor*
|
|
// entry points with one call site per verb instead of nine.
|
|
void MGPipeValidateForVerb(MGPipeVerb verb);
|
|
|
|
// Ends the verb in flight without starting another: bumps the serial, so every field the
|
|
// verb stamped goes stale, and puts the current verb back to "none", so a read made after
|
|
// it aborts as Fatal{UnmigratedPipeInput, "<Field>@<none>"} - which is what such a read
|
|
// is - instead of naming whichever verb happened to be filled last. Nothing in the GL
|
|
// entry points calls this: a real verb is always followed by the next verb's fill. It
|
|
// exists for a caller that drives a backend helper directly and wants its declaration to
|
|
// stop where it says it stops (MG_Test/ScopedPipeVerb.h).
|
|
void MGPipeLeaveVerb();
|
|
|
|
// PipeFill.cpp. DOES THIS BUILD, ON THIS BACKEND, AT THIS MASK, EMIT FOR THIS P4a FAMILY?
|
|
// (ID-39, widened by S-3 / ID-41.) The four conjuncts are the operator's per-subsystem bit
|
|
// in MOBILEGL_PIPE_PUSH, the family's own kMGPipeWired*Subsystem constant (`wired`, which
|
|
// the caller passes because it lives in the family's emit header and this header may not
|
|
// include one), and - for the four families P4a migrates - a backend having registered
|
|
// MGPipeResourceOps (the same per-backend signal `MGPipeResourceSubsystemEnabled()` has
|
|
// applied to P3a's buffers since the phase began) and every D-K2 dependency bit of the
|
|
// family being set in the same mask.
|
|
//
|
|
// THE LAST TWO CONJUNCTS ARE THE ONES THIS DECLARATION EXISTS FOR, and they are the same
|
|
// defect twice. Magma (DirectVulkan) registers no table and has no P4a twins; at a mask like
|
|
// 0x7ff Espryt REFUSES the texture family server-side because D-K2's fourth row says bit 10
|
|
// requires bit 11. In both cases the client emitted anyway, the applier accepted, the
|
|
// emitters cleared their per-level dirty flags on that acceptance, and the legacy upload
|
|
// path that still owed those texels found nothing to upload (66 DirectVulkan cases at ID-39,
|
|
// 47 DirectGLES cases at ID-41). With them the four families emit NOTHING in that state and
|
|
// the legacy pull path runs exactly as it does on a pull build.
|
|
//
|
|
// D-K2's TABLE IS IN PipeFill.cpp, ONCE: bit 9 requires bit 10, bit 10 requires bits 7 and
|
|
// 11, bit 11 requires bit 10, bit 12 depends on nothing - the client mirror, bit for bit, of
|
|
// the four `Resolve<Family>SubsystemArm()` refusals in DirectGLES/Managers.cpp.
|
|
//
|
|
// It is exported for the unit gate and for no other caller: the gate itself is
|
|
// FamilyIsLive() inside PipeFill.cpp, every birth hook and every `wants()` row resolves
|
|
// through it, and this returns that same expression rather than a second copy of it.
|
|
Bool MGPipeP4aFamilyEmits(Uint64 subsystem, Uint64 wired);
|
|
|
|
// PipeFill.cpp. P3a D-H2.1: the DRAW's raw vertex-fetch base instance, which
|
|
// set_vertex_buffers now carries as an explicit field.
|
|
//
|
|
// It replaces an ambient process global the backend read at VAO sync time, which is a
|
|
// shape that cannot cross a pushed boundary. The client sends the raw value and never a
|
|
// pre-shifted offset: whether to emulate the fetch shift or let GL_EXT_base_instance do
|
|
// it is the SERVER's decision. It is also an input to set_vertex_buffers' content hash
|
|
// and to the tracker's bit-9 shutter, so a draw whose only change is its base instance
|
|
// still reaches the emitter and still goes out.
|
|
//
|
|
// DO NOT CALL IT DIRECTLY FROM A GL ENTRY POINT - use MGP_SET_BASE_INSTANCE below. This
|
|
// whole declaration block is inside #if MOBILEGL_PIPE_PUSH, so a bare call would not even
|
|
// compile in a pull build, and the three call sites are in a file that is compiled in
|
|
// both. The macro is the same shape MGP_FILL already has, for the same reason.
|
|
//
|
|
// The validate point consumes and clears it - on both of its exits - and MGPipeLeaveVerb
|
|
// clears it too, so a plain draw that follows a base-instanced one sees 0 again. The
|
|
// tracker's Reset() deliberately does NOT clear it (Tracker.h): a make-current happens
|
|
// BETWEEN the setter and the fill that reads it.
|
|
//
|
|
// The three GL entry points that make this call (ID-10's grant) are
|
|
// MG_Impl/GLImpl/Drawing/GL_Drawing.cpp's DrawElementsInstancedBaseVertexBaseInstance,
|
|
// DrawElementsInstancedBaseInstance and DrawArraysInstancedBaseInstance - one line each,
|
|
// immediately above the MGP_FILL, carrying the RAW baseinstance argument.
|
|
void MGPipeSetPendingBaseInstance(Uint32 baseInstance);
|
|
// What the next set_vertex_buffers will carry. The unit gate reads it to pin that a
|
|
// make-current between the setter and the fill does not eat it
|
|
// (TrackerWalk.ABaseInstanceSurvivesTheFirstWalkOnAFreshContext).
|
|
Uint32 MGPipePendingBaseInstance();
|
|
|
|
// PipeFill.cpp. Negative control B (P1 brief D6): the filler withholds the STAMP - never
|
|
// the value - of `field` at `verb`, so that verb's read of it is
|
|
// Fatal{UnmigratedPipeInput, "Field@Verb"} while every other verb is unaffected. The
|
|
// MOBILEGL_PIPE_POISON_OMIT knob ("<Verb>:<FieldName>") calls this once, on the first
|
|
// fill; tests call it directly. Both null clears the omission. An unknown name is
|
|
// Fatal{PipeVerifyBadKnob}.
|
|
void MGPipeSetPoisonOmission(const char* verb, const char* field);
|
|
|
|
// PipeFill.cpp. How many times set_vertex_attrib_defaults' applier failed to reproduce
|
|
// the value the call carried, so the client wrote the mirror itself
|
|
// (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.
|
|
//
|
|
// 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
|
|
// moved.
|
|
Uint64 MGPipeVertexAttribDefaultRepairCount();
|
|
|
|
// PipeFill.cpp. The header of the last set_vertex_attrib_defaults that actually went out
|
|
// - Mask, and Count == 0 for "none ever did", since a call naming no attribute is not
|
|
// emitted. Two properties of this call have no other observable, because reading
|
|
// m_currentVertexAttribute back at a verb whose class does not carry it is the poison
|
|
// violation the fill table exists to forbid: that a FRESH CONTEXT republishes all 32
|
|
// (the server's mirror still holds the previous context's defaults), and that one moved
|
|
// attribute publishes exactly one. Eight bytes, written only when a call goes out.
|
|
MGPVertexAttribDefaults MGPipeVertexAttribDefaultsLastHeader();
|
|
|
|
#if MOBILEGL_PIPE_VERIFY
|
|
// PipeFill.cpp. The second arm of the comparator (P1 brief D8, ARCHITECTURE.md 13.2-2):
|
|
// fills `snapshot` from the live GLContext the old way, for every field in `mask`. This
|
|
// is the branch that survives P13, which is why it is its own function rather than the
|
|
// filler's loop.
|
|
void SnapshotFromGLContext(PipeInputs& snapshot, const MGPipeFieldMask& mask);
|
|
#endif
|
|
} // namespace MobileGL::MG_Pipe
|
|
#define MGP_FILL(Verb) ::MobileGL::MG_Pipe::MGPipeValidateForVerb(::MobileGL::MG_Pipe::MGPipeVerb::Verb)
|
|
// P3a D-H2.1. One line immediately ABOVE the MGP_FILL of a draw entry point that takes a
|
|
// baseinstance, carrying the argument RAW. It has to be a macro for MGP_FILL's reason: the
|
|
// three call sites are compiled in the pull build too, where MGPipeSetPendingBaseInstance is
|
|
// neither declared nor defined.
|
|
#define MGP_SET_BASE_INSTANCE(BaseInstance) \
|
|
::MobileGL::MG_Pipe::MGPipeSetPendingBaseInstance(static_cast<::MobileGL::Uint32>(BaseInstance))
|
|
#else
|
|
#define MGP_FILL(Verb) ((void)0)
|
|
#define MGP_SET_BASE_INSTANCE(BaseInstance) ((void)0)
|
|
#endif
|