diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58f66629..c4c813e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2042,6 +2042,13 @@ jobs: "PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw", "PersistentCoherentMapScenario.AWriteAfterAFrameBoundaryReachesTheNextFramesDraw", "StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding", + # P11 A2: the >= 16 MiB arena and its tier assertion (emulated). + "LargeArenaAdoptionScenario.SubDataAfterAnInFlightDrawReachesTheNextDraw", + "LargeArenaAdoptionScenario.RespecifiedVertexArenaKeepsVaoBindings", + "LargeArenaAdoptionScenario.RespecifiedIndexArenaKeepsVaoBinding", + "LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite", + "LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack", + "LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares", ] slp.require_green(json.load(open(sys.argv[2])), sys.argv[3], ["DirectVulkan.Split.Buffers." + name for name in cases]) @@ -2104,6 +2111,12 @@ jobs: "PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw", "PersistentCoherentMapScenario.AWriteAfterAFrameBoundaryReachesTheNextFramesDraw", "StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding", + "LargeArenaAdoptionScenario.SubDataAfterAnInFlightDrawReachesTheNextDraw", + "LargeArenaAdoptionScenario.RespecifiedVertexArenaKeepsVaoBindings", + "LargeArenaAdoptionScenario.RespecifiedIndexArenaKeepsVaoBinding", + "LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite", + "LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack", + "LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares", ] required = [sys.argv[4] + name for name in cases] if sys.argv[5] == "tcp": diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index ab4f3bdf..6a4ffa35 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -1495,14 +1495,25 @@ gtest_discover_tests(MobileGLIntegrationTest # No log path: none of these three cases reads one, and giving them one would only add a file for # four processes to truncate. The fourth case skips in both lanes for exactly that reason, saying # so. +# +# P11 A2: both lanes pin monolith, so both DECLARE the adopted arm (MGITEST_PERSISTENT_MAP_ARM) and +# LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares asserts the 24 MiB arena really +# adopted on the handle arm (Ops_H_MapPersistentTracked) and on the legacy one. Red once with +# MOBILEGL_DISABLE_LARGE_BUFFER_ADOPTION=1 in the job environment (CONTRACT-P11 §2). mgl_itest_join_environment(MGL_ITEST_GLES_ARENA_SUBSYSTEM_ON_ENVIRONMENT - "MOBILEGL_TRANSPORT=monolith" + "MOBILEGL_TRANSPORT=monolith" "MGITEST_PERSISTENT_MAP_ARM=adopted" "MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_PUSH=0x1fff" ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) mgl_itest_join_environment(MGL_ITEST_GLES_ARENA_SUBSYSTEM_OFF_ENVIRONMENT - "MOBILEGL_TRANSPORT=monolith" + "MOBILEGL_TRANSPORT=monolith" "MGITEST_PERSISTENT_MAP_ARM=adopted" "MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_PUSH=0x7f" ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) +# ... and Magma's monolith half: the legacy AcquirePersistentMap (the wire resource table is +# registered only off monolith, VkBufferManager.cpp RegisterWireResourceOps). +mgl_itest_join_environment(MGL_ITEST_VULKAN_ARENA_ARM_ENVIRONMENT + "MOBILEGL_TRANSPORT=monolith" "MGITEST_PERSISTENT_MAP_ARM=adopted" + "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ITEST_REQUIRE_GPU=1" + ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) gtest_discover_tests(MobileGLIntegrationTest TEST_PREFIX "DirectGLES.ResourceSubsystemOn." @@ -1522,6 +1533,15 @@ gtest_discover_tests(MobileGLIntegrationTest TIMEOUT ${MGL_ITEST_TIMEOUT} ENVIRONMENT "${MGL_ITEST_GLES_ARENA_SUBSYSTEM_OFF_ENVIRONMENT}" ) +gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.ArenaArm." + TEST_FILTER "LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares" + DISCOVERY_TIMEOUT 30 + PROPERTIES + LABELS "integration-gpu\;integration-monolith-control" + TIMEOUT ${MGL_ITEST_TIMEOUT} + ENVIRONMENT "${MGL_ITEST_VULKAN_ARENA_ARM_ENVIRONMENT}" +) # --- G12: the P4a subsystem A/B, and its dependency refusal --------------------------- # @@ -2407,6 +2427,30 @@ if (MOBILEGL_BUILD_DISAGGREGATED) mgl_itest_register_split_arms("ClearThenReadPixelsScenario.*" "") mgl_itest_register_split_arms("TriangleScenario.*" "") mgl_itest_register_split_arms("PersistentCoherentMapScenario.*" "") + # P11 A2 (MG_Remote/CONTRACT-P11.md §2): the >= 16 MiB arena on all three arms. Its tier case + # is declared emulated per entry by SplitLogPaths (T2: every acquisition declines); the mpr and + # adopt-tier-refusal cases skip here for want of their lane markers. The Magma arms are the + # gated `Arena.` block beside `Pbo.` / `Fence.` below, plus the Buffers lane. + mgl_itest_register_split_arms("LargeArenaAdoptionScenario.*" "") + # P11 A1 (CONTRACT-P11 §1): the tcp arm with MOBILEGL_IPC_ADOPT_TIER=0. A stream refuses the + # tier by name at the client's handshake and runs T2, so the whole scenario is green here and + # AStreamSessionRefusesTheAdoptTierOnceByName reads exactly one refusal line. TCP ONLY by + # construction: over shared segments (split, spawn) the same knob dies at the handshake, which + # is the unit gate RemoteClientControls.AdoptTier*OverSharedSegments*. The `.AdoptTier0.` tail is + # what keeps these names out of spawn_lane_parity.py's case set (see MAGMA_TCP_ONLY there). + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectGLES.Tcp.AdoptTier0." + TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" + TEST_FILTER "LargeArenaAdoptionScenario.*" + DISCOVERY_TIMEOUT 30 + PROPERTIES + LABELS "integration-gpu\;integration-tcp" + TIMEOUT ${MGL_ITEST_TIMEOUT} + ENVIRONMENT "${MGL_ITEST_GLES_TCP_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_TIER_REFUSAL=T0" + FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp + ) + list(APPEND MGL_SPLIT_ARM_TEST_LISTS "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}") + math(EXPR MGL_SPLIT_ARM_INDEX "${MGL_SPLIT_ARM_INDEX} + 1") # ---- P3b/P4b espryt D1 slice 1: THE ORPHANED XFB CAPTURE TARGET ------------------------- # @@ -3697,6 +3741,16 @@ endif() # Fatal{ReplyTooLarge, "ReadPixels 256x512 0x1908/0x1406 2097152 > 2097136"}. One entry per case # for a private log each - two processes append to it on the spawn arm. if (MOBILEGL_BUILD_DISAGGREGATED) + # P11: LargeArenaAdoptionScenario's cases that run on every Magma arm - the five behavioural + # cases and the tier assertion (the mpr and adopt-tier-refusal cases need lane markers of their + # own). The `Arena.` block below and the Buffers lane both walk it. + set(MGL_ITEST_ARENA_CASES + SubDataAfterAnInFlightDrawReachesTheNextDraw + RespecifiedVertexArenaKeepsVaoBindings + RespecifiedIndexArenaKeepsVaoBinding + ReadbackSeesTheLatestCpuWrite + GpuWriteIntoTheArenaIsReadBack + TheArenaLandsInTheTierItsLaneDeclares) set(MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1" "MOBILEGL_IPC_RUN_AHEAD=1" @@ -3753,6 +3807,34 @@ if (MOBILEGL_BUILD_DISAGGREGATED) ${mglItestArmProperties} ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p10-fence${mglItestArmLogTag}-${fenceCase}.log") endforeach() + # P11 A2 (MG_Remote/CONTRACT-P11.md §2): the >= 16 MiB arena on Magma's three arms, in the + # same flavour. Declared emulated: every split acquisition declines (the wire resource + # table's MapPersistent returns null, VkBufferManager.cpp), so the tier case asserts T2 + # on every arm; DirectVulkan.ArenaArm. is its monolith (adopted) half. + foreach(arenaCase IN LISTS MGL_ITEST_ARENA_CASES) + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.${mglItestArm}.Arena." + TEST_FILTER "LargeArenaAdoptionScenario.${arenaCase}" + DISCOVERY_TIMEOUT 30 + PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT} + ${mglItestArmProperties} + ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MGITEST_PERSISTENT_MAP_ARM=emulated\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena${mglItestArmLogTag}-${arenaCase}.log") + endforeach() + # P11 A1 (CONTRACT-P11 §1): the tcp arm with MOBILEGL_IPC_ADOPT_TIER=0 - refused by name at + # the client's handshake, T2, the scenario green. TCP ONLY by construction (over shared + # segments the knob dies at the handshake); spawn_lane_parity.py names the tail in + # MAGMA_TCP_ONLY. + if(mglItestArm STREQUAL "Tcp") + foreach(arenaCase IN LISTS MGL_ITEST_ARENA_CASES ITEMS AStreamSessionRefusesTheAdoptTierOnceByName) + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.Tcp.AdoptTier0." + TEST_FILTER "LargeArenaAdoptionScenario.${arenaCase}" + DISCOVERY_TIMEOUT 30 + PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT} + ${mglItestArmProperties} + ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_TIER_REFUSAL=T0\;MGITEST_PERSISTENT_MAP_ARM=emulated\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-adopt-tier0-tcp-${arenaCase}.log") + endforeach() + endif() endforeach() endif() @@ -3915,7 +3997,15 @@ if (MOBILEGL_BUILD_DISAGGREGATED) CrossFrameBufferScenario.IndexOrphanAndReupload PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw PersistentCoherentMapScenario.AWriteAfterAFrameBoundaryReachesTheNextFramesDraw - StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding) + StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding + # P11 A2: the >= 16 MiB arena, the flavour's MGITEST_PERSISTENT_MAP_ARM=emulated + # asserted by its tier case (MG_Remote/CONTRACT-P11.md §2). + LargeArenaAdoptionScenario.SubDataAfterAnInFlightDrawReachesTheNextDraw + LargeArenaAdoptionScenario.RespecifiedVertexArenaKeepsVaoBindings + LargeArenaAdoptionScenario.RespecifiedIndexArenaKeepsVaoBinding + LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite + LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack + LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares) gtest_discover_tests(MobileGLIntegrationTest TEST_PREFIX "DirectVulkan.${mglItestArm}.Buffers." TEST_FILTER "${bufferCase}" diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in index 19d53277..2d61c1ec 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in +++ b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in @@ -25,6 +25,9 @@ foreach(listName IN ITEMS @MGL_SPLIT_ARM_TEST_LISTS@) elseif(entry MATCHES "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares$") set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MGITEST_PMAP_LANE=1;MGITEST_PERSISTENT_MAP_ARM=emulated;MOBILEGL_PIPE_STATS=1;MOBILEGL_PIPE_STATS_PERIOD=1") + elseif(entry MATCHES "LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares$") + # P11 A2: every split arm runs T2, so the arena is declared emulated. + set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MGITEST_PERSISTENT_MAP_ARM=emulated") elseif(entry MATCHES "CtWireScenario\\.") set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_TEST_SERVER_COUNTERS=1") endif() diff --git a/MobileGL/MG_IntegrationTest/Scenarios/LargeArenaAdoptionScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/LargeArenaAdoptionScenario.cpp index d736591e..19692a33 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/LargeArenaAdoptionScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/LargeArenaAdoptionScenario.cpp @@ -49,8 +49,10 @@ #include #include "../Harness/HeadlessGL.h" +#include "../Harness/PersistentMapPeek.h" #include "../Harness/PipeStatsWindow.h" #include "../Harness/ScenarioFixture.h" +#include "../Harness/SplitLane.h" #ifdef GLAPI #undef GLAPI @@ -94,6 +96,9 @@ void main() { word = 0xC0FFEEu; } // Set by the MapPersistentRoundtrips. ctest entry and by nothing else; a harness marker, // never read by the library. constexpr const char* kLaneMarker = "MGITEST_MPR_LANE"; + // P11 A1: set (to the tier the lane names, "T0") only by the `.AdoptTier0.` tcp entries, + // which also set MOBILEGL_IPC_ADOPT_TIER=0; a harness marker, never read by the library. + constexpr const char* kAdoptTierRefusalMarker = "MGITEST_ADOPT_TIER_REFUSAL"; // Draws issued against the arena inside the counted window. One definition, many draws: // "one per definition" (1) and "one per draw" (kDrawsInTheWindow) have to be different // numbers or the assertion cannot tell them apart. @@ -260,6 +265,27 @@ void main() { word = 0xC0FFEEu; } return px; } + // P11 A2: the arena's tier as the frontend object records it, against the lane's + // declaration. Adopted = the resource owner minted a persistent coherent mapping and + // the CPU shadow was released; emulated = the owner declined (T2) and the shadow is + // the truth. Pixels cannot tell them apart - every case above is green on both. + void ExpectDeclaredArm(const std::string& declared, const char* when) { + bool adopted = false; + ASSERT_TRUE(PeekBufferIsAdoptedPersistentMap(m_arena, &adopted)) + << "no frontend BufferObject behind the arena " << when; + const std::string landed = adopted ? "adopted" : "emulated"; + RecordProperty(std::string("arena_arm_") + (m_armPeeks++ == 0 ? "defined" : "used"), + landed.c_str()); + EXPECT_EQ(declared, landed) + << "the lane declared the " << declared << " arm and the " << (kArenaBytes >> 20) + << " MiB arena is " << landed << " " << when + << ". Monolith lanes adopt on both backends (Espryt's handle arm mints through " + "Ops_H_MapPersistentTracked, Magma's legacy AcquirePersistentMap); split lanes " + "run T2, where every acquisition declines."; + } + + int m_armPeeks = 0; + unsigned int m_program = 0; unsigned int m_compute = 0; unsigned int m_vao = 0; @@ -505,4 +531,92 @@ void main() { word = 0xC0FFEEu; } << window.line; } + // P11 A2 (MG_Remote/CONTRACT-P11.md §2): WHICH TIER THE ARENA REALLY LANDED IN. The cases above + // are green whether the arena adopted or stayed emulated, so the lane declares the arm + // (MGITEST_PERSISTENT_MAP_ARM) and this case asserts it, the way + // PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares does for a sub-16-MiB map. + // Asked twice: after the NULL-data definition (TryAdoptLargeStorage's door) and after a frame + // of use that ends in an SSBO binding and a dispatch (the storage-binding door, + // EnsureGpuResidentStorage, which Magma's server walk opens). Lanes that declare nothing - the + // ambient entries, which run under whatever transport the job exports - skip. + TEST_F(LargeArenaAdoptionScenario, TheArenaLandsInTheTierItsLaneDeclares) { + if (!Ready() || IsSkipped()) return; + const std::string declared = SplitLane::DeclaredPersistentMapArm(); + if (declared.empty()) { + GTEST_SKIP() << "this lane declares no arm: MGITEST_PERSISTENT_MAP_ARM is set only by the " + "lanes that pin their transport (monolith: adopted; the split arms: " + "emulated)"; + } + if (!PersistentMapPeekAvailable()) { + GTEST_SKIP() << "this lane declares the " << declared << " arm but Harness/PersistentMapPeek " + "cannot look in this build (on Android this module links the shipping " + "library, built -fvisibility=hidden). 'Could not look' is not 'it was " + << declared << "'."; + } + ExpectDeclaredArm(declared, "right after its NULL-data definition"); + if (HasFatalFailure()) return; + + UploadQuad(1.f, 0.f, 0.f); + DrawQuad(); + EXPECT_GT(CenterPixel()[0], 200) << "the draw from the arena never landed"; + Gl().EndFrame(); + + GLint maxComputeStorageBlocks = 0; + glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeStorageBlocks); + if (maxComputeStorageBlocks >= 1) { + const GLuint compute = CompileStage(GL_COMPUTE_SHADER, kMarkerComputeSource); + ASSERT_NE(compute, 0u) << m_buildLog; + m_compute = glCreateProgram(); + glAttachShader(m_compute, compute); + glLinkProgram(m_compute); + glDeleteShader(compute); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, m_arena, 0, sizeof(unsigned int)); + glUseProgram(m_compute); + glDispatchCompute(1, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT); + unsigned int marker = 0; + glBindBuffer(GL_ARRAY_BUFFER, m_arena); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(marker), &marker); + EXPECT_EQ(marker, 0xC0FFEEu) << "the dispatch through the arena's SSBO binding never landed"; + } else { + RecordProperty("arena_arm_used", "not asked: no compute shader storage blocks"); + } + EXPECT_EQ(FirstGLError(), 0u); + ExpectDeclaredArm(declared, "after a frame of draws, an SSBO binding and a dispatch"); + } + + // P11 A1 (MG_Remote/CONTRACT-P11.md §1): THE TCP LANE WITH MOBILEGL_IPC_ADOPT_TIER=0. A stream + // data plane has no memory the two sides share, so the client refuses the knob by name at its + // handshake - exactly one `Refuse{AdoptTierOnStream, "T0"}` line in its log - and the session + // runs T2: the other cases in the same `.AdoptTier0.` lane are green and the arena is emulated. + // Before A1 this lane ran T2 silently. Everywhere else it skips. + TEST_F(LargeArenaAdoptionScenario, AStreamSessionRefusesTheAdoptTierOnceByName) { + if (!Ready() || IsSkipped()) return; + const char* tier = std::getenv(kAdoptTierRefusalMarker); + if (tier == nullptr || tier[0] == '\0') { + GTEST_SKIP() << "runs only in the `.AdoptTier0.` tcp entries, which set " + "MOBILEGL_IPC_ADOPT_TIER=0 and " << kAdoptTierRefusalMarker << "=T0"; + } + if (PipeStatsWindow::LibraryLogPath().empty()) { + GTEST_SKIP() << "the lane configured no MOBILEGL_LOG_FILE_PATH, and the client's log is " + "where the refusal is written"; + } + // A frame of the arena's work first: a refusal is only worth reading from a session that + // then ran - and an armed split lane requires the case to move the record ordinal anyway. + UploadQuad(0.f, 1.f, 0.f); + DrawQuad(); + EXPECT_GT(CenterPixel()[1], 200) << "the session that refused the tier did not render"; + EXPECT_EQ(FirstGLError(), 0u); + Gl().EndFrame(); + + const std::string log = PipeStatsWindow::ReadWholeFile(PipeStatsWindow::LibraryLogPath()); + const std::string line = std::string("Refuse{AdoptTierOnStream, \"") + tier + "\"}"; + std::size_t count = 0; + for (auto at = log.find(line); at != std::string::npos; at = log.find(line, at + 1)) ++count; + RecordProperty("adopt_tier_refusals", static_cast(count)); + EXPECT_EQ(count, 1u) << "the client names MOBILEGL_IPC_ADOPT_TIER on a stream exactly once, at " + "its handshake (" << line << "), in " << PipeStatsWindow::LibraryLogPath(); + EXPECT_EQ(log.find("Fatal{UnimplementedAdoptTier"), std::string::npos) << log; + } + } // namespace MGITest diff --git a/scripts/ci/spawn_lane_parity.py b/scripts/ci/spawn_lane_parity.py index 22447aaa..7bff0bf8 100644 --- a/scripts/ci/spawn_lane_parity.py +++ b/scripts/ci/spawn_lane_parity.py @@ -122,6 +122,16 @@ MAGMA_SERVER_ENV_KNOB_NO_TCP = (".ShaderMip1.", ".ShaderMip2.", ".DepthMip.", f".PrimGenReroute.PrimitivesGeneratedNoXfbScenario.{case}" for case in PRIMGEN_REROUTE_CASES) +# P11 A1: THE MIRROR SHAPE - entries that exist on tcp ALONE, by construction. `.AdoptTier0.` runs +# LargeArenaAdoptionScenario with MOBILEGL_IPC_ADOPT_TIER=0 on the CLIENT: a stream data plane +# refuses that tier by name and runs T2 (the scenario is green there), while over shared segments - +# the split and spawn arms - the same knob dies at the handshake by design (CONTRACT-P11 §1), so a +# split or spawn copy could only ever be a death. Each tail must match at least one tcp entry, or +# the exception names nothing. (The DirectGLES `.AdoptTier0.` twins need no entry: the Espryt +# CASE pattern does not parse a middle segment, so they never enter that comparison.) +MAGMA_TCP_ONLY = (".AdoptTier0.",) + + def lane_names(build_dir, label): """Every ctest entry name under an ANCHORED label (`ctest -L` is a regex, not a name).""" out = subprocess.run(["ctest", "-N", "-L", "^" + label + "$"], cwd=build_dir, @@ -169,7 +179,7 @@ def tcp_lock_check(build_dir): return False -def compare_arms(build_dir, tier, labels, inproc_only=(), no_tcp=()): +def compare_arms(build_dir, tier, labels, inproc_only=(), no_tcp=(), tcp_only=()): """The three arms of one tier must name the same set after the arm segment comes off. `inproc_only` drops a key from the comparison for EVERY non-split arm; `no_tcp` drops it for @@ -195,6 +205,20 @@ def compare_arms(build_dir, tier, labels, inproc_only=(), no_tcp=()): return False failed = False reference = "split" + if tcp_only and "tcp" in sets: + for tail in tcp_only: + hits = sorted(k for k in sets["tcp"] if tail in k) + if not hits: + print(f"::error::{tier}: the tcp-only tail {tail!r} matches no tcp entry - an exception " + f"about nothing.", file=sys.stderr) + return True + elsewhere = sorted(k for arm, keys in sets.items() if arm != "tcp" for k in keys if tail in k) + if elsewhere: + print(f"::error::{tier}: the tcp-only tail {tail!r} also names non-tcp entries " + f"{elsewhere}.", file=sys.stderr) + return True + print(f"{tier}: {len(hits)} tcp-only entrie(s) excluded from the comparison ({tail})") + sets["tcp"] = {k for k in sets["tcp"] if not any(only in k for only in tcp_only)} comparable = {k for k in sets[reference] if not any(only in k for only in inproc_only)} if len(comparable) != len(sets[reference]): @@ -294,7 +318,8 @@ def main(): "spawn": "integration-magma-spawn", "tcp": "integration-magma-tcp"}, inproc_only=MAGMA_INPROC_ONLY, - no_tcp=MAGMA_SERVER_ENV_KNOB_NO_TCP) + no_tcp=MAGMA_SERVER_ENV_KNOB_NO_TCP, + tcp_only=MAGMA_TCP_ONLY) failed |= compare_arms(args.build_dir, "magma informational tier", {"split": "integration-magma-all-split", "spawn": "integration-magma-all-spawn",