50 KiB
Scout report — MG_State/GLState/ProgramState/ProgramArtifacts.h (P0.5)
Worktree root (all paths below are relative to it unless absolute):
C:/Users/geekerwan/AndroidStudioProjects/FoldCraftLauncher/MobileGL-disagg
Branch feat/disaggregated. Read-only scout; nothing was modified.
Every file:line below was opened and read.
0. The authoritative list of the five types
docs/Disaggregated/ARCHITECTURE.md:260 (section 7, "Shader state = SPIR-V + 反射归档"), verbatim
naming of the five:
P0.5 把
TypeFacts、ResourceReflection、XfbVarying、LinkArtifacts、SpirvArtifacts抽到MG_State/GLState/ProgramState/ProgramArtifacts.h(只 include<Includes.h>与容器),更新 7 个 includer,加 CI-H闭包断言。
docs/Disaggregated/ROADMAP.md:16 (P0.5 row) repeats it: "ProgramArtifacts.h(五个反射类型,不
include ShaderObject.h/SpvcSession.h,更新 7 个 includer);Visit() 归档 + sizeof 绊线;CI -H
include 闭包断言". Exit gate: "全套测试逐名不变(纯搬移);两条闭包断言绿且人为加回一个 MG_State
include 能变红;nm/.text 变化可逐符号归因". Dependency line: "P0; P1 与 P7 的硬前置".
So the five are, in dependency order:
TypeFactsResourceReflection(+ its four aliases)XfbVaryingLinkArtifactsSpirvArtifacts
Other supporting facts from the docs:
ARCHITECTURE.md:258—CreateShaderState's payload is per-stage SPIR-V + the archive (LinkArtifacts+SpirvArtifacts, whole structs), not source. glslang lives entirely on the client, SPIRV-Cross (TranspileSpirvToEssl) entirely on the server, split at file granularity.ARCHITECTURE.md:259— archive mechanism isVisit()+ asizeoftripwire (static_assert(sizeof(LinkArtifacts) == MGL_LINKARTIFACTS_SIZE)), one field table serving both serialization directions. Required coverage listed explicitly: the fourResourceReflectionvectors (each carryingTypeFacts),uniformSamplerOrImageUnitIndex,uniformBlockBinding,shaderStorageBlockBinding(by name),explicitOpaqueUniformBindings,xfbVaryings/xfbStrides/xfbPackedStride/xfbNeedsScatteredCapture,computeLocalSize, the GS/TCS/TES facts,usesReservedNumSamples,uniformOffsets.XfbVaryingcarries two spellings (GL name + block instance/member/element).ARCHITECTURE.md:133—MGPProgramDescis 192 bytes: per-stage SPIR-V blob x6 + reflection blobStageMask/GlobalUboSize/ReservedNumSamplesOffset+ four state bytes.
ARCHITECTURE.md:260last sentence — without this step, P7'snm -D | grep glslangcriterion is unreachable.ROADMAP.mdP7 row — gate is literallynm -D libMobileGLServer.so | grep glslangempty.
1. Current definitions — file:line, members, dependencies
All five live inside class ProgramObject in
MobileGL/MG_State/GLState/ProgramState/ProgramObject.h (1803 lines total; class opens at :25,
closes at :1802). They are therefore spelled ProgramObject::TypeFacts etc. everywhere.
1.1 TypeFacts — ProgramObject.h:44–72 (doc comment :41–43)
Flattened glslang::TType. All scalar POD; no heap member.
| line | member | type |
|---|---|---|
| 45 | isArray |
Bool |
| 48 | isSizedArray |
Bool |
| 49 | isMatrix |
Bool |
| 50 | isVector |
Bool |
| 51 | isOpaque |
Bool |
| 52 | isTexture |
Bool |
| 53 | isImage |
Bool |
| 54 | isDouble |
Bool (getBasicType() == EbtDouble) |
| 55 | isVoid |
Bool (hidden block members) |
| 56 | isBuffer |
Bool (qualifier.storage == EvqBuffer) |
| 57 | isPatch |
Bool |
| 58 | hasIndex |
Bool |
| 59 | hasFormat |
Bool |
| 60 | vectorSize |
Int |
| 61 | matrixCols |
Int |
| 62 | matrixRows |
Int |
| 63 | layoutIndex |
Int |
| 64 | layoutFormat |
Uint |
| 68 | layoutMatrix |
Int (widened glslang::TLayoutMatrix) |
| 71 | basicType |
Int (widened glslang::TBasicType) |
Already glslang-free by type — the widening to Int/Uint was done deliberately (comments at
:65–70 say so). Bool/Int/Uint come from MG_Util/Types.h:33,29,30.
1.2 ResourceReflection — ProgramObject.h:76–99 (doc comment :74–75); aliases :101–104
Flattened glslang::TObjectReflection; used for uniforms, blocks, pipe inputs and pipe outputs
alike.
| line | member | type |
|---|---|---|
| 77 | name |
String (= std::string, MG_Util/Types.h:21) |
| 78 | glDefineType |
GLenum |
| 79 | offset |
Int = -1 |
| 82 | size |
Int (raw TObjectReflection::size) |
| 86 | index |
Int = -1 (TPROGRAM block index owning it) |
| 87 | counterIndex |
Int = -1 |
| 88 | arrayStride |
Int |
| 89 | topLevelArraySize |
Int |
| 90 | topLevelArrayStride |
Int |
| 91 | binding |
Int = -1 |
| 92 | location |
Int = -1 |
| 95 | stages |
Uint32 (an EShLanguageMask, widened) |
| 98 | arraySize |
GLint = 1 |
99 (type;) |
type |
TypeFacts |
Aliases, ProgramObject.h:101–104:
using UniformReflection = ResourceReflection; // :101
using BlockReflection = ResourceReflection; // :102
using PipeInputReflection = ResourceReflection; // :103
using PipeOutputReflection = ResourceReflection; // :104
Dependencies: String, GLenum/GLint (GL headers via Includes.h:68–73), TypeFacts. No
glslang type by value.
1.3 XfbVarying — ProgramObject.h:1146–1171 (doc comment :1144–1145)
| line | member | type |
|---|---|---|
| 1147 | name |
String (GL spelling, incl. "Block.member") |
| 1148 | type |
GLenum = GL_FLOAT |
| 1149 | size |
GLint = 1 |
| 1150 | bufferIndex |
Uint32 |
| 1151 | offsetBytes |
Uint32 |
| 1152 | byteSize |
Uint32 |
| 1155 | packedOffsetBytes |
Uint32 |
| 1166 | blockInstanceName |
String |
| 1167 | blockName |
String |
| 1168 | blockMemberIndex |
Int = -1 |
| 1171 | blockMemberElement |
Int = -1 |
No glslang type. This is the "two spellings" struct ARCHITECTURE.md:259 calls out.
1.4 LinkArtifacts — ProgramObject.h:1210–1393 (doc comment block :1173–1209)
53 members. Only two carry a glslang type:
- :1215
SharedPtr<glslang::TProgram> program;— live only betweenLinkProgram()and the end ofDoReflection; null for an L1-memo-served link; must not be dereferenced outsideDoReflection(comment :1211–1214). - :1282
Vector<glslang::TIntermediate::TUniformInitializer> uniformInitialValues;
Full member list (line: declaration):
1215 SharedPtr<glslang::TProgram> program; <-- glslang
1218 Vector<UniformReflection> uniformReflection;
1219 Vector<BlockReflection> blockReflection;
1220 Vector<PipeInputReflection> pipeInputReflection;
1221 Vector<PipeOutputReflection> pipeOutputReflection;
1227 Bool lastStageIsFragment = false;
1228 Array<GLuint, 3> computeLocalSize{};
1231 UnorderedMap<String, Int> uniformIndexByName;
1234 Vector<String> attribs;
1235 Vector<GLenum> attribTypes;
1238 UnorderedMap<String, Uint> linkedFragDataLocation;
1239 UnorderedMap<String, Uint> linkedFragDataIndex;
1245 Vector<Int> glUniformIndexToTProgram;
1246 Vector<Int> tProgramUniformIndexToGl;
1247 Vector<Int> glBlockIndexToTProgram;
1248 Vector<Int> tProgramBlockIndexToGl;
1269 Vector<Int> glUniformBlockIndexToBlock;
1270 Vector<Int> blockIndexToGlUniformBlock;
1275 UnorderedMap<String, Int> linkedExplicitUniformLocations;
1282 Vector<glslang::TIntermediate::TUniformInitializer> uniformInitialValues; <-- glslang
1283 UnorderedMap<String, Uint> uniformLocations;
1291 Vector<Uint64> writtenUniformLocationBits;
1292 Vector<Uint64> writtenUniformIndexBits;
1293 Vector<Uint> writtenUniformIndices;
1296 Vector<Int> uniformIndexInTProgram;
1298 Vector<Int> uniformSamplerOrImageUnitIndex;
1303 UnorderedMap<String, Uint> explicitOpaqueUniformBindings;
1313 UnorderedMap<String, Uint> uniformBlockIndexByName;
1314 Vector<Int> uniformBlockBinding;
1325 UnorderedMap<String, Int> shaderStorageBlockBinding;
1331 std::set<String> storageBlocksWithoutBinding; <-- needs <set>
1336 std::set<String> uniformBlocksWithoutBinding; <-- needs <set>
1338 Uint activeUniformCount = 0;
1346 Bool usesReservedNumSamples = false;
1347 Uint maxUniformLocation = 0;
1348 Int uniformNameMaxLength = 0;
1349 Int attribInNameMaxLength = 0;
1350 Int uniformBlockNameMaxLength = 0;
1352 String infoLog;
1353 Bool linkStatus = false;
1357 Vector<XfbVarying> xfbVaryings;
1363 Vector<String> xfbInterfaceNames;
1364 Vector<Uint32> xfbStrides;
1365 Vector<Uint32> gsStripTriangles;
1366 Bool gsStripCaptureFixup = false;
1367 GLenum gsInputPrimitive = GL_NONE;
1371 Int tcsOutputVertices = 0;
1380 GLenum gsOutputPrimitive = GL_NONE;
1381 Int gsMaxVertices = 0;
1382 Int gsInvocations = 0;
1385 GLenum tessGenMode = GL_NONE;
1386 GLenum tessGenSpacing = GL_NONE;
1387 GLenum tessGenVertexOrder = GL_NONE;
1388 Bool tessGenPointMode = false;
1389 GLenum xfbBufferMode = GL_INTERLEAVED_ATTRIBS;
1390 Int xfbVaryingNameMaxLength = 0;
1391 Bool xfbNeedsScatteredCapture = false;
1392 Uint32 xfbPackedStride = 0;
Membership rule stated at :1177–1180: "exactly the field list ResetLinkArtifacts() clears (plus
the four it forgot to — infoLog, linkedFragDataLocation/Index and the geometry strip-capture
pair)". Access rule (invariant I5) at :1187–1192: m_artifacts is private and reachable only via
ProgramObject::Artifacts(), which calls EnsureLinkJoined() first.
std::set is used at :1331 and :1336 but ProgramObject.h never includes <set> — it arrives
transitively (Includes.h includes <map> at :17 but not <set>; <set> appears explicitly only
in MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.h:15, ShaderCompiler.h:16,
ShaderSourceProcessor.h:10, SpirvPasses/FlattenXfbInterfaceBlocksPass.h:15,
SpirvPasses/UniquifyIoBlockNamesPass.h:16, TranslationCache.h:15). The new header must include
<set> itself or it will fail to compile once the transitive path is cut.
1.5 SpirvArtifacts — ProgramObject.h:1409–1449 (doc comment :1395–1408)
| line | member | type |
|---|---|---|
| 1410 | generatedSpirv |
Vector<Vector<unsigned>> |
| 1411 | enableSpirvValidation |
Bool |
| 1414 | uniformOffsets |
Vector<Uint> |
| 1415 | globalUboScratch |
Vector<Uint8> |
| 1420 | reservedNumSamplesOffset |
Uint = kInvalidUniformOffset |
| 1427 | spirvStatus |
Bool |
| 1436 | nativeFloat64 |
Bool |
| 1448 | pointSizeDemoted |
Bool |
No glslang, no spirv-cross, no SPIRV-Reflect type. One external dependency:
kInvalidUniformOffset, declared at ProgramObject.h:547 as
static constexpr Uint kInvalidUniformOffset = ~0u; — i.e. a ProgramObject static member declared
before SpirvArtifacts but outside it. Moving SpirvArtifacts out requires moving (or
mirroring) this constant. Its readers, all of which spell it ProgramObject::kInvalidUniformOffset:
MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:4659 and :8553;
MG_Impl/GLImpl/Program/GL_Program.cpp:1249,1304,1522; MG_State/GLState/Core.cpp:513,514;
MG_State/GLState/ProgramState/ProgramObject.cpp:198,273; plus in-header uses at
ProgramObject.h:565, :811, :1420.
1.6 TUniformInitializer — the one type that must be re-declared
include/glslang/MachineIndependent/localintermediate.h:627–636 (vendored/forked glslang; this
struct is a MobileGL fork addition, see commit e5846569c):
struct TUniformInitializer {
std::string name;
TBasicType basicType = EbtVoid;
int vectorSize = 1;
int matrixCols = 0;
int matrixRows = 0;
int arraySize = 1;
std::vector<long long> intValues;
std::vector<double> floatValues;
};
It is a plain aggregate — no pool allocator, no TString. ProgramTranslationCache.h:44–47
independently states this: "the only member that ever pointed into glslang-owned memory was
program itself, and TUniformInitializer / XfbVarying, which look like glslang types, are
std::string + std::vector aggregates."
Producers/consumers of uniformInitialValues:
MG_State/GLState/ProgramState/ProgramLinkTask.cpp:612–618(dedupe + push_back)MG_State/GLState/ProgramState/ProgramObject.cpp:148(ApplyUniformInitialValues, reads it)MG_State/GLState/ProgramState/ProgramObject.cpp:356(ResetLinkArtifactsclears it)
ProgramObject.cpp:180–183 compares init.basicType against glslang::EbtFloat,
glslang::EbtFloat16, glslang::EbtDouble, glslang::EbtInt, glslang::EbtUint,
glslang::EbtBool. Re-typing basicType to Int keeps this compiling only because those
enumerators implicitly convert; if the new type is to be glslang-free, that .cpp (which is
client-side and may keep including glslang) stays as-is, but the header must not name
glslang::TBasicType.
2. Transitive include closure of ProgramObject.h today
Direct includes, ProgramObject.h:10–14:
:10 #include <Includes.h>
:11 #include "ShaderObject.h"
:13 #include <MG_Util/Metrics/BufferMetrics.h>
:14 #include <MG_Util/ShaderTranspiler/SpvcSession.h>
Level 2:
MG_State/GLState/ProgramState/ShaderObject.h:10–13→<Includes.h>,ShaderStage.h(no includes at all),ShaderCompileTask.h,ShaderCompileAdoptionMap.h.MG_Util/ShaderTranspiler/SpvcSession.h:10–12→<Includes.h>,<spirv_reflect.h>,MG_Util/ShaderTranspiler/Types.h.MG_Util/Metrics/BufferMetrics.h:10→<Includes.h>only.
Level 3:
ShaderCompileTask.h:10–15→<Includes.h>,MG_Util/Async/JobNode.h,MG_Util/ShaderTranspiler/CompileEnv.h,MG_Util/ShaderTranspiler/Types.h,MG_State/GLState/BufferState/BufferState.h,ProgramState/ShaderPreprocessCache.h.ShaderCompileAdoptionMap.h:10–11→<Includes.h>,ProgramState/ShaderSourceKey.h.MG_Util/ShaderTranspiler/Types.h:10–11→<Includes.h>,CompileEnv.h.
Includes.h is the elephant. MobileGL/Includes.h:79–83, unconditional:
#include <glslang/Include/Types.h>
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <glslang/Include/intermediate.h>
#include <glslang/MachineIndependent/localintermediate.h>
plus Includes.h:56 <spirv_cross/spirv_cross_c.h>, :53 <ska/flat_hash_map.hpp>, :55
<xxhash.h>, :63–74 EGL/GL/GLES headers, :130 <vulkan/vulkan.h>, :148 MG_Util/Debug/Log.h,
:149 MG_Util/Types.h (which at :11 includes <Includes.h> back and at :12 GLExtensions.h).
Consequence to flag loudly for the implementers: the ARCHITECTURE wording "只 include
<Includes.h> 与容器" does not by itself remove glslang from the preprocessed text of
ProgramArtifacts.h, because Includes.h drags glslang in unconditionally. What it removes is the
by-value/by-symbol dependency (SharedPtr<glslang::TProgram> needs ~TProgram;
Vector<TUniformInitializer> needs the type complete) — which is exactly what the P7 gate
(nm -D | grep glslang, a link/symbol gate) measures. Two options for the header, both worth
recording in the design note:
- (a) include
<Includes.h>as the docs say and rely on the type-level purge; the-Hclosure assertion then asserts the absence ofShaderObject.h/SpvcSession.h/spirv_reflect.h, not the absence of glslang. - (b) include only
MG_Util/Types.h+<set>+ GL headers, which would be genuinely glslang-free — butMG_Util/Types.h:11itself includes<Includes.h>, so this is not achievable without splittingTypes.htoo. (a) is the only reachable shape today; say so in the header comment.
Where Includes.h gets its type aliases (MG_Util/Types.h): String :21, Int8..Uint64 :23–32,
Bool :33, Float/Double :34–35, StringView :36, Vector :37–38, Pair :39–40, SharedPtr
:41–42, UniquePtr :43–44, WeakPtr :45–46, SizeT :55, Array :56–57,
UnorderedMap = ska::flat_hash_map :87–88, Optional :117, Data = Vector<Uint8> :123.
3. Every includer — verified counts
3.1 ProgramObject.h — 8 non-self includers (9 with its own .cpp)
| # | file:line | what it uses |
|---|---|---|
| 1 | MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h:13 |
ProgramObject by reference in ComputeHash (:474), GetOrCreateProgram (:475), VkProgramObject::program pointer (:583), stages doc note (:590). Names no artifact type. |
| 2 | MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp:13 |
const ProgramObject& parameter on ~15 resolvers (:232, :250, :465, :751, :808, :835, :862, :986, :1146, :1260, :1643, :1712, :1759, :1802, :1884). Accessors only; names no artifact type. |
| 3 | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:17 |
ProgramObject::kInvalidUniformOffset (:4659, :8553); constructs internal blit/depth-mipmap ProgramObjects (:4232–4253, :4312–4316). |
| 4 | MobileGL/MG_Impl/GLImpl/Program/ProgramInterface.cpp:11 |
the heaviest consumer. ProgramObject::TypeFacts (:84, :187, :198, :435), ProgramObject::ResourceReflection (:93), ProgramObject::BlockReflection (:176, :251), ProgramObject::LinkArtifacts (:228, :265, :328, :437, :528). Fields read off LinkArtifacts: uniformReflection, blockReflection, pipeInputReflection, pipeOutputReflection, lastStageIsFragment (only those five). |
| 5 | MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.h:11 |
ProgramObject::LinkArtifacts artifacts; (:80) and SpirvHandoff::reflection (:118); SharedPtr<const ProgramObject::SpirvArtifacts> cachedSpirv (:147); SharedPtr<const ProgramObject::LinkArtifacts> linkArtifactsForCache (:151). Also holds Vector<SharedPtr<glslang::TShader>> shaders (:108) — genuinely glslang, stays client-side. |
| 6 | MobileGL/MG_State/GLState/ProgramState/ProgramPipelineObject.h:11 |
pipeline composite; holds SharedPtr<ProgramObject> per stage. |
| 7 | MobileGL/MG_State/GLState/ProgramState/ProgramState.h:12 |
the object table. This is the fan-out node: MG_State/GLState/Core.h:16 includes it, so every Core.h includer (all of MG_Impl, both backends' Managers.h/DirectVulkan.cpp) reaches ProgramObject.h transitively. |
| 8 | MobileGL/MG_State/GLState/ProgramState/ProgramTranslationCache.h:11 |
ProgramTranslationResult { ProgramObject::LinkArtifacts link; ProgramObject::SpirvArtifacts spirv; } (:50–51). |
| (self) | MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp:9 |
— |
The roadmap's "7 个 includer" does not reproduce as the ProgramObject.h includer count (8 non-self,
9 with the .cpp). It does reproduce exactly as the ShaderObject.h includer count (7, below), so
the number is either a miscount or was written against ShaderObject.h. Implementers should plan
for 8 includers of ProgramObject.h plus the files that only name the types (§3.4).
3.2 ShaderObject.h — exactly 7 includers
| # | file:line | uses |
|---|---|---|
| 1 | MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h:14 |
ShaderObject for stage/source |
| 2 | MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:18 |
builds internal shaders (:4232 ff.) |
| 3 | MobileGL/MG_State/GLState/ProgramState/ProgramObject.h:11 |
SharedPtr<ShaderObject> attach lists, LinkedShaderRef (:150–154) |
| 4 | MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp:9 |
self |
| 5 | MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.h:11 |
ShaderStage conversion |
| 6 | MobileGL/MG_Util/Converters/MGToGL/ProgramEnumConverter.h:11 |
ditto |
| 7 | MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.h:13 |
source preprocessing |
3.3 SpvcSession.h — 18 includers
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp:13;
MobileGL/MG_Benchmark/ShaderCache/TranslationCacheBench.cpp:60;
MobileGL/MG_Benchmark/Transpile/TranspileProfile.cpp:43;
MobileGL/MG_State/GLState/ProgramState/ProgramObject.h:14;
MobileGL/MG_State/GLState/ProgramState/ProgramSpirvTask.cpp:14;
MobileGL/MG_Test/ShaderTranspiler/DemoteFloat64Test.cpp:18;
.../DemotePointSizeTest.cpp:17; .../FlattenXfbInterfaceBlocksTest.cpp:18;
.../LegalizeResourceArrayIndexTest.cpp:17; .../LowerViewportIndexTest.cpp:29;
.../StripIoBlockLocationsTest.cpp:17; .../TranslationCacheTest.cpp:39;
.../UniquifyIoBlockNamesTest.cpp:19; .../WidenImageFormatsTest.cpp:34;
MobileGL/MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.cpp:10;
MobileGL/MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.h:11;
MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h:11;
MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp:9.
ProgramObject.h:14 is the only one of the 18 that is a ProgramObject.h include — dropping it
costs nothing to the other 17. Grep confirms ProgramObject.h names no spvc_* / SpvReflect* /
SpvcMetadata symbol in its own body; the include is dead weight there (verify with a build before
deleting — ProgramObject.cpp may rely on it transitively).
There are no includers of any of the three headers outside MobileGL/ (checked the whole
worktree; Testing/, tools/, 3rdparty/ have none).
3.4 Files that name one of the five types (these need ProgramArtifacts.h, or keep getting it
via ProgramObject.h)
MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp:27–28(using TypeFactsRef = const ...::ProgramObject::TypeFacts&), then :243, :250, :1197, :1213, :1246, :1301, :1545.MobileGL/MG_Impl/GLImpl/Program/ProgramInterface.cpp— see §3.1 row 4.MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp:48–49(MakeTypeFacts), :79–81 (MakeResourceReflection), :94, :133, :866, :872, :876–877, :987, :991, :1114, :1122, :1302, :1331, :1383, :1439, :1539, :1730, :1758, :1768, :1789, :1796, :1857, :1944, :1960, :2038.MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.h:80, :114, :118, :145, :147, :148, :151, :203.MobileGL/MG_State/GLState/ProgramState/ProgramSpirvTask.h:46;ProgramSpirvTask.cpp:127, :196, :234, :349.MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp:198, :207, :273, :332–343, :356, :499–501, :686.MobileGL/MG_State/GLState/ProgramState/ProgramTranslationCache.h:21, :34, :38–39, :46, :50–51;ProgramTranslationCache.cpp:39–40(sizeof(ProgramObject::ResourceReflection)).MobileGL/MG_Util/ShaderTranspiler/TranslationCache.h:54, :429, :442— comments only; MG_Util deliberately must not depend on MG_State (ProgramTranslationCache.h:38–39).MobileGL/MG_Pipe/MGPipeTypes.h:296— comment onMGPProgramDescnaming the archive.MobileGL/MG_Test/Program/AsyncSpirvPhaseTest.cpp:336— comment.MobileGL/MG_Backend/DirectGLES/Managers.cpp:6424, :6440— usesTypeFactsthroughconst auto&, never spelling the name.
4. How the two backends read the artifacts today
Neither backend names LinkArtifacts, SpirvArtifacts, ResourceReflection or XfbVarying
anywhere. Both go exclusively through ProgramObject accessors, which are what the PipeInputs
work of P1 replaces. This is good news for P0.5: moving the type declarations does not touch backend
code at all.
4.1 DirectGLES (Espryt) — reads the front-end tables
Reaches ProgramObject.h transitively: DirectGLES/Managers.h:17 → MG_State/GLState/Core.h:16 →
ProgramState/ProgramState.h:12 → ProgramObject.h. (DirectGLES.h:10–15 and Managers.h:10–18
contain no direct include.)
Call sites (file:line → accessor → underlying artifact field):
XFB (all LinkArtifacts)
DirectGLES.cpp:890GetTransformFeedbackPackedStride()→xfbPackedStrideDirectGLES.cpp:925, :1034GetTransformFeedbackStride(i)→xfbStrides[i]DirectGLES.cpp:931,Managers.cpp:7448,Managers.cpp:8092GetTransformFeedbackVaryings()→xfbVaryingsDirectGLES.cpp:1005GetTransformFeedbackBufferCount()DirectGLES.cpp:1027NeedsScatteredTransformFeedbackCapture()→xfbNeedsScatteredCaptureDirectGLES.cpp:1039, :1063packed stride again (scatter buffer bind)DirectGLES.cpp:1097,Managers.cpp:8140, :8155, :8219GetTransformFeedbackBufferMode()→xfbBufferModeManagers.cpp:8090GetTransformFeedbackVaryingCount()
Attributes / uniforms
DirectGLES.cpp:1247GetActiveAttributeLocationMask()DirectGLES.cpp:1276, :1290GetAttribType(location)→attribTypesDirectGLES.cpp:3036,Managers.cpp:6419, :6572GetMaxUniformLocation()→maxUniformLocationDirectGLES.cpp:3038, :3564,Managers.cpp:468, :6450, :6589GetUniformSamplerOrImageUnitIndex(loc)→uniformSamplerOrImageUnitIndexDirectGLES.cpp:3039,Managers.cpp:6423, :6576GetUniformType(loc)Managers.cpp:463, :465, :6579GetUniformLocation(name)→uniformLocationsManagers.cpp:6421, :6574GetUniformName(loc)→uniformReflection[...].nameManagers.cpp:6424GetUniformTypeFacts(loc)→uniformReflection[...].type— the only directTypeFactsread in either backend (comment at :6440: "From the OWNED TypeFacts, not from a live TType"). It is bound withconst auto&, so the type name never appears.
Blocks / SPIR-V / status
DirectGLES.cpp:2734, :3033, :3336, :3374, :3663, :4071,Managers.cpp:7321GetLinkStatus()/GetSpirvStatus()→linkStatus/spirvStatusDirectGLES.cpp:3397, :3402, :3444, :3449,Managers.cpp:8290, :8293GetUBOSize()→globalUboScratch.size()DirectGLES.cpp:3481GetUniformBlockBinding(i)→uniformBlockBindingManagers.cpp:6192, :6202, :7341GetShaderStorageBlockBindingOverrides()→shaderStorageBlockBindingManagers.cpp:7408GetLinkedShaderStages();:7409GetGeneratedSpirv()→generatedSpirv;:7430GetLinkedShaderSnapshot();:7439GetSpirvValidationEnabled()→enableSpirvValidation- version/identity:
GetLinkVersion(DirectGLES.cpp:2798, :3715, :7368),GetImageUnitVersion(:2799),GetBackendStateVersion(:3335, :3350, :3540),GetLifetimeId(:3333, :3349),GetExternalIndex(many,Managers.cpp:7212ff.). These live onProgramObject, not in the artifacts — they must stay behind.
ARCHITECTURE.md:310 additionally notes Espryt's ScatterCapturedRecords is a read-modify-write on
the client shadow and that under split the client keeps the varying/stride tables from the
reflection archive.
4.2 DirectVulkan (Magma) — re-derives with SPIRV-Reflect
MG_Backend/DirectVulkan/DirectVulkan.cpp:22 #include <spirv_reflect.h>.
The re-derivation lives in GetProgramResourceCache(const ProgramObject&) at
DirectVulkan.cpp:161–2xx (the docs' "around line 161" is exact — the function opens at :161,
the cache lookup at :162). Shape:
- Caches keyed by GL program name:
UnorderedMap<GLuint, ProgramResourceCache> g_programResourceCaches;atDirectVulkan.cpp:103; cleared at:305(ClearProgramResourceCaches); insertion hazard note at:728. struct BufferVariableResource:51–57,struct StorageBlockResource:58–63,struct ProgramResourceCache:65–81(programLifetimeId,backendStateVersion,blockBindingVersion,Vector<StorageBlockResource> storageBlocks,Vector<BufferVariableResource> bufferVariables).- Validity key
:171–174:GetLifetimeId()+GetBackendStateVersion()+GetBlockBindingVersion(). On a block-binding-only bump (:176–190) it re-appliesprogram.GetShaderStorageBlockBindingOverride(block.name)by name instead of re-reflecting — i.e.LinkArtifacts::shaderStorageBlockBindingis the authority and spirv-reflect is the structure. - The reflect run itself
:196–...:program.GetGeneratedSpirv()→ per modulespvReflectCreateShaderModule(spirv.size() * sizeof(Uint), spirv.data(), &module)(:205),spvReflectEnumerateDescriptorBindings(:216, :222). NormalizeDescriptorName:118–131strips a"[0]"suffix offtype_description->type_name.AddBufferVariablesRecursive:133–159walksSpvReflectBlockVariable::membersrecursively to build theGL_BUFFER_VARIABLElist with names/offsets/sizes.
This is exactly the "three consumers of one archive" open question at ROADMAP.md:81: Espryt reads
the front-end tables, Magma runs SPIRV-Reflect, and DirectVulkan.cpp reflects a second time for
glGetProgramResource*. P0.5 changes none of it — but note that the archive as shipped
(LinkArtifacts alone) does not contain buffer-variable / storage-block reflection, which is why
Magma re-derives; the server can keep doing exactly that from the shipped SPIR-V.
Magma's other ProgramObject reads (UniformManager.cpp, ProgramFactory.h/.cpp,
VulkanRenderer.cpp) are all accessor calls; see §3.1 rows 1–3.
5. Existing Visit() / archive / serialization patterns to follow
There is no Visit(), Archive(), Serialize() or ForEachField() in the tree today
(grep -rn "Visit(\|ForEachField\|Archive(\|Serialize(\|Deserialize(" over MobileGL/ returns
nothing). The Visit() archive is genuinely new. The two prior-art patterns to imitate:
5.1 The X-macro field table — MG_Pipe/PipeFields.def
MobileGL/MG_Pipe/PipeFields.def:9–18 states the contract: one macro per payload in
MGPipeTypes.h, listing only the fields that carry meaning (padding deliberately absent, because
MOBILEGL_PIPE_VERIFY must have zero false positives). Hand-maintained alongside the struct; a field
added without a .def entry makes the comparator blind to it.
Shape (PipeFields.def:83–85):
#define MGP_FIELDS_MGPProgramDesc(F) \
F(Cso) F(StageMask) F(GlobalUboSize) F(ReservedNumSamplesOffset) F(SpirvStatus) F(NativeFloat64) \
F(PointSizeDemoted) F(EnableSpirvValidation) F(Spirv) F(Reflection)
and a roll-up list at PipeFields.def:227 enumerating every payload. Consumed by
scripts/gen_pipe.py (docstring at scripts/gen_pipe.py:9–33), which emits
MobileGL/MG_Pipe/generated/{PipeWire,PipeVerify,PipeTables,PipeThunks,PipeFilled,PipeSpanTable,PipeCoverage}.inc.
Outputs are committed; CI regenerates and fails on a diff. gen_pipe.py refuses a catalogue payload
with no field list.
Recommendation: the Visit() archive is the C++-template analogue of this — but because
LinkArtifacts members are String/Vector/UnorderedMap/std::set rather than scalars, a
generated .inc is less natural than a member-template. The prior art to cite is the
discipline (one field table, both directions, a gate that goes red on a missed field), not the
macro mechanics.
5.2 The sizeof tripwire — MG_Pipe/MGPipeTypes.h
Two idioms, both worth copying:
(a) The POD assertion macro, MGPipeTypes.h:43–45:
#define MGP_ASSERT_POD(T, Size) \
static_assert(std::is_trivially_copyable_v<T>, #T " must be trivially copyable"); \
static_assert(sizeof(T) == (Size), #T " changed size; update the wire format and this assertion")
used e.g. MGP_ASSERT_POD(MGPProgramDesc, 192); at MGPipeTypes.h:308. Rationale at
MGPipeTypes.h:20–22: "Sizes are asserted rather than merely documented because the wire records
generated from these structs are memcpy'd; a field silently changing width is a protocol break that
no test would otherwise see."
(b) The named-constant ratchet, MGPipeTypes.h:527–539 — this is the exact shape
MGL_LINKARTIFACTS_SIZE should take:
#define MGL_RESIDUAL_BLOCK_SIZE 1248
static_assert(sizeof(ResidualValueBlock) == MGL_RESIDUAL_BLOCK_SIZE,
"the residual value block changed size; lower MGL_RESIDUAL_BLOCK_SIZE if a field "
"retired, and do not raise it");
with a comment block (:527–535) explaining the direction of travel and what makes it a build break.
MGPipeTypes.h:504–507 adds the caveat that a heterogeneous POD also needs member-by-member
offsetof assertions, since padding differs across ABIs and the monolith verify harness is blind to
it (both sides are one TU).
Caveat for LinkArtifacts: sizeof(LinkArtifacts) is not ABI-stable and not
trivially-copyable — it contains std::string, std::vector, ska::flat_hash_map and std::set,
whose sizes differ between libstdc++/libc++/MSVC and between debug/release STL configurations.
MGL_LINKARTIFACTS_SIZE therefore cannot be a single literal the way MGL_RESIDUAL_BLOCK_SIZE is;
it is a same-build tripwire ("did somebody add a field without touching Visit()"), not a wire
size. Options: (i) assert per-toolchain values behind #if, (ii) assert only under a canonical CI
toolchain (#if defined(__GLIBCXX__) && !defined(_GLIBCXX_DEBUG)), or (iii) drop the byte count and
assert a field count the Visit() template computes at compile time. State whichever you pick in
the header — the ARCHITECTURE wording assumes (i)/(ii).
5.3 The -H include-closure gate
ARCHITECTURE.md:425 describes purity gate A: MG_Remote/Transport/ headers must not include the
front-end umbrella, "纯度门 A 断言 -H 输出". No such script exists yet — scripts/ contains
only check_doc_citations.py, format_code.sh, gen_pipe.py, gen_pipe_dirty_surface.py,
gen_protocol.py, update-source-file-header.py. The P0.5 implementer writes the first one, and
ROADMAP.md:16 requires it be demonstrably red when a MG_State include is added back.
Related counter-example already in the tree: MGPipeTypes.h:24–31 documents its own P0.5 debt in the
file header ("P0.5 DEBT, recorded here so it is impossible to miss") for #include <MG_Backend/BackendObject.h> (:32) and #include <MG_State/GLState/RenderState/RenderState.h>
(:33). Follow that convention: any include ProgramArtifacts.h cannot yet drop gets an explicit
debt paragraph in the file header.
6. Proposed contents of ProgramArtifacts.h
File: MobileGL/MG_State/GLState/ProgramState/ProgramArtifacts.h
Namespace: MobileGL::MG_State::GLState (top-level, not nested in ProgramObject).
6.1 Includes
#pragma once
#include <Includes.h> // String/Vector/UnorderedMap/Array + GL enums; see debt note below
#include <set> // std::set<String>, used by LinkArtifacts - NOT in Includes.h
Nothing else. Explicitly not: ShaderObject.h, SpvcSession.h, ProgramLinkTask.h,
BufferMetrics.h, <spirv_reflect.h>.
File-header debt paragraph (mandatory, mirroring MGPipeTypes.h:24–31): <Includes.h> still pulls
glslang (Includes.h:79–83), spirv-cross C (:56) and Vulkan (:130). This header is glslang-free
by type and by symbol, which is what the P7 nm -D | grep glslang gate measures; a textually
glslang-free closure additionally requires splitting MG_Util/Types.h (which itself includes
<Includes.h> at :11) and is out of P0.5 scope.
6.2 Types, in dependency order
-
kInvalidUniformOffset— movestatic constexpr Uint kInvalidUniformOffset = ~0u;(ProgramObject.h:547) out to a namespace-scopeinline constexpr Uint kInvalidUniformOffset = ~0u;in this header, and leavestatic constexpr Uint kInvalidUniformOffset = kInvalidUniformOffset;… no — leave ausing-style re-export inProgramObject:static constexpr Uint kInvalidUniformOffset = MobileGL::MG_State::GLState::kInvalidUniformOffset;so the 9 existing spellingsProgramObject::kInvalidUniformOffset(§1.5) keep compiling unchanged. This is required:SpirvArtifacts::reservedNumSamplesOffset(:1420) uses it as a default member initializer. -
struct TypeFacts— verbatim move of ProgramObject.h:44–72, comments included. Zero changes; already POD, already glslang-free. -
struct ResourceReflection— verbatim move of :76–99, plus the four aliases :101–104 moved to namespace scope. -
struct XfbVarying— verbatim move of :1146–1171. Zero changes. -
struct UniformInitializer— NEW, the de-glslang'd replacement forglslang::TIntermediate::TUniformInitializer(include/glslang/MachineIndependent/localintermediate.h:627–636):struct UniformInitializer { String name; Int basicType = 0; // widened glslang::TBasicType, same convention as TypeFacts::basicType Int vectorSize = 1; Int matrixCols = 0; Int matrixRows = 0; Int arraySize = 1; Vector<Int64> intValues; // was std::vector<long long> Vector<Double> floatValues; // was std::vector<double> };Int64=MG_Util/Types.h:31,Double=:35. The widening ofbasicTypetoIntmatches the precedent already set byTypeFacts::basicType(ProgramObject.h:71) andTypeFacts::layoutMatrix(:68). -
struct LinkArtifacts— moved from :1210–1393, with exactly two member re-types::1215 SharedPtr<glslang::TProgram> program;→ stays inProgramObject.h, see §6.3.:1282 Vector<glslang::TIntermediate::TUniformInitializer> uniformInitialValues;→Vector<UniformInitializer> uniformInitialValues;All other 51 members move verbatim.
-
struct SpirvArtifacts— verbatim move of :1409–1449, oncekInvalidUniformOffsetis in scope (item 1). -
The archive —
Visit()as a member template on each of the five, plus the tripwires:template <class V> void TypeFacts::Visit(V&& v) { v(isArray); v(isSizedArray); /* 20 fields */ }with a matching
static_assertper struct. TheVisit()must beconst-overloaded (or takeauto&& self, C++23 deducing-this — the project is C++23, seeIncludes.h:47__cplusplus >= 202302L) so one field list serves both write (const) and read (mutable), which is the "一份字段表服务序列化两个方向" requirement ofARCHITECTURE.md:259.
6.3 What must stay in ProgramObject.h
ProgramObject::MAX_UNIFORM_LOCATIONS(:39) —static_cast<Int>(glslang::TQualifier::layoutLocationEnd). Named byMG_Impl/GLImpl/Getter/GL_Getter.cpp:1945,ProgramLinkTask.cpp:1251. Stays glslang-bound; it is not one of the five types. (If P7 later needs it server-side, replace the RHS with the literal andstatic_assertagainst glslang on the client — flag but do not do this in P0.5.)LinkArtifacts::program— theSharedPtr<glslang::TProgram>. Two options; pick (A):- (A) Keep it inside
LinkArtifactsbut change nothing about the header's purity by making the field's type a forward-declared incompleteglslang::TProgram.SharedPtr<T>of an incompleteTis legal to declare, copy and move; only construction and destruction need completeness, and both happen inProgramLinkTask.cpp(the only writer: :646, :678, :764, :846, :877) andProgramObject.cpp:499–501(whole-struct reset). ButLinkArtifacts's own implicit destructor must destroy theshared_ptr, and that is instantiated wherever aLinkArtifactsis destroyed — including inProgramInterface.cppandProgramTranslationCache.cpp. Ashared_ptr's destructor does not requireTcomplete (the deleter was type-erased at construction), so this works:namespace glslang { class TProgram; }at the top ofProgramArtifacts.his sufficient. Verify by compiling with a hand-editedIncludes.hthat hides glslang, because this is the single highest-risk claim in this plan. - (B) Move
programout ofLinkArtifactsontoProgramLinkTaskand pass it beside the artifacts. Cleaner for the server, but touches ~30 call sites inProgramLinkTask.cpp(§1.4 readers) andProgramSpirvTask.cpp:127, :234— a bigger diff than "纯搬移" allows. Record as the P7 follow-up if (A)'s incomplete-type trick fails on any toolchain.
- (A) Keep it inside
- Everything else: the ~120 accessors,
Artifacts()/Spirv()join gates (:1682–1698),EnsureLinkJoined/EnsureSpirvJoined(:1633, :1648),ResetLinkArtifactsdeclaration (:1457, definedProgramObject.cpp:332),LinkedShaderRef(:150–154),PendingUniformWrite(:1658–1663),BackendHashMemoSlot(:1767–1771), and allm_*members (:1712–1801).
6.4 Forwarding shape
ProgramObject.h keeps compiling every existing spelling (ProgramObject::LinkArtifacts,
ProgramObject::TypeFacts, …) via in-class using re-exports, so no other file changes at all
in P0.5 (this is what makes the exit gate "全套测试逐名不变(纯搬移)" achievable):
// ProgramObject.h, replacing lines 41-104, 1144-1449
#include <MG_State/GLState/ProgramState/ProgramArtifacts.h>
...
class ProgramObject {
public:
using TypeFacts = MobileGL::MG_State::GLState::TypeFacts;
using ResourceReflection = MobileGL::MG_State::GLState::ResourceReflection;
using UniformReflection = ResourceReflection;
using BlockReflection = ResourceReflection;
using PipeInputReflection = ResourceReflection;
using PipeOutputReflection = ResourceReflection;
using XfbVarying = MobileGL::MG_State::GLState::XfbVarying;
using UniformInitializer = MobileGL::MG_State::GLState::UniformInitializer;
using LinkArtifacts = MobileGL::MG_State::GLState::LinkArtifacts;
using SpirvArtifacts = MobileGL::MG_State::GLState::SpirvArtifacts;
static constexpr Uint kInvalidUniformOffset = MobileGL::MG_State::GLState::kInvalidUniformOffset;
...
ProgramObject.h also drops #include <MG_Util/ShaderTranspiler/SpvcSession.h> (:14) — it names
no spvc/SpvReflect symbol (verified by grep). Keep #include "ShaderObject.h" (:11) and
#include <MG_Util/Metrics/BufferMetrics.h> (:13); both are genuinely used by ProgramObject
itself. Removing :14 is the one purity win available to ProgramObject.h in this phase; the glslang
dependency stays because MAX_UNIFORM_LOCATIONS and SharedPtr<glslang::TProgram> are there.
Note the alias using TypeFacts = ...::TypeFacts; inside a class whose enclosing namespace already
declares TypeFacts is legal (member alias shadows), but the qualified RHS is mandatory or it
self-references. Spell it fully qualified as above.
6.5 Where the Visit() archive's two directions live
Serializer/deserializer bodies do not belong in ProgramArtifacts.h (that would drag whichever
byte-stream helper into every Core.h consumer). Put them where MGPProgramDesc::Reflection's blob
is built and parsed — the natural home is a new MG_Pipe/ or MG_Remote/ TU that includes
ProgramArtifacts.h and nothing else from MG_State. ProgramArtifacts.h carries only the field
tables (Visit) and the tripwires.
7. Risk list
R1 — Includes.h is not glslang-free (HIGH, design-level). Includes.h:79–83 pulls all five
glslang headers unconditionally. Following the ARCHITECTURE literally ("只 include <Includes.h> 与
容器") yields a header that is glslang-free by type but not by preprocessed text. Decide and
document which gate the -H assertion enforces before writing it, or the gate will be written to a
criterion the header cannot meet. See §2.
R2 — sizeof(LinkArtifacts) is not a stable number. String/Vector/ska::flat_hash_map/
std::set sizes vary by STL and by _GLIBCXX_DEBUG. MGL_LINKARTIFACTS_SIZE cannot be a bare
literal the way MGL_RESIDUAL_BLOCK_SIZE (MGPipeTypes.h:536) is. Also note the project builds on
Windows/MSVC (this worktree), Android/NDK-libc++ and Linux/libstdc++ — three different answers. See
§5.2 for the three mitigation options.
R3 — SharedPtr<glslang::TProgram> by value in LinkArtifacts (ProgramObject.h:1215). This is
the single member that references glslang by value in the archived types. The incomplete-type plan
(§6.3 option A) is sound in principle but must be compile-verified on all three toolchains; MSVC in
particular has historically been strict about shared_ptr<Incomplete> in aggregates with implicit
special members. Fallback is §6.3 option B. Related: the server will deserialize a LinkArtifacts
whose program is always null — assert that at the deserializer, matching the existing insert-time
assertion documented at ProgramTranslationCache.h:42–43 ("link.program is null by construction …
Asserted at insert").
R4 — glslang::TIntermediate::TUniformInitializer re-typing breaks a comparison.
MG_State/GLState/ProgramState/ProgramObject.cpp:180–183 compares init.basicType against
glslang::EbtFloat / EbtFloat16 / EbtDouble / EbtInt / EbtUint / EbtBool. With
basicType widened to Int this still compiles (enum→int promotion) but the file keeps a glslang
dependency; that is fine (it is a client TU) as long as the header does not. Also
ProgramLinkTask.cpp:612–618 builds the vector via std::find_if on .name and copies the whole
struct — a plain type substitution, no field-order sensitivity.
R5 — <set> (MEDIUM, will bite immediately). LinkArtifacts uses std::set<String> at :1331
and :1336 and no header on the path includes <set> deliberately. Add it explicitly. See §1.4.
R6 — kInvalidUniformOffset ordering (MEDIUM). Declared at ProgramObject.h:547, used as a
default member initializer at :1420. Moving SpirvArtifacts without moving the constant is an
immediate compile error, and moving the constant without a re-export breaks 9 call sites across three
modules (§1.5). Handle both halves in one commit.
R7 — ODR / aliasing (LOW but real). Injecting using TypeFacts = ...::TypeFacts; inside
ProgramObject while a namespace-scope TypeFacts exists in the same namespace is fine, but
ProgramLinkTask.cpp:48 writes
static MobileGL::MG_State::GLState::ProgramObject::TypeFacts MakeTypeFacts(...) — the
fully-qualified-through-the-class spelling. That resolves through the alias unchanged. Watch for any
place that forward-declares one of these types instead of including the header: grep found none, but
re-check after the move. Also: do not leave a second definition behind — the classic P0.5 failure
is copying the struct into the new header and forgetting to delete it from ProgramObject.h, which
compiles (different scopes: GLState::TypeFacts vs GLState::ProgramObject::TypeFacts) and silently
splits the type in two.
R8 — pImpl / join-gate boundary (LOW, but the invariant to protect). LinkArtifacts and
SpirvArtifacts become namespace-scope types, so anyone can now declare one without going through
ProgramObject::Artifacts(). The I5 invariant (ProgramObject.h:1187–1192, :1622–1631) is protected by
m_artifacts being private, not by the type being nested — so the invariant survives the move.
Say so in a comment at the top of LinkArtifacts in the new header, because the current comment
explains I5 in terms of "the member below is private", and that member no longer lives next to the
struct.
R9 — ProgramTranslationCache.cpp:40 takes sizeof(ProgramObject::ResourceReflection). It feeds
a byte budget, not an allocator ("Approximate on purpose", ProgramTranslationCache.cpp:46–48), so a
size change is harmless — but it is a second place that must keep compiling after the alias move.
R10 — the "7 includers" figure is wrong. Verified: ProgramObject.h has 8 non-self includers
(9 with ProgramObject.cpp); ShaderObject.h has exactly 7; SpvcSession.h has 18. Budget P0.5
against the real numbers (§3), and note that if ProgramObject.h drops its SpvcSession.h include
(§6.4) the SpvcSession.h count falls to 17 while every remaining includer is a
ShaderTranspiler/test/benchmark TU that legitimately wants spirv-cross.
R11 — fan-out. MG_State/GLState/Core.h:16 → ProgramState.h:12 → ProgramObject.h means both
backends and all of MG_Impl recompile on any ProgramObject.h touch, and nothing in either
backend directly includes it. That is the reason the extraction buys real build-time and symbol
separation later — and the reason the -H closure assertion should be run against
MG_Backend/DirectGLES/Managers.cpp and MG_Backend/DirectVulkan/DirectVulkan.cpp (the two big
transitive consumers), not only against ProgramArtifacts.h itself.
8. Quick reference — file inventory
| path | why it matters |
|---|---|
MobileGL/MG_State/GLState/ProgramState/ProgramObject.h |
all five types live here today (1803 lines) |
MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp |
ResetLinkArtifacts :332, ApplyUniformInitialValues :143–215, kInvalidUniformOffset uses :198/:273 |
MobileGL/MG_State/GLState/ProgramState/ShaderObject.h |
319 lines; 7 includers; the glslang/ShaderCompileTask path |
MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h |
18 includers; pulls <spirv_reflect.h> :11 |
MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.h |
artifacts :80, SpirvHandoff :92–158 |
MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp |
sole writer of LinkArtifacts.program; MakeTypeFacts :48, MakeResourceReflection :79 |
MobileGL/MG_State/GLState/ProgramState/ProgramSpirvTask.h |
artifacts :46 |
MobileGL/MG_State/GLState/ProgramState/ProgramTranslationCache.h |
ProgramTranslationResult :49–52 — the existing "whole archive as a value" precedent |
MobileGL/MG_Impl/GLImpl/Program/ProgramInterface.cpp |
only consumer that names LinkArtifacts outside ProgramState/ |
MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp |
TypeFactsRef alias :27–28 |
MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp, Managers.cpp |
Espryt accessor call sites (§4.1) |
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp |
SPIRV-Reflect re-derivation :51–305 |
MobileGL/MG_Pipe/MGPipeTypes.h |
MGPProgramDesc :296–308; the two tripwire idioms :43–45 and :527–539; the P0.5 debt-note convention :24–33 |
MobileGL/MG_Pipe/PipeFields.def |
the X-macro field-table prior art :9–18, :83–85, :227 |
MobileGL/MG_Pipe/PipeCalls.def:102 |
X(CreateShaderState, MGPProgramDesc, kCtxCso, kHasBlob) |
scripts/gen_pipe.py |
the generator that consumes the .def files; --check mode is the CI gate |
MobileGL/Includes.h |
:79–83 glslang, :56 spirv-cross, :17 <map> (no <set>), :149 MG_Util/Types.h |
MobileGL/MG_Util/Types.h |
every alias the new header uses (:21–123) |
include/glslang/MachineIndependent/localintermediate.h:627–636 |
TUniformInitializer — the struct to re-declare |
docs/Disaggregated/ARCHITECTURE.md:256–264 |
section 7 |
docs/Disaggregated/ROADMAP.md:16 |
P0.5 row |