mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
bb2a236d5f76f5d5b3df2cdc16e61adf98a3cabb
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
59191cd296 |
[Fix] (Pipe, DirtySurface): resolve both sides of the dirty-surface derivation to member+field, follow reference and pointer aliases, and turn every write the analysis cannot place into an UNDECIDED answer instead of a verdict
- The writer side recorded a member-rooted write bound to a reference (`for (auto& blendState : m_parameters.BlendStates)`) as the field alone, so seven RenderState setters read as writing nothing; the reader side resolved `render.PatchVertices` to the whole of m_parameters, so every setter that touched any byte of it "supported" NEW_PATCH_STATE and a row saying glClearColor publishes the patch state was green. Both were the same defect: the two sides did not resolve to the same token. - Both sides now carry MEM:<member> and FIELD:<member>.<leaf>; a whole-member write or read is every field. A reference, pointer or range-for alias bound to a member-rooted lvalue is followed (rebinds and aliases of aliases included), a write through a call-result lvalue and a mutating call on a member-rooted lvalue count as whole writes, a const alias cannot be written through with `.`. - A write, or a non-read-only method call, whose root the analysis cannot place - a reference parameter, a call result, a member without the m_ prefix, an unattributable assignment operator - taints the function; the taint rides the call-graph fixed point and every (row, bit) that depends on a tainted function is UNDECIDED, never a verdict. Nothing is trusted by name. - Match rule: a writer supports a bit iff the two sides share a member and, both field-resolved, their field sets intersect; a member in common with no field information on one side is COARSE, reported and never counted; UNDER-FIRING only when both sides are resolved and disjoint for every member the shutter reads. - --check counts only supported answers as derived, prints the COARSE and UNDECIDED tallies, and fails on an UNDECIDED row unless MGP_DIRTY_SURFACE_UNDECIDED_LIST in DirtySurface.def marks it; a mark on a row the derivation decides is a red gate too. The list is empty: all 8 non-render bit answers are supported at field level, and NEW_PATCH_STATE has exactly three legal carriers again. - --self-test grows from 10 to 21 negative controls, including the synthetic bodies of every shape above through the real extractor, the NEW_PATCH_STATE analogue of the value-class control, the taint, COARSE and stale-mark paths, and positive controls for SetPixelStoreParam's pasted writes and the seven alias setters. |
||
|
|
8d0ed5b82c |
[Fix] (Pipe, DirtySurface): read the writes that go through a member's field and the ones the preprocessor pastes together, and decline the rows the write analysis cannot answer - its "UNDER-FIRING" verdicts were an absence proof it did not have, and one of them put a false answer in the map for a bit P2 already ships
- the write analysis under-approximated in the exact direction its own claim forbids: written_tokens recorded a write through a member's field (m_foo.bar = v) as FIELD:bar and never as MEM:m_foo, while resolve_reader resolves a shutter's accessor to MEM:m_foo, so for any struct-valued member the two halves could not meet; a new MEMBER_ROOTED_WRITE_RE records both, for m_x.f, m_x[i].f, m_x->f and nested - it could not see RenderState::SetPixelStoreParam's sixteen writes twice over, because they are spelled with the token-pasting operator and the file was read raw - the "field" it recorded was the macro parameter name, paramNameTail. The derivation now expands the function-like macros defined under its two roots (directives blanked, parameters substituted, ## pasted), which is also what makes SET_CAPABILITY's m_parameters.capability##Enabled writes visible - and it now DECLINES rather than answers wherever it cannot say it read every writer: a body carrying a construct it does not model (an unexpandable token paste), anything that reaches such a body through the call-graph fixed point the writes already travel, and any shutter member with a write-shaped occurrence outside the analysed roots. --check prints every decline with its site, plus how many bodies and files the absence claim rests on and the one place it stays coarse - the BitwiseEqual bits' shutter window now also starts at the last `}` before the `dirty |=`, so the pack block's trailing `m_pack = pack;` no longer leaks the pixel store into NEW_PATCH_STATE's reader set - consequence in the map: X(SetPixelStoreParam, NEW_PIXEL_PACK) went from a verdict the gate could not support - no function name in the tree could carry that bit - to an accepted, checked answer, and the row it forced (kPulledEveryVerb, documented as "no shutter exists, and none is needed yet") said that of the only mutator behind the shipped set_pixel_pack_state. The row is now kPulledPartialShutter|NEW_PIXEL_PACK: the pull is what holds on every mutating path, the bit moves on the eight Pack arms, and both facts are machine-readable for the P3a reader D16 writes this file for - --self-test grows from 7 negative controls to 10 - kPulledPartialShutter naming no bit, a mutator whose write analysis is incomplete, and a shutter member written outside the roots, the last two asserting a DECLINE and no verdict - and gains a positive control that fails if SetPixelStoreParam's pasted writes ever go unread again |
||
|
|
f15b0fdf4b |
[Fix] (Pipe): derive the dirty-surface map's object-class and value-class answers too, and correct the two rows that named a shutter their mutator never moves
- X(SetNamedTransformFeedbackBinding, NEW_SO_TARGETS) was false on EVERY path: that mutator binds a BufferState binding point or writes a saved-bindings entry, while the bit's shutter mixes the buffer-CONTENT aggregate with the transform-feedback generation, and a binding moves neither. It answers kPulledEveryVerb, which is what reaches the backend today (GetBufferBindingPoint, in the class's may-read mask). - X(SetPixelStoreParam, NEW_PIXEL_PACK) was false on the eight Unpack arms: the setter writes both halves and the tracker's bit 2 is a byte compare of the PACK half alone, because set_pixel_pack_state deliberately has no unpack counterpart. It answers kPulledEveryVerb, the one publisher every arm has. - --check no longer rubber-stamps the 28 rows the RenderState derivation cannot reach. It reads Tracker.h's Update() for what each bit's shutter READS, resolves those accessors through MG_State's getters to the members behind them, computes what every mutator transitively WRITES as a fixed point over MG_State/GLState and MG_Impl/Pipe (expanding MGP_NOTE_AGGREGATE through MGPipeNoteAggregate's own switch rather than assuming the hop), and fails a row naming a bit whose shutter its mutator moves on no path. One-directional by construction: the write analysis over-approximates, so it can prove absence and not presence, and absence is the under-firing direction. - the enumerator spelling and the NEW_* spelling are paired BY POSITION out of Tracker.h, so the enum and kMGPipeDirtyNames drifting apart is itself a gate failure. - two more self-test negative controls, one per family, both built from the defect that was really in the file; 7 controls now, all tripping. - --check prints what it did NOT check: how many rows carry a prose answer, and every row the derivation declined, so "all mapped" cannot be read as "all verified". - render_state_publishers() folds the bodies of one name with INTERSECTION, so two overloads - one BumpVersions, one bare ++m_version - can no longer derive as "both always fire" and bless an under-firing row. - the header states what "every path" means: every path that MUTATES, so a redundant-write guard does not make its publisher conditional, while a publisher reached on only some mutating paths must not be named. |
||
|
|
c574043c13 |
[Fix] (Pipe): derive the render-state answers of the dirty-surface map from RenderState.cpp instead of believing them, and correct the two rows that named a publisher which does not always fire
- SetCapability named NEW_PIPELINE_STATE, but its ClipDistance0..7 arms write ClipDistanceEnabledMask (dynamic chunk D7) and deliberately do not BumpVersions, so that publisher does not fire at all for glEnable(GL_CLIP_DISTANCE0); SetStencilFunc named it too, while ++m_pipelineStateVersion there is conditional on Func moving, so a ref-only glStencilFunc does not move it either. Both are now NEW_RENDER_STATE, the answer that holds on every path - a row may now carry several publishers joined with '|', which is what lets the 18 setters that call BumpVersions on every path state both counters, and the patch trio state its own bit and the two render counters it also moves - --check no longer validates only row existence and answer vocabulary: it reads RenderState.cpp, derives per setter which of the two counters moves on EVERY path (BumpVersions moves both, a bare ++m_version only the first, a setter with both kinds of path only the first, a delegating setter inherits its callee's) and fails when a row claims a publisher that under-fires or omits one that always fires - two new self-test negative controls, one per direction, both built from the defects that were actually in the file - MarkProgram/MarkProgramPipeline/MarkShaderForDeletion answered kExplicitDestroy, a mechanism D13 does not build for them: Espryt 0b's explicit destroy is scoped to six object kinds that exclude programs, pipelines and shaders. They answer kUnpublishedDestroy now - a recorded hole rather than a mechanism that does not exist |
||
|
|
3302ee82b5 |
[Feat] (Pipe): map every frontend mutator onto the aggregate generation that publishes it, and make the dirty-surface scanner a gate
- MG_Pipe/DirtySurface.def: 73 rows, one per distinct mutator the scanner finds, each answering
"what publishes this". The answer vocabulary is a MGPipeDirty bit name or one of five
non-bit answers, and each of the five is documented in the file's header rather than left to
be inferred: kImmediate, kReverseChannel, kNoBackendRead, kExplicitDestroy and
kPulledEveryVerb. Where a mutator has more than one true answer the row carries the COARSER
one - the one that cannot under-fire.
- gen_pipe_dirty_surface.py --check is the gate and it fails in BOTH directions: an unmapped
mutator renders stale, and a row naming a mutator the scan no longer finds keeps a real hole
looking covered. It also rejects an answer that is neither a documented non-bit answer nor a
bit name read out of Tracker.h's own kMGPipeDirtyNames, so a renamed bit cannot leave a row
silently pointing at nothing.
- --self-test runs three canned negative controls - a withheld mutator, a stale row, a bad
answer - and each must trip; trips == 0 is itself an error, the shape
check_include_closure.py and gen_pipe.py --self-test already use. ROADMAP.md's rule is that
every gate must be able to go red for the reason it exists.
- --summary keeps working unchanged, because the CI file that still calls it belongs to
another package until it lands.
- The human report prints the mapped answer where it printed UNMAPPED.
- FillPoints.def: the verdict on the eight statically over-approximated rows, recorded per
group in the def's own comment. All eight are KEPT and the reason is the same in all three
groups - each row names a concrete backend path (the depth/stencil read emulation's paused
capture, VkClearManager::PreCompensateSrgbClearColor's GL_FRAMEBUFFER_SRGB read, the shader
blit's viewport / provoking vertex / binding-point reads), and the only evidence that could
retire one is dynamic. A corpus that never reaches a path proves nothing about it, and a row
dropped on that basis turns a rare path into Fatal{UnmigratedPipeInput} in a shipped build.
The contract's new FramebufferSrgb storage in fact makes one of the eight MORE load-bearing
than it was, not less: it used to read a compile-time constant.
|
||
|
|
9bbf71990c |
[Build] (CI): add the pipe-gen check, the stdio-instrumentation gate and the citation lint
- Three P0 gates from plan B section 11, in one job that needs no build: a broken build must not be able to hide a drifted interface. - pipe-gen-check regenerates G1-G7 and runs git diff --exit-code over MobileGL/MG_Pipe/generated. Since all seven generators read the same .def files, this is what makes drift between them impossible rather than merely unlikely. - The stdio gate refuses fprintf(stderr and printf( under MG_Backend and MG_State. Nothing there matches today, so it lands with NO whitelist - verified with a negative control that fprintf(stderr and std::printf both trip it while snprintf does not. Per-draw instrumentation has been committed by accident before, once inside a mutex critical section, and MGLOG_D is the channel these trees are allowed to use because it compiles out in INFO builds. - scripts/gen_pipe_dirty_surface.py reports the frontend mutation surface corollary 4 needs covered: 926 mutator calls under MG_Impl/GLImpl over 73 distinct mutators, of which only 92 sit in a function that also reaches the backend. The other 834 are published by the NEXT verb, which is precisely the population that needs an aggregate generation. Informational in P0; it becomes a gate in P1 when there is a mapping file to diff against. - scripts/check_doc_citations.py resolves every `path:line` citation against a git revision. It reproduces the failure that motivated it - the plan's first draft cited SamplerObject.h:468-492 in a 160-line file - and reports 84 unresolved citations out of 1028 in docs/Disaggregated today, which is why CI runs it warning-only until those documents settle. --strict exits 1, verified in both directions. |