Commit Graph
3416 Commits
Author SHA1 Message Date
swung0x48 e258a822e4 [MG_Backend] (DirectGLES): one blank line between the two upload arms 2026-09-20 21:10:33 -04:00
swung0x48 cb64b62b57 [MG_Backend] (DirectGLES): both client-snapshot wrappers are monolith-only
The indexed wrapper reads the bound VAO to tell a client index array from an
element-buffer offset, and the indirect one reconciles the command buffer's
shadow - both frontend reads, and under a transport the entry points run on the
apply thread where the client owns those bytes (Fatal{RoleViolation}, which the
split lane aborted on).
2026-09-20 21:03:16 -04:00
swung0x48 82f509c535 [MG_Backend] (Magma): reconcile a shader-written element buffer before the draw records
The client-memory upload's index scan waits for the GPU (SyncGpuWrites), and that
wait flushes the batch the frame's command buffer belongs to - so a handle taken
before it is stale by the time the draw records into it. Reconciling in the entry
point, before SetupDraw starts recording, keeps the wait out of the recording.
2026-09-20 20:58:48 -04:00
swung0x48 64182b76dc [MG_Backend] (Magma): the indirect client-array staging block is monolith-only 2026-09-20 20:58:18 -04:00
swung0x48 92fe12a2f4 [MG_Backend] (DirectGLES): the indexed client snapshot is monolith-only
The backend draw entry points run on the apply thread when a transport is live,
where MGB_CTX->GetBoundVertexArray() is a frontend read the server role must not
make (Fatal{RoleViolation}) - which is what the split lane aborted on. With a
transport the CLIENT owns those bytes and snapshots them from the GL thread, so
the server has nothing to stage.
2026-09-20 20:58:10 -04:00
swung0x48 e550d10339 [MG_Backend] (Magma): an indirect draw's client-memory arrays are staged from the commands
The commands live in GPU memory, so the draw is issued natively and the vertex
range was left unknown (vertexCount = 0, instanceCount = 1). That is exactly
right for a buffer-backed array and wrong for a client-memory one: its bytes must
be in the store before the GPU reads the command, and the range the upload is
bounded by came out as one element - so the quad the command named was never
staged and the draw painted nothing.

The words are now resolved on the CPU for exactly that case, with the shadow
reconciled first (they may be shader-written), and the draw stays native.
2026-09-20 20:53:57 -04:00
swung0x48 cf7366276e [MG_Backend, MG_Impl] (DirectGLES, Magma): the monolith arm's client fetch bounds
DirectGLES: the client snapshot's baseInstance shift belongs to the backend's
emulation, not to the element set. A driver that applies baseInstance itself
(UseNativeBaseInstance) must not also receive a shifted pointer - doing both
moved instance 0 from element 1 to element 2 and left the instance that reads
element 1 outside the snapshot. EmulatedFetchBaseInstance is not declared this
early in the file, so the wrapper states the same answer inline.

Magma: ResolveIndirectCommandBytes reconciles a GPU-written command block before
the callers read it. Every one of them reads the words on the CPU - the
per-command expansion, the baseInstance the shader emulation is fed, and the
range a client-memory vertex array's upload is bounded by - so a command a
compute shader wrote this frame was expanded as the poison the shadow still
held, which is no draw at all for glDrawArraysIndirect.

Magma: a client-memory INSTANCE-RATE binding is bounded by its instances
(baseInstance + (instanceCount - 1) / divisor), not by the vertex range; the
upload now covers the union of the two, which is what instances shifted off the
front of the array needed.
2026-09-20 20:51:09 -04:00
swung0x48 ae418e80a0 [MG_Backend] (DirectGLES): the client snapshot's baseInstance shift is the emulated one
The element set a divisor'd client array fetches starts at GL's raw
baseInstance, but the pointer shift that carries it belongs to this backend's
emulation: when the driver applies baseInstance itself (UseNativeBaseInstance),
EmulatedFetchBaseInstance answers 0 and shifting the pointer as well would move
the fetch twice - instance 0 read element 2 instead of element 1, and the
instance that used element 1 read past the snapshot and vanished.

Also drops the two 'continue' statements the indirect executors' lambdas cannot
carry (a lambda is not a loop; 'return' leaves the pass).
2026-09-20 20:50:11 -04:00
swung0x48 62bfe4615d [MG_Impl, MG_Backend] (Disaggregated): the monolith arm fetches the client arrays a draw names
The wire arm snapshots the elements a draw fetches out of the application's own
vertex/index memory (beba0256's MGPipeOwnedDrawInputs). The monolith arm's upload
answered only the non-indexed family's contiguous (first, count) run, so an
INDEXED or INDIRECT draw over a client-memory array staged nothing: the enabled
array kept whatever the ES context last held for it - normally disabled, i.e. every
vertex reading the generic current value - and the draw painted nothing with no GL
error anywhere.

ClientFetchPlan.h is that question factored out once (the fetches, the referenced
vertices, the per-divisor element sets) so both arms answer it from one place;
OwnedDrawInputs now calls it and keeps its behaviour byte for byte.

DirectGLES: SyncClientSideAttributesForDraw stages the sparse snapshot for a draw
whose elements the (first, count) run does not describe, driven by the plan; the
indexed entry points and both indirect executors ask for it, and the arrays path
learns the baseInstance shift a divisor'd client array needs.

W3: DirectGLES' six monolith failures are the four indexed/indirect shapes.
2026-09-20 20:47:50 -04:00
swung0x48 e4ae19643d [CI] (Disaggregated): P7's packed-float mip gate flips to green, by name
5310ff62 generates the R11F_G11F_B10F chain with a real GPU shader blit and gives
F1WireScenario.GenerateMipmapPackedFloatPixels pixel assertions, so the entry now
passes under strict split state and `Magma:mipmap-native-format@P7` survives in no
source file. The old arm would therefore have failed its own `test rc -ne 0`.

Both halves of the arm are retired in one commit, because either alone is a lie:
CMakeLists' fm loop drops the special case that gave this slot the
`integration-magma-p7-known-red` label, so the entry is discovered by the ordinary
`integration-magma-split` lane - the same label, environment and pixels assertion
as GenerateMipmapPixels and GenerateMipmapDepthPixels next to it, and the same
label the dual-block census lane already re-runs.

The lane membership alone would not be the old gate's equal: a lane counts its
entries' exit codes, not their absence. So the CI step keeps its shape and inverts
its expectation - the same single named entry, discovered and run by name under
MOBILEGL_IPC_STRICT_ERRORS=1 and MOBILEGL_IPC_ROLE_SPLIT_STATE=1, now asserted
through split_log_paths.py's require_green, which rejects a skip, a missing entry
and a failure alike. No assertion is skipped away or weakened: the pixels are
checked by the test, and the named-refusal check the log carried is what the GPU
blit retired.
2026-09-20 20:34:35 -04:00
swung0x48 3542f64f8c [MGPipe] cover MGPSubData's four appended extent words in PipeFields.def
40c5443f appended LevelWidth/LevelHeight/LevelDepth/LevelPad to MGPSubData and raised
MGP_ASSERT_POD to 88, but left MGP_FIELDS_MGPSubData at its old eight rows.
gen_pipe.py's check_field_lists_cover_struct_members runs in both modes and in
pipe-gates, so "member(s) with no F(...) in PipeFields.def: LevelWidth, LevelHeight,
LevelDepth, LevelPad" reddened both 'Regenerate the MGPipe interface' and
'--self-test'.

The four rows go in declaration order. LevelWidth/Height/Depth are ordinary members
(the record's full staged mip extent, which the ResourceSubData codec's
"ResourceSubData.LevelExtent" arm bounds the dirty box against). LevelPad is padding
by role only: PADDING_MEMBER_RE is ^Pad\d*$, so a member named LevelPad is a member
to the cover check and MUST have a row. That row risks no false positive - every
producer builds the record as `MGPSubData{}` and the payload rides the wire as one
POD memcpy of all 88 bytes, so both sides of a comparison read the same zero.

Field tables only describe the existing struct, so no generated file changes:
--check stays byte-identical.
2026-09-20 20:32:58 -04:00
swung0x48 0fa4a98139 [MG_Backend] drop the invented point count from the handle arm's memo comment
The record carries eleven surfaces, but the walk writes the attachment points
only - eight colour plus depth and stencil - and the read surface is applied by
ApplyReadBufferFromRecord rather than by the walk. Say "the SAME attachment
points on it" instead of a count that is wrong for the walk.
2026-09-20 19:56:27 -04:00
swung0x48 fd30b5c30a [MG_Backend] (Magma): keep a view's mip window and readback inside the layers it names
Magma's generate path (VulkanRenderer::GenerateMipmap) took its window from the
BOUND object and its blits from the STORAGE image, and the two do not coincide
for a texture view: glTextureView gives a view its own BASE_LEVEL/MAX_LEVEL and
layer slice over somebody else's image. Three things followed from the mix-up,
all visible through a two-layer view over a four-layer 2D array:

  * EnsureGenerateMipmapStorageAllocated allocated the chain THROUGH the bound
    object from that object's OWN base extent, so the storage owner's level was
    resized to the view's layer count - the storage behind its other layers was
    gone - and the levels outside the view's window were redefined with it.
    Immutable storage and views already own every level generation can write,
    so neither allocates anything now, and a mutable owner gets only the levels
    its window is missing (AllocateStorage RESIZES the level it is handed, so
    an existing level keeps the shadow bytes its own upload path wrote).
  * The level the chain starts at was the view's, applied to the storage image:
    a view whose MIN_LEVEL is 1 generated from the storage's level 1 instead of
    its level 2. The window now comes from the frontend's own plan
    (MipmapGenerationPlan.h - the same numbers MGPMipPlan::LevelCount publishes)
    and is shifted into the storage's numbering before any blit or barrier.
  * Every layer of the storage was written instead of the view's window. The
    blits carry the view's baseArrayLayer and layerCount now; every layer
    outside that window belongs to the storage owner and is left as it was.

The end of the window follows MAX_LEVEL for the same reason the frontend's plan
does: a chain MAX_LEVEL cuts short is no longer generated past it.

The readback had the same shape of bug pointing the other way.
VulkanRenderer::GetTextureImage sized its copy by the storage image's array
layer count while starting it at the caller's layer origin, so a two-layer view
of a four-layer array copied four layers - two outside the window, and the last
one past the end of the image. glGetTexImage carries no destination size, while
the size glGetTextureImage validates is built from the VIEW's own extents, so a
caller that sized its buffer for the view had it overrun by exactly the layers
outside its window. The copy covers the caller's own layers now.

TextureViewScenario.GenerateMipmapThroughAViewLeavesTheOwnerShapeAlone runs on
both backends now. Its DirectVulkan skip named the reallocation above, and the
sub-image readback the skip blamed as well is not broken: a control run of the
same scenario with the generate removed reads every seeded level and layer of a
2D array back, mip 2 and 3 included, through both glGetTexImage and a per-layer
FBO attachment - the two mismatches that control reported are the colours the
generate was supposed to write. GetTexImageThroughAViewReturnsOnlyItsOwnLayers
is new and pins the readback window with canary slices past the view's last
layer; it fails on the reverted readback and passes on DirectGLES too.
2026-09-20 19:48:16 -04:00
swung0x48 b16531c3a8 [Docs] record the landed stage chunking in the R-10 texts
Every place that stated "P5 does not chunk" as current behaviour except the
Fatal texts (previous commit) now records the stage chunk budget and what it
does not cover.

- CONTRACT-P5.md section 5: the STAGE_MB row names MGPipeStageChunkBytes() =
  clamp(segment/4, 4096, segment), the two buffer walks (1e7c372e) and one
  texture level's whole-width slabs (9469d48e, assembled by
  StagedTextureStore::AdoptRun), and the Fatal that stays the backstop for a row
  with no cut; the RING_MB row says the record bound is about the record's own
  bytes.
- BRIEF-P5.md's R-10 section is no longer headed "does not chunk": it keeps the
  record bound and the maxrec proof obligation unchanged and records that
  content is cut, with the encoder Fatal as the fallback.
- ROADMAP.md's staging debt row and open question 11, ARCHITECTURE.md's
  record-bound bullet and SEG_STAGE table note, README.md's knob row,
  MEASUREMENTS.md section 6.3 and CURRENT_STAGE_PROGRESS.md's open item: the
  explicit MOBILEGL_IPC_STAGE_MB=256 profile is no longer needed for the two cut
  paths, and the carrier design for the rest stays P8.
- Historical results (notes/**/p5-results/, p5b/) are left exactly as recorded.
2026-09-20 19:39:56 -04:00
swung0x48 a63577354d [MGPipe] the Fatal texts no longer claim P5 does not chunk
R-10's "P5 does not chunk (R-10)" wording predates the stage chunk budget: buffer
content ranges are cut at MGPipeStageChunkBytes() (1e7c372e) and a texture level
at whole-width slabs (9469d48e), so the claim is false for the two rows that can
outgrow SEG_STAGE - and these messages are what a bring-up reads.

- PipeWireEncoder::StageAllocate's two Fatal{RingOverrun, "SEG_STAGE"} sites now
  say what reaching them means: a blob is staged whole, a row that cuts its
  content at the stage chunk budget never arrives, so this is a record type with
  no cut or a piece still larger than the arena.
- EncodeRecord's MaxRecordBytes arm says the bound is about the record's own
  bytes - the stage chunk budget cuts blobs, not a header, payload or inline
  tail - and keeps the integrator decision it names.
- Comments in PipeWireCodec.h, EmitTables.cpp's maxrecop line, Config.h's
  STAGE_MB knob, PipeStats.h's MaxRecordBytes gauge, SplitRuntimePeek.h and
  WireLedgerChecks.h said P5 does no chunking; they now say what is cut and what
  is not.
- PipeWireCodecTest.ARecordLargerThanHalfTheRingIsFatalRingOverrun asserts the
  new diagnostic text instead of "does not chunk (R-10)".
2026-09-20 19:39:51 -04:00
swung0x48 e004269d96 [MG_Backend] key the handle arm's attachment walk on the object, not the binding
SyncToBackendByHandle gated its record-driven attachment walk (the eleven
applyPoint calls plus NoteFramebufferTextureAttachments) on
m_syncedRecordHashes[asTarget], a PER-TARGET slot, while the work inside is
object-level: glFramebufferTexture2D/glFramebufferRenderbuffer write the
driver FBO behind whichever binding is current, so a Draw walk and a Read walk
write the same points. Two slots can therefore hold two individually true,
jointly false accounts of one physical state.

improved-transparency flips a composite framebuffer between two attachment
states and syncs one of them as DRAW in a depth pre-pass. That walk detaches
the colour point, stamps the Draw slot; the end-of-frame READ-side sync of the
other record then found its own slot already stamped by an earlier identical
sync, walked nothing, left the point detached, and the driver refused the
following glBlitFramebuffer with INVALID_OPERATION - the default framebuffer
froze from frame 835. The object form is immune because a per-point version
array gates it underneath; the handle arm has no frontend array, so this memo
is its only gate.

Replace it there with m_syncedAttachmentRecordHash, one slot for the object.
The per-target array stays for the object arm, where it is a re-arm trigger
over that version array rather than a claim. InvalidateSyncedState now zeroes
both memos: it invalidates the version array, which is not the handle arm's
gate, so a memo left standing would let a re-sync of the same record skip the
re-attach that call exists to force.
2026-09-20 19:36:16 -04:00
swung0x48 7059869848 [Test] fix the two new stage-chunk cases' own expectations
Both cases were written against the wrong property and went red on the first WGB
build, which is the one thing a case may not do.

TextureEmit.ALevelTooLargeForTheStageChunkIsCutIntoSlabs asserted D == 8 slices for
every slab of the 512x128x33 level, whose last slab is the 33rd slice alone. The
per-slab check now names the scheme (Z = 8 * index, D = 8 for every slab but the
remainder) instead of restating one number for all of them.

StagedTextureStoreTest.ALevelWithAPieceMissingIsRefusedAsAWholeLevelReadByName
wanted "not covered" out of a single LEADING run, and the store cannot answer that:
it knows no format, so it cannot know how many bytes the level should have - the
check is "the range the reader asks is covered". The case now uses the shape the
check really is about, two runs with a hole between them (ServerLoopTest's
R-11 coverage case, one store over), and the looseness is written down: in the
header's coverage ruling, and as an assertion on the leading-run case beside it.
2026-09-20 19:22:38 -04:00
swung0x48 9469d48edb [MGPipe, MG_Remote] cut a texture level's staged run at the stage chunk
ONE RECORD'S BLOB IS STAGED WHOLE IN SEG_STAGE, a linear arena, so a texture
level larger than the segment's chunk budget was Fatal{RingOverrun, "SEG_STAGE"}
at the encoder rather than a split - and the CI traces hit it: the texture half of
resource_subdata stages the whole level shadow regardless of how little of it
changed, and a 512x128x33 GL_RGBA32F level is 34,603,008 bytes while a 192-cube
GL_RGBA16 level is 56,623,104, both against the default 32 MiB segment. Three
retrace cases aborted there: iterationt-in-world and iterationt-nodsa-in-world in
both backends, and iterationrp-in-world in DirectVulkan.

THE CLIENT now cuts such a level into WHOLE-WIDTH SLABS, one resource_subdata
each (TextureEmit.h's MGPipeForEachTextureSlab, driven at MGPipeStageChunkBytes -
the same cap PipeFill.cpp's buffer walks are cut at). A piece is a set of
consecutive rows of the level shadow, so its box names both its texels and its
byte run; whole slices are the coarsest cut, a slice bigger than the cap falls
back to whole rows and a row bigger than the cap to whole texels inside one row.
Every piece is emitted, dirty or not: the server assembles one level image out of
the runs, so a piece left out would leave a hole no texel read can answer. Each
piece carries at least one region (its clipped dirty rects, or its own box when
none reaches it) because RegionCount == 0 remains the whole-level spelling, and
each region's SrcOffset is rebased onto its own piece's run - MGPipeTypes.h has
always said SrcOffset is "into the blob" - while the strides stay the level's. A
level that FITS keeps the single whole-level record, field for field what it has
always been, which is what every existing gate is written against.

THE SERVER now assembles levels from those runs. StagedTextureStore::AdoptRun
places one run at an image offset, its coverage set is exactly the runs it has
adopted (StagedShadowStore's CoverageAdd, the buffer half's helpers), and only a
FULLY covered level satisfies IsCovered / RequireLevelBytes - a level with one
piece missing is as unreadable as one that never arrived, and it is the same
Fatal{StageSnapshotTooNarrow}. The whole-level spelling survives as Adopt, its
offset-0 replacement, and the two are told apart by the run's own LENGTH: the
whole-level run is the level's complete shadow whatever its box says, a piece's
run is exactly its box's byte extent. Both adoption call sites -
Ops_H_TextureSubData and PipeApply.cpp's hookless fallback, which is what Magma
takes - read the offset that way.

THE PROTOCOL IS UNCHANGED: no field moved, no caps bit was added. The one
compatibility statement is that a split level now crosses as several records of
one apply batch, so an OLD server (which replaces the level on every record) would
keep only the last piece - inproc runs both roles out of one library, so the
constraint is noted here rather than armed with a gate.

Tests. TextureEmit.ALevelTooLargeForTheStageChunkIsCutIntoSlabs drives the split
at MGPipeStageChunkBytesFor's clamp with both trace shapes (5 and 7 slabs), the
row-granularity and texel-granularity fallbacks, the three shapes that refuse to
cut, the region clipping/rebasing, and the piece no rect reaches.
StagedTextureStoreTest gains the multi-run assembly, the gap that must not read as
coverage, the partial-level Fatal by name, the run-offset discriminator, and two
production cases - the applier's hookless arm and DirectGLES's real TextureSubData
hook - that would put both halves at the front of the image if the offset were
dropped.
2026-09-20 19:19:47 -04:00
swung0x48 ed919f0d64 [MG_Impl] guard the monolith mip generate arm against immutable storage and views
EnsureGeneratedMipmapStorageAllocated branched on the transport. The split arm
planned the generation window from BASE_LEVEL/MAX_LEVEL and refused to touch
immutable storage (70c8230b); the monolith arm rebuilt the chain from the bound
object's level-0 extent and then truncated it. Through a view that is
destructive in a way the split arm had already been fixed for: a view's level-0
extent is one slice of the owner's layers, so a two-layer view of a four-layer
array resized every owner level the generation touched to two layers - the
storage behind the owner's other layers was gone - and redefined the owner's
levels out of the view's window as well.

Both arms now take one path. An immutable texture or a view returns before any
allocation: it already owns every level generation can write. A mutable texture
gets levels from the plan's Base..End window and nothing else moves - the chain
is not truncated, because neither MAX_LEVEL nor a generate is a truncation (a
level-0 respecification drops the stale tail itself, in
DiscardMipmapChainOnBaseRespecification). MipmapGenerationPlan.h is no longer
behind MOBILEGL_BUILD_DISAGGREGATED for the same reason: the window is a
property of the GL call, not of how the backend is reached, and the plan the
wire carrier publishes (MGPMipPlan::LevelCount, end-exclusive) is this one.

The window rule was asserted only by the split-only F1WireScenario, whose
fixture skips under every other transport - so the monolith arm, which is what
a non-disaggregated library runs for every application, had no coverage at all.
TextureViewScenario.GenerateMipmapThroughAViewLeavesTheOwnerShapeAlone runs in
an integration-gpu lane under both transports and pins the owner's layer counts,
its own level extents, and the pixels of the levels and layers outside the
view's window. It is DirectGLES-only, and the skip names why: Magma reallocates
the chain through the bound object in its own generate path
(DirectVulkan/Renderer/VulkanRenderer.cpp's EnsureGenerateMipmapStorageAllocated),
and a CPU sub-image into the layers of mip 2 or 3 does not read back through
Magma at all - a run of the same scenario with the generate removed fails
identically - so the per-level colours this case is built on are unobservable on
that backend rather than merely wrong.
2026-09-20 19:17:17 -04:00
swung0x48 f1d3fa8f1a [MG_Backend] keep the default framebuffer on the image GL last rendered into across a swap
DirectVulkan resolved every default-framebuffer role against
m_imageIndexAcquired. Present() advances that to the image it acquires for
the frame that is about to start, and a present record is kWaitPresent - the
client does not wait for the apply - so a readback belonging to the frame
being presented (the retrace snapshot, taken at the swap call) can be applied
after the acquire and then resolve an image nothing had written. That is the
pure-black sundial-lite DirectVulkan retrace frame; DirectGLES is immune
because the driver keeps handing back the buffer the app drew into.

Split the GL-visible index out (VulkanRenderer::m_defaultFramebufferImageIndex)
and keep it out of the present/acquire path: it only re-points at the acquired
image when something WRITES the default framebuffer (DefaultFramebufferWriteIndex,
called at the FBO-0 render-pass creation, the deferred-clear-free direct writes
and the wire draw/clear/blit resolutions) and is reset on RecreateSwapchain.
Reads - glReadPixels both arms, the parked-clear materialization, a blit or copy
SOURCED from FBO 0, ResolveWireImage's read role (carrying the resolved index in
WireImage) - resolve DefaultFramebufferReadIndex instead, i.e. whatever that is.

A readback that resolves a swapchain image with no recorded write now says so
once (MGLOG_W_ONCE, both arms) instead of silently handing back raw pixels.

New integration scenario DirectVulkan|DirectGLES.DefaultFramebufferAcrossSwap
pins both halves: a readback after a swap still sees the drawn frame, and the
first draw after a swap lands in the image the readback then resolves.
2026-09-20 18:52:08 -04:00
swung0x48 73b37bbe83 [MG_Backend] (DirectGLES): clip view mip generation to the view's layer window
glGenerateMipmap's native arm carries BASE_LEVEL/MAX_LEVEL of the view's own
ES name, so the LEVEL axis was already clipped the way the view describes, but
the layer set it writes is every layer of the storage the view aliases. A
generation through a windowed texture view therefore rewrote the owner's layers
outside the window (F1WireScenario
.GenerateMipmapThroughViewKeepsOwnerLevelsAndLayersOutsideItsWindow).

Both arms now read the window and generate the chain with per-layer blits
between the owner's levels MinLevel + L, over layers
[MinLayer, MinLayer + NumLayers), and leave the native call for the shapes it
already gets right: no view, a view whose layer window covers the whole
storage, a multisample view, and aspects the layer blits do not carry. The
record arm reads the window from the sampler view CSO with the same validation
SyncTextureViewToBackendByRecord uses (Fatal{ProtocolCorruption} on a stale or
foreign record); the monolith arm reads the same numbers off the frontend view
object. ScratchFBOImpl gains EnsureDepthAttachmentLayer for the depth-only
aspect.
2026-09-20 18:47:26 -04:00
swung0x48 8ffb87109a [MGPipe] include the stage-chunk helper where it is called
PipeFill.cpp reaches MG_Remote::Client::MGPipeStageChunkBytes through
GpuWritePending.h, which the TU did not include: the declaration was visible to
the helper's own TU and to no other, so both MobileGL translation units for
PipeFill.cpp failed to compile while the shared objects it defines built fine.

PipeFill.cpp.
2026-09-20 18:46:13 -04:00
swung0x48 bde7624310 [MGPipe, Test] pin the stage-chunk split and the empty-store respecify
ResourceEmit.AWideBufferSubDataSplitsAtTheStageChunkBytes: the walk driven at
MGPipeStageChunkBytesFor's clamp (a quarter of the segment, floored at 4096 and
never above the segment), with the trace's own 128 MiB arena against the default
32 MiB segment - sixteen contiguous ascending pieces, each encodable by the
record builder, covering the range exactly once. The no-session answer (0 = keep
the record's own bound) is asserted beside it.

ResourceEmit.AZeroByteRespecifyDefinesTheStoreAndCarriesNoContent: armed the way
the split-only unit cases are (Transport = InProcess, an op table, and the caps
mirror R-8's liveness gate reads), BufferObject::Respecify(0, nullptr) must not
abort, must land Width == 0 && HasDefinedContent == 1 on the applier, and must
emit no resource_subdata at all. The drive runs in a child because the defect's
verdict is std::abort() and a case that aborts its own binary reports nothing.

MG_Test/Pipe/ResourceEmitTest.cpp.
2026-09-20 18:45:34 -04:00
swung0x48 07f91f352a [MGPipe] carry no initial bytes for an empty store
glBufferData(target, 0, NULL, usage) DEFINES its store - that is not the
orphaning idiom - so HasDefinedContent is set, and MappedData() answers a
non-null pointer for it because the shadow reserves one byte whatever the size
(PipeResource.h:140-143). Reading MappedData() alone therefore put the call on
the split arm's respecify(nullptr)-plus-follow-up shape, whose walk emits
nothing for a zero-length range (ResourceTracker.h:253), and the
InitialBytesNotCarried self-check aborted by name on the first such call in the
bsl-esc-menu trace.

The predicate is 'HasDefinedContent && GetSize() > 0': there are bytes here,
not merely a defined store. The split arm's own condition is untouched - moving
it instead would send the zero-byte case to the UncarriedInitialBytes refusal
in the wire escape.

PipeFill.cpp.
2026-09-20 18:45:29 -04:00
swung0x48 1e7c372e6c [MGPipe] cut buffer content ranges at the stage segment's chunk budget
One resource_subdata record's blob is staged whole in SEG_STAGE, so a range
larger than that arena is Fatal{RingOverrun, SEG_STAGE} at the encoder rather
than a split: the CI traces abort there on a 128 MiB arena's whole-buffer
follow-up against a 32 MiB segment.

- ClientSession::StageCapacityBytes(), the twin of EventRingCapacityBytes, so
  the emitter can size against the arena the codec allocates from.
- MGPipeStageChunkBytes(): clamp(segment/4, 4096, segment), 0 when there is
  nothing to fit (monolith, the server role's own uploads, no session).
- PipeFill.cpp's two buffer content walks take that cap instead of the
  record's own 2^32-1 bound, so an over-long range becomes several contiguous
  ascending records. StagedShadowStore::Adopt already accumulates coverage.

ClientSession.h/.cpp, GpuWritePending.h/.cpp, PipeFill.cpp.
2026-09-20 18:45:24 -04:00
swung0x48 6f0248048d [Docs] add CI/inproc continuation handoff (2026-09-20) 2026-09-20 18:16:45 -04:00
swung0x48 5cbf29b041 [CI] retain failed retrace-split artifacts and read full inventories before cleanup
The remove-artifact-clutter jobs deleted trace fixtures and results while
reading job/artifact inventories through unchecked process substitution, so
a failed GitHub API page looked like 'no failed cases' and fixtures for a
red retrace could be deleted. test.yml cleanup also did not wait for the
retrace-split lane even though those jobs download the same fixtures.

Read both paginated inventories into files before any deletion so an API
failure fails the step, add retrace-split to the cleanup dependencies, and
retain fixtures plus result artifacts for failed cases in all three lanes.
2026-09-20 18:16:44 -04:00
swung0x48 70c8230b3e [MG_Impl, Test] preserve immutable view storage and plan mip generation within base/max levels 2026-09-20 13:10:58 -04:00
swung0x48 ed01f4e31a [MGPipe] preserve explicit mip extents in the shared texture staging owner 2026-09-20 13:02:56 -04:00
swung0x48 2426abb814 [MG_Backend] (Disaggregated): bound mip generation to the logical base and view window 2026-09-20 13:02:51 -04:00
swung0x48 7b92642999 [MG_Backend] (Disaggregated): preserve the untouched aspect of depth stencil resolves 2026-09-20 13:02:04 -04:00
swung0x48 0a91fa4197 [Magma] diagnose unavailable owned mip readback bytes 2026-09-20 12:55:41 -04:00
swung0x48 5310ff62bb [MG_Backend] (Disaggregated): generate packed float mips with native shader blits 2026-09-20 12:51:19 -04:00
swung0x48 d339053ea6 [Magma] reject truncated or overflowing RGB staging before expansion 2026-09-20 12:51:17 -04:00
swung0x48 677922a930 [Tests] pin full texture staging extents beside scattered upload regions 2026-09-20 12:49:50 -04:00
swung0x48 616f3c90d2 [Magma] preserve packed texture storage and read unbacked sparse levels from owned bytes 2026-09-20 12:48:34 -04:00
swung0x48 40c5443fc0 [MGPipe] carry full mutable mip extents independently of dirty upload boxes 2026-09-20 12:46:01 -04:00
swung0x48 0cb0e9af9b [MG_Backend] (Disaggregated): resolve multisample depth and stencil through renderpass2 2026-09-20 12:44:37 -04:00
swung0x48 e2d6e33db4 [MG_Backend, MG_Impl] complete GLES XFB lifetimes, lazy readback and sampler windows 2026-09-20 12:43:07 -04:00
swung0x48 c3a5d2f58e [MG_Pipe] preserve image-unit slots and publish named storage-binding programs 2026-09-20 12:41:29 -04:00
swung0x48 ec4ca06d1e [MG_Backend] (Disaggregated): resolve multisample color through submission-owned scratch images 2026-09-20 12:31:28 -04:00
swung0x48 c7de0dffc1 [MG_Test] pin every migrated query and XFB deletion verb boundary 2026-09-20 12:30:52 -04:00
swung0x48 1389e8e7f6 [MG_Backend] (Disaggregated): resolve cube face attachments from their wire upload target 2026-09-20 12:27:32 -04:00
swung0x48 5922e54b47 [Magma] provide native placeholders for unbound wire image descriptors 2026-09-20 12:21:36 -04:00
swung0x48 4610b573f4 [MG_Remote] (Disaggregated): gate timer caps before renderer creation and advertise native primitive queries 2026-09-20 12:18:08 -04:00
swung0x48 03c6b8c979 [Test] keep texture upload shape coverage on inproc with applied-frame stats boundaries 2026-09-20 12:16:23 -04:00
swung0x48 cf0f77b067 [MG_Backend, Test] isolate native mip completeness during GLES texture readback 2026-09-20 12:13:19 -04:00
swung0x48 347bb90c4a [CI] make migrated client arrays and full inproc status hard gates 2026-09-20 12:07:52 -04:00
swung0x48 bb816906a7 [Magma] resolve copy-image renderbuffer endpoints from wire storage 2026-09-20 12:07:28 -04:00
swung0x48 39e1692861 [MG_Remote, Espryt] copy image regions through server-owned renderbuffer handles 2026-09-20 12:03:47 -04:00