mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-11 12:03:24 +09:00
[MG_Backend, MG_IntegrationTest, docs] (P7): the readback family runs on the split arms, and it was already green
P3b/P4b espryt D1 slice 4, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "回读 / pack state", §3 wave 2-D).
(a) THE CENSUS, AND IT CAME BACK CLEAN. Twenty-two cases per arm now run on the three split
arms: the two F1 pack-state cases, PixelStoreSweepScenario, FramebufferChurnScenario,
PackedWordReadbackScenario, LayeredTextureReadbackScenario and
DepthStencilReadbackMatrixScenario. The row expected reds and said so ("Expect reds - that
is the point"); the first run was 65/65 before any of this commit's code changes. That is
the finding: the 回读 half was assumed open because nothing had ever run it under a
transport, and the assumption was what was wrong - ScatterTightReadbackIntoPackState and the
server's neutral-pack read were already carrying it. No case needed a fix and no case needed
a named deferral.
DepthStencilReadbackMatrixScenario gets a per-arm block of its own, because the registration
macro cannot carry MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1 and without that pin the
scenario is unfalsifiable here: ES has no core depth/stencil readback, Mesa accepts the
reads anyway, and on llvmpipe every case goes green through a path the device does not have.
Its SeparateDepthAndStencilAttachmentsAreBothReadable SKIPS on all three arms and on monolith
too, naming the driver reason itself - a driver fact, not a transport one.
(b) THE TWO std::abort EMULATIONS, checked rather than assumed. copy-image-shadow-mirror
ALREADY cannot abort: MirrorCopyImageIntoDestinationShadow returns under a transport before
reaching MGPipeUnmigratedEmulation, and has since P5b package i1. get-tex-image-shadow is
retired here, and the reason is that its own premise did not hold: P4a wrote "a split server
holds no shadow to convert", but the level storage this converts IS the server's own, fed by
the staged texture store. When MapMipmapData answers, the conversion reads bytes that exist
on this side. So it converts when the shadow is there and DECLINES BY NAME when it is not
(rule I: MGLOG_E_ONCE + GL_INVALID_OPERATION, destination untouched). std::abort inside a
server answering a client's readback takes the session down for a level it happens not to
hold, and bypasses Session::Fail, which is the funnel the census gate watches. The funnel
keeps its teeth for the other four sites - generate-mipmap-storage,
generate-mipmap-cpu-fallback, generate-mipmap-cpu-filter, texture-remint-pull - which really
do reach into a client address space; fatal_census is unchanged at 79 abort sites because
the abort lives in MGPipeUnmigratedEmulation and that function stays. Stated plainly in the
notes: no case in the armed census reaches this site under a transport (the server answers
GetTextureImage with ReadTextureImageWire, never through the backend's GetTexImage slot), so
the change is defence in depth and has no integration red-once of its own.
(c) THE CASE NOTHING COVERED, and this slice's red-once:
PixelStoreSweepScenario.ReadPixelsIntoAPackBufferHonoursPackStateAndLeavesTheGapsAlone -
glReadPixels into a bound GL_PIXEL_PACK_BUFFER at a non-zero buffer offset with
PACK_ALIGNMENT/ROW_LENGTH/SKIP_ROWS/SKIP_PIXELS all non-default, read back with
glMapBufferRange. It is the only thing that drives EmitTables.cpp's `if (pbo)` branch, the
client servicing the pack PBO itself after the reply lands; the existing sweep case reads
into a client pointer, which is the other half and has unit cover. Every number makes a term
matter: ROW_LENGTH 5 at 4 bpp is 20, which ALIGNMENT 8 rounds up to a 24-byte stride;
SKIP_ROWS 1 and SKIP_PIXELS 2 put the first pixel at image byte 32; the read is issued at
buffer offset 16, so it lands at 48. The whole buffer starts 0xCD and the case asserts both
halves - the rows land where the pack state says, and every other byte is still poison.
Red-once (R-16): dropping just the buffer-offset term from EmitTables.cpp's `start` reds the
three split entries with "readback[at] Which is: '\xCD' (205)" - the destination still poison
because the rows landed 16 bytes early - while the ambient monolith entries pass, since the
driver services its own PBO there. EmitTables.cpp is not part of this commit.
(d) SplitReadbackPackBuffer's comment claimed "The client refuses PACK_BUFFER before
emission". It does not: it KEEPS the PBO and services it after the reply, and a refusal would
have been observable as a GL error. The conclusion (the server must see no pack buffer) was
right and the reason was backwards; rewritten to ID-49's actual split of the work.
Gates: integration-split 225/225 (base 186), integration-spawn 141/141 (base 102),
integration-tcp 144/144, ctest -L unit 2408/2408, integration-magma-split 73/73,
spawn_lane_parity / fatal_census (79 sites, 0 unmarked) / link_ratchet (186, unchanged)
green, G1 .text 0xa52203 with both nm lists byte-identical to ~/w7/p7-before.
This commit is contained in:
@@ -66,8 +66,20 @@
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// read_pixels writes server reply scratch. The client refuses PACK_BUFFER before
|
||||
// emission, so no frontend pack binding can affect this destination.
|
||||
// A readback on the server writes REPLY SCRATCH, so it has no pack buffer - and this
|
||||
// function is how every pack-PBO lookup in this file says so under a transport.
|
||||
//
|
||||
// THE REASON IS NOT "THE CLIENT REFUSES PACK_BUFFER BEFORE EMISSION", which is what this
|
||||
// comment used to claim and is not true of the tree (P3b/P4b espryt D1 slice 4). The client
|
||||
// SERVICES the pack PBO itself, AFTER the reply lands: EmitTables.cpp uploads the reply's
|
||||
// tight rows into the bound buffer with pbo->UploadSubData, one row at a time at the stride
|
||||
// the application's GL_PACK_* state implies, so padding and untouched bytes survive. A
|
||||
// refusal would have been visible as a GL error; what actually happens is an ordinary
|
||||
// buffer upload the client performs on its own side.
|
||||
//
|
||||
// The server's half is ID-49's: read with NEUTRAL pack state into a tight w*h*bpp reply.
|
||||
// Which is exactly why the server must see NO pack buffer here - not because the client
|
||||
// forbade one, but because the client kept it.
|
||||
static const SharedPtr<MG_State::GLState::BufferObject>& SplitReadbackPackBuffer() {
|
||||
static const SharedPtr<MG_State::GLState::BufferObject> none;
|
||||
return none;
|
||||
@@ -14917,11 +14929,43 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (width <= 0 || sliceHeight <= 0 || sliceCount <= 0) {
|
||||
return true;
|
||||
}
|
||||
const void* shadow = textureMipmapObject->MapMipmapData(uploadTarget, level);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a (D-M). glGetTexImage is answered out of the frontend's own level shadow,
|
||||
// converted to the requested format and type. Monolith is unchanged; a split server
|
||||
// holds no shadow to convert, and the readback family as a whole is P3b/P4b's and
|
||||
// P8's rather than this phase's.
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// P4a (D-M) SAID "a split server holds no shadow to convert" AND ABORTED. That is the
|
||||
// premise this row of P3b/P4b re-examined, and it is wrong in the only case that can
|
||||
// get here: the level storage this converts IS the server's own, fed by the staged
|
||||
// texture store, so when MapMipmapData answers the conversion is reading bytes that
|
||||
// exist on this side and the answer is right. There is nothing to migrate - the
|
||||
// emulation was never reaching across the split, it only looked as though it must.
|
||||
//
|
||||
// WHEN IT DOES NOT ANSWER, THE VERB DECLINES BY NAME (rule I) RATHER THAN ABORTING.
|
||||
// std::abort inside a server that is answering a client's readback takes the whole
|
||||
// session down for a texture level it happens not to hold; GL's own answer for "this
|
||||
// read cannot be served" is an error code and untouched destination bytes, and a
|
||||
// client that gets GL_INVALID_OPERATION can carry on. The abort also bypassed
|
||||
// Session::Fail entirely, which is the funnel the census gate watches.
|
||||
//
|
||||
// THE FUNNEL KEEPS ITS TEETH FOR THE OTHER SITES: MGPipeUnmigratedEmulation still
|
||||
// aborts for generate-mipmap-storage, generate-mipmap-cpu-fallback,
|
||||
// generate-mipmap-cpu-filter and texture-remint-pull, which really do reach into a
|
||||
// client address space. Only this call site is retired, and only because its own
|
||||
// premise did not hold.
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
if (shadow == nullptr) {
|
||||
MGLOG_E_ONCE("GetTexImage: no level shadow on this side for target=0x%x level=%d, and under an "
|
||||
"active transport there is no client shadow to fall back to - the read is "
|
||||
"DECLINED and the destination keeps its bytes",
|
||||
static_cast<unsigned>(uploadTarget), level);
|
||||
MGB_CTX->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"DirectGLES", "GetTexImage",
|
||||
"the level has no shadow on the server and there is no client shadow to convert"));
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
MG_Pipe::MGPipeUnmigratedEmulation("get-tex-image-shadow");
|
||||
#endif
|
||||
const auto& pixelPackBufferObject =
|
||||
@@ -14934,7 +14978,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* shadow = textureMipmapObject->MapMipmapData(uploadTarget, level);
|
||||
if (!shadow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user