diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 5225c987..101adccd 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -8988,6 +8988,27 @@ namespace MobileGL::MG_Backend::DirectGLES { auto* srcMipmap = MG_State::GLState::AsMipmapTexture(srcEndpoint.Texture.get()); auto* dstMipmap = MG_State::GLState::AsMipmapTexture(dstEndpoint.Texture.get()); if (!srcMipmap || !dstMipmap) return; +#if MOBILEGL_BUILD_DISAGGREGATED + // P5b package i1's ONE backend edit, and the contract names this site + // (MG_Remote/CONTRACT-P5B.md §2 i1 "the copy-image-shadow-mirror emulation", ruling + // §6.7). Migrating glCopyImageSubData moves the first blocker off the client's + // Fatal{UnmigratedVerb} and onto the Fatal below, on every Espryt copy between two + // textures with CPU shadows - so the ruling is: UNDER A REAL TRANSPORT THE SERVER SKIPS + // THE MIRROR, and the client-side mirror ROADMAP P8 names ("CopyImage 镜像搬到 client") + // stays P8's. + // + // WHAT THE SKIP LOSES IS BOUNDED BY TWO FATALS, which is the whole reason it is allowed + // to be a skip rather than a port: a later glGetTexImage of the destination served from + // the shadow is class C wave 3 (Fatal{UnmigratedVerb, "GetTexImage"}, P9) and a texture + // re-mint that re-uploads the level is Fatal{UnmigratedEmulation, "texture-remint-pull"} + // (Managers.cpp:5634). Neither can silently read the un-mirrored shadow. + // + // BEHIND #if MOBILEGL_BUILD_DISAGGREGATED so the pull build's code does not move (G1), + // and the arm is the TRANSPORT and not the build - build-split runs its unit and + // integration-gpu lanes under MOBILEGL_TRANSPORT=monolith, where this mirror is on an + // ordinary correct path and must still run. + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) return; +#endif #if MOBILEGL_PIPE_PUSH // P4a (D-M). glCopyImageSubData's CPU-shadow mirror copies the source level's shadow // rows into the DESTINATION's shadow so a later readback of the destination sees what diff --git a/MobileGL/MG_Pipe/FieldOwnership.def b/MobileGL/MG_Pipe/FieldOwnership.def index beeca7c4..9cccb3ec 100644 --- a/MobileGL/MG_Pipe/FieldOwnership.def +++ b/MobileGL/MG_Pipe/FieldOwnership.def @@ -105,6 +105,14 @@ /* DirectGLES.cpp:4497, PrepareForDraw, the second unconditional pointer read of every draw. */ \ X(GetProgramForDraw, BARRIER_PULLED, "P8 (Espryt), P7 (Magma)", \ "frontend SharedPtr; the record carries a handle") \ + /* P5b package i1 (CONTRACT-P5B.md §6.9, the one row the contract grants a package). This */ \ + /* was FATAL "reachable only from kDispatch; there is no compute on the reduced path" - and */ \ + /* i1 IS what puts compute on the path: launch_grid (60) now crosses and the backend's */ \ + /* PrepareForCompute pulls this inside it (DirectGLES.cpp:5779, VulkanRenderer.cpp:7327). */ \ + /* It is GetProgramForDraw's twin in every respect, so it takes its class and its retiring */ \ + /* phases. Overturned by: nothing in P5b; P7/P8 retire both rows together. */ \ + X(GetProgramForDispatch, BARRIER_PULLED, "P7 (Magma), P8 (Espryt)", \ + "frontend SharedPtr; the record carries a handle") \ /* The XFB six. XFB itself is off the reduced path, but kDraw's may-read mask carries all */ \ /* six and the draw walk reads them regardless - which is exactly the case a field census */ \ /* taken from "what the scenario does" rather than from the mask would miss. */ \ @@ -132,13 +140,13 @@ X(GetPixelStoreParameters, APPLIER_DERIVED, "-", \ "set_pixel_pack_state; the applier writes m_pixelStore[0] (PipeApply.cpp:1373)") \ \ - /* ---- FATAL: three non-sticky fields, each off the reduced path for a checkable reason --- */ \ + /* ---- FATAL: two non-sticky fields, each off the reduced path for a checkable reason ----- */ \ + /* Three until P5b: GetProgramForDispatch moved up to BARRIER_PULLED when package i1 put */ \ + /* compute on the path (CONTRACT-P5B.md §6.9). */ \ X(GetBoundTransformFeedbackName, FATAL, "-", \ "DEAD: read by no backend since the D21 rekey (PipeInputs.h:232-234)") \ X(GetTransformFeedbackPausedPrimitiveCounter, FATAL, "-", \ "reachable only from class kQuery, which the reduced path never enters") \ - X(GetProgramForDispatch, FATAL, "-", \ - "reachable only from kDispatch; there is no compute on the reduced path") \ \ /* ---- the seven sticky forwards, as FIELD rows ---- */ \ /* They have no storage, so a read of the FIELD is a call of the FORWARD; the field row and */ \ diff --git a/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc b/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc index 8e2e6faa..44cdf75b 100644 --- a/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc +++ b/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc @@ -69,7 +69,7 @@ inline constexpr MGPipeFieldOwnership kMGPipeFieldOwnership[kMGPipeInputFieldCou MGPipeFieldOwnership::kRecordSupplied, // GetPolygonOffsetFactor MGPipeFieldOwnership::kRecordSupplied, // GetPolygonOffsetUnits MGPipeFieldOwnership::kRecordSupplied, // GetPrimitiveRestartIndex - MGPipeFieldOwnership::kFatal, // GetProgramForDispatch + MGPipeFieldOwnership::kBarrierPulled, // GetProgramForDispatch MGPipeFieldOwnership::kBarrierPulled, // GetProgramForDraw MGPipeFieldOwnership::kBarrierPulled, // GetProgramObject MGPipeFieldOwnership::kRecordSupplied, // GetProvokingVertexMode @@ -136,7 +136,7 @@ inline constexpr const char* kMGPipeFieldRetiringPhase[kMGPipeInputFieldCount] = "-", // GetPolygonOffsetFactor "-", // GetPolygonOffsetUnits "-", // GetPrimitiveRestartIndex - "-", // GetProgramForDispatch + "P7 (Magma), P8 (Espryt)", // GetProgramForDispatch "P8 (Espryt), P7 (Magma)", // GetProgramForDraw "P9", // GetProgramObject "-", // GetProvokingVertexMode @@ -298,6 +298,6 @@ inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3; // The class sizes, as constants a test can pin without recounting the table. inline constexpr SizeT kMGPipeRecordSuppliedFieldCount = 32; inline constexpr SizeT kMGPipeApplierDerivedFieldCount = 1; -inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 27; -inline constexpr SizeT kMGPipeFatalFieldCount = 3; +inline constexpr SizeT kMGPipeBarrierPulledFieldCount = 28; +inline constexpr SizeT kMGPipeFatalFieldCount = 2; static_assert(kMGPipeRecordSuppliedFieldCount + kMGPipeApplierDerivedFieldCount + kMGPipeBarrierPulledFieldCount + kMGPipeFatalFieldCount == kMGPipeInputFieldCount, "the four class sizes do not partition the field set");