diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 18fc0481..6c9c6b0e 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -361,46 +361,85 @@ if (MOBILEGL_PIPE_PUSH) list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_PUSH_BUILD=1") endif() -file(GLOB MGL_ITEST_ESPRYT_SLOT_TABLES CONFIGURE_DEPENDS - "${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectGLES/SlotTables.h") -if (MGL_ITEST_ESPRYT_SLOT_TABLES) - message(STATUS "Integration tests: DirectGLES is keyed on {slot, gen} (SlotTables.h present)") - list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectGLES=1") -else() - message(STATUS "Integration tests: DirectGLES has no SlotTables.h - HandleRecycle.Handles will SKIP on it") -endif() - -file(GLOB MGL_ITEST_TRACKER_SOURCE CONFIGURE_DEPENDS - "${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe/Tracker.cpp") -if (MGL_ITEST_TRACKER_SOURCE) - message(STATUS "Integration tests: the MGPipe tracker is present, so CSOs are minted") - list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_TRACKER_PRESENT=1") -else() - message(STATUS "Integration tests: no MG_Impl/Pipe/Tracker.cpp - CsoContentAddressing will SKIP") -endif() - -set(MGL_ITEST_MAGMA_VERTEX_INPUT - "${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp") -if (EXISTS "${MGL_ITEST_MAGMA_VERTEX_INPUT}") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${MGL_ITEST_MAGMA_VERTEX_INPUT}") - file(STRINGS "${MGL_ITEST_MAGMA_VERTEX_INPUT}" MGL_ITEST_MAGMA_REKEY_HITS - REGEX "kMGPipeSubsystemMagmaVertexInput") - file(STRINGS "${MGL_ITEST_MAGMA_VERTEX_INPUT}" MGL_ITEST_MAGMA_ABA_HITS - REGEX "PipeHandleAbaControl") - if (MGL_ITEST_MAGMA_REKEY_HITS) - message(STATUS "Integration tests: DirectVulkan's vertex input is keyed on {slot, gen}") - list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectVulkan=1") +# THE THREE MARKERS BELOW ANSWER A QUESTION ABOUT THE SOURCE TREE, so each is only a true +# statement about THIS LIBRARY while this build compiles the arm the source implements - and all +# three arms are `#if MOBILEGL_PIPE_PUSH`. A pull build has no {slot, gen} key (the slot tables +# and the re-keyed memos are push-only) and no Features.PipeHandleAbaControl at all (Config.h +# declares the field inside `#if MOBILEGL_PIPE_PUSH` and ConfigLoader parses it in the same arm). +# A source-only probe would therefore arm the PULL build's lanes the moment packages C and D +# land: the AbaControl lane would go hard red on a gate G2 requires green (the guards it means to +# defeat are still in force, so the scenario's "expect the stale pixels" assertion fails), and the +# Handles lane would report green against a library that contains no re-key at all - the +# "test that cannot fail" this scenario exists to avoid. +# +# So the whole block sits under the same `if (MOBILEGL_PIPE_PUSH)` as MGITEST_PIPE_PUSH_BUILD, and +# HandleRecycleScenario re-checks that marker before either arm asserts, so a hand-forced +# environment cannot arm an arm this build does not have either. +if (MOBILEGL_PIPE_PUSH) + file(GLOB MGL_ITEST_ESPRYT_SLOT_TABLES CONFIGURE_DEPENDS + "${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectGLES/SlotTables.h") + if (MGL_ITEST_ESPRYT_SLOT_TABLES) + message(STATUS "Integration tests: DirectGLES is keyed on {slot, gen} (SlotTables.h present)") + list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectGLES=1") else() - message(STATUS "Integration tests: DirectVulkan's vertex input is not re-keyed yet - " - "HandleRecycle.Handles will SKIP on it") + message(STATUS "Integration tests: DirectGLES has no SlotTables.h - HandleRecycle.Handles will SKIP on it") endif() - if (MGL_ITEST_MAGMA_ABA_HITS) - message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL has a consumer") - list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_ABA_IMPLEMENTED=1") + + # The CSO counters' EMITTER, probed by content rather than by a filename. The tracker package + # owns its own file layout and may implement the tracker and the cache header-only - today it + # does (MG_Impl/Pipe/{Tracker,CsoCache}.h, no Tracker.cpp) - so a glob for `Tracker.cpp` is a + # probe for a file nobody promised to create, and it would answer "no" forever AFTER the + # package landed, leaving the CSO control skipping with a reason that had become false. What + # the control actually needs is something that emits the two counters it reads, so that is + # what is looked for: any client-side pipe source naming RenderStateCsoMints / Binds. The glob + # is CONFIGURE_DEPENDS (a new file re-runs it) and every file it finds is added to + # CMAKE_CONFIGURE_DEPENDS (an edit to one re-runs it), so neither half can go stale. + file(GLOB MGL_ITEST_PIPE_CLIENT_SOURCES CONFIGURE_DEPENDS + "${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe/*.h" + "${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe/*.cpp") + set(MGL_ITEST_CSO_EMITTER "") + foreach(mglItestPipeSource IN LISTS MGL_ITEST_PIPE_CLIENT_SOURCES) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mglItestPipeSource}") + file(STRINGS "${mglItestPipeSource}" MGL_ITEST_CSO_HITS REGEX "RenderStateCso(Mints|Binds)") + if (MGL_ITEST_CSO_HITS AND NOT MGL_ITEST_CSO_EMITTER) + set(MGL_ITEST_CSO_EMITTER "${mglItestPipeSource}") + endif() + endforeach() + if (MGL_ITEST_CSO_EMITTER) + message(STATUS "Integration tests: the CSO counters have an emitter (${MGL_ITEST_CSO_EMITTER})") + list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_TRACKER_PRESENT=1") else() - message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL has no consumer - " - "HandleRecycle.AbaControl will SKIP") + message(STATUS "Integration tests: no MG_Impl/Pipe source emits RenderStateCsoMints/Binds - " + "CsoContentAddressing will SKIP") endif() + + set(MGL_ITEST_MAGMA_VERTEX_INPUT + "${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp") + if (EXISTS "${MGL_ITEST_MAGMA_VERTEX_INPUT}") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${MGL_ITEST_MAGMA_VERTEX_INPUT}") + file(STRINGS "${MGL_ITEST_MAGMA_VERTEX_INPUT}" MGL_ITEST_MAGMA_REKEY_HITS + REGEX "kMGPipeSubsystemMagmaVertexInput") + file(STRINGS "${MGL_ITEST_MAGMA_VERTEX_INPUT}" MGL_ITEST_MAGMA_ABA_HITS + REGEX "PipeHandleAbaControl") + if (MGL_ITEST_MAGMA_REKEY_HITS) + message(STATUS "Integration tests: DirectVulkan's vertex input is keyed on {slot, gen}") + list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectVulkan=1") + else() + message(STATUS "Integration tests: DirectVulkan's vertex input is not re-keyed yet - " + "HandleRecycle.Handles will SKIP on it") + endif() + if (MGL_ITEST_MAGMA_ABA_HITS) + message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL has a consumer") + list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_ABA_IMPLEMENTED=1") + else() + message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL has no consumer - " + "HandleRecycle.AbaControl will SKIP") + endif() + endif() +else() + message(STATUS "Integration tests: pull build - HandleRecycle.{Handles,AbaControl} and " + "CsoContentAddressing stay registered (G2) and SKIP: every arm they assert is " + "compiled only under MOBILEGL_PIPE_PUSH") endif() mgl_itest_join_environment(MGL_ITEST_GLES_ENVIRONMENT @@ -770,11 +809,27 @@ gtest_discover_tests(MobileGLIntegrationTest # (VertexInputStateFactory::ComputeHash's key and LookupVaoDrawMemo's lifetimeId compare); it # steers nothing on DirectGLES, and a lane that configured it there would be a permanent skip # claiming to be a control. +# +# The two PUSH-ONLY knobs of those arms are set only in a push build, and the lane NAMES are +# unaffected by that (an ENVIRONMENT property is not part of a test's name, so G2 still sees the +# same list in both builds). MOBILEGL_PIPE_LEGACY_MEMOS=0 says "never enter the legacy arm"; in a +# pull build the legacy arm is the ONLY arm and every Track-H subsystem bit is clear, which is +# precisely D14's startup Fatal{PipeLegacyMemosDisabled} condition - so a lane that set it there +# would abort the process before the scenario could report its skip. MOBILEGL_PIPE_HANDLE_ABA_CONTROL +# has no field to parse into in a pull build at all (Config.h declares it under #if MOBILEGL_PIPE_PUSH). +if (MOBILEGL_PIPE_PUSH) + set(MGL_ITEST_HANDLES_ARM_KNOBS "MOBILEGL_PIPE_LEGACY_MEMOS=0") + set(MGL_ITEST_ABA_ARM_KNOBS "MOBILEGL_PIPE_HANDLE_ABA_CONTROL=1") +else() + set(MGL_ITEST_HANDLES_ARM_KNOBS "") + set(MGL_ITEST_ABA_ARM_KNOBS "") +endif() + mgl_itest_join_environment(MGL_ITEST_GLES_HANDLE_HANDLES_ENVIRONMENT - "MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_HANDLE_ARM=handles" "MOBILEGL_PIPE_LEGACY_MEMOS=0" + "MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_HANDLE_ARM=handles" ${MGL_ITEST_HANDLES_ARM_KNOBS} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT - "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=handles" "MOBILEGL_PIPE_LEGACY_MEMOS=0" + "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=handles" ${MGL_ITEST_HANDLES_ARM_KNOBS} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) mgl_itest_join_environment(MGL_ITEST_GLES_HANDLE_LEGACY_ENVIRONMENT "MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_HANDLE_ARM=legacy" "MOBILEGL_PIPE_PUSH=0" @@ -784,7 +839,7 @@ mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_LEGACY_ENVIRONMENT ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) mgl_itest_join_environment(MGL_ITEST_VULKAN_HANDLE_ABA_ENVIRONMENT "MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=aba" "MOBILEGL_PIPE_PUSH=0" - "MOBILEGL_PIPE_HANDLE_ABA_CONTROL=1" + ${MGL_ITEST_ABA_ARM_KNOBS} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) gtest_discover_tests(MobileGLIntegrationTest diff --git a/MobileGL/MG_IntegrationTest/Scenarios/CsoContentAddressingScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/CsoContentAddressingScenario.cpp index 47d6538a..b13f09a7 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/CsoContentAddressingScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/CsoContentAddressingScenario.cpp @@ -53,11 +53,15 @@ // file is written against the P2 contract commit, before that package lands. Until the tracker // exists there is no CSO to mint, csom is structurally 0 and an assertion about its ratio to csob // would be a statement about nothing. The build answers the question rather than a hand-maintained -// list: MG_IntegrationTest/CMakeLists.txt looks for MG_Impl/Pipe/Tracker.cpp and passes the answer -// in as MGITEST_PIPE_TRACKER_PRESENT, with a CONFIGURE_DEPENDS on that directory so the answer -// cannot go stale. When the tracker lands the arms arm themselves; until then the entries are -// registered, visible and SKIPPED with the reason - never absent, and never green for having -// asserted nothing. +// list: MG_IntegrationTest/CMakeLists.txt greps every source under MG_Impl/Pipe/ for the two +// counters' names and passes the answer in as MGITEST_PIPE_TRACKER_PRESENT, with a +// CONFIGURE_DEPENDS on that directory and on each file it finds so the answer cannot go stale. +// It is a CONTENT probe, not a filename probe, precisely so that the owning package keeps control +// of its own file layout - it implements the tracker and the cache header-only today, and a glob +// for `Tracker.cpp` would have kept this control skipping forever after that package landed, with +// a reason that had become false. When an emitter lands the arms arm themselves; until then the +// entries are registered, visible and SKIPPED with the reason - never absent, and never green for +// having asserted nothing. #include #include @@ -213,10 +217,12 @@ void main() { oColor = vec4(0.0, 1.0, 0.0, 1.0); } return; } if (!BuildMarkerIsSet("MGITEST_PIPE_TRACKER_PRESENT")) { - GTEST_SKIP() << "the CSO counters have no emitter in this build: MG_Impl/Pipe/Tracker.cpp " - "does not exist, so nothing mints or binds a render-state CSO and " - "csom / csob are structurally zero. P2 package B owns the tracker; this " - "entry arms itself when it lands."; + GTEST_SKIP() << "the CSO counters have no emitter in this build: no source under " + "MobileGL/MG_Impl/Pipe/ names RenderStateCsoMints or " + "RenderStateCsoBinds, so nothing mints or binds a render-state CSO " + "and csom / csob are structurally zero. P2 package B owns the tracker " + "and the CSO cache; this entry arms itself when they land, whatever " + "files that package chooses to put them in."; return; } if (LibraryLogPath().empty()) { diff --git a/MobileGL/MG_IntegrationTest/Scenarios/HandleRecycleScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/HandleRecycleScenario.cpp index 79268293..9ef37e27 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/HandleRecycleScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/HandleRecycleScenario.cpp @@ -69,6 +69,14 @@ // answer in as MGITEST_HANDLE_REKEY_ / MGITEST_HANDLE_ABA_IMPLEMENTED, with a // CMAKE_CONFIGURE_DEPENDS on those files so the answer cannot go stale. When C and D land, the // arms arm themselves. +// +// Those two markers are a statement about the SOURCE TREE, and they are set only in a push build, +// because that is the only build in which the thing they name is compiled: the {slot, gen} re-key +// and Features.PipeHandleAbaControl are both `#if MOBILEGL_PIPE_PUSH`. In a pull build the two +// push arms therefore skip on MGITEST_PIPE_PUSH_BUILD before they ever look at a per-arm marker - +// otherwise, once C and D landed, the pull build would run AbaControl against guards that are +// still in force (a hard red on `ctest -L integration-gpu`, which G2 requires green in BOTH +// builds) and Handles against a library with no re-key in it (a green that asserts nothing). #include #include @@ -116,6 +124,17 @@ namespace MGITest { return Arm::Legacy; } + // Whether the lane named an arm this file knows. A value that is set but unrecognised is a + // FAILURE (SetUp below), never a quiet fall-through to Legacy: a typo in a lane's + // MGITEST_HANDLE_ARM would otherwise downgrade that lane's Handles or AbaControl assertion + // to the Legacy one, which passes - a lane reporting green for an arm it never ran. Same + // shape as CsoContentAddressingScenario's FAIL() on an unknown MGITEST_CSO_LANE. + bool ArmNameIsRecognised() { + const char* name = std::getenv(kArmMarker); + return name == nullptr || std::strcmp(name, "handles") == 0 || + std::strcmp(name, "legacy") == 0 || std::strcmp(name, "aba") == 0; + } + bool RunningInAHandleRecycleLane() { return std::getenv(kArmMarker) != nullptr; } const char* ArmName(Arm arm) { @@ -222,6 +241,13 @@ void main() { oColor = texture(uTex, vUv); } void SetUp() override { ScenarioTest::SetUp(); if (!Ready()) return; + if (!ArmNameIsRecognised()) { + const char* raw = std::getenv(kArmMarker); + FAIL() << "unknown " << kArmMarker << " value '" << (raw != nullptr ? raw : "") + << "': the arms are handles / legacy / aba. Reading an unrecognised name " + "as Legacy would make this lane assert the pre-re-key guards while " + "claiming to test something else, and it would pass."; + } m_arm = CurrentArm(); std::string error; m_colorProgram = CompileProgram(kColorVS, kColorFS, &error); @@ -249,15 +275,37 @@ void main() { oColor = texture(uTex, vUv); } "The ambient entries configure none of that, so there is nothing here to " "assert."; } + // Both push arms are compiled only under MOBILEGL_PIPE_PUSH, so in a pull build + // neither has anything to say whatever the source tree contains. This check comes + // BEFORE the per-arm markers deliberately: those answer "does the source tree + // implement it", which stops being a statement about this library the moment the + // library is the pull one. Without it, a pull build would run the Handles arm + // against a library with no {slot, gen} key (a green asserting nothing) and the + // AbaControl arm against one whose guards are still in force (a hard red on + // `ctest -L integration-gpu`, which G2 requires green in BOTH builds). + // MG_IntegrationTest/CMakeLists.txt already withholds the markers in a pull build; + // this is the second lock, so a hand-forced environment cannot arm them either. + if (m_arm != Arm::Legacy && !BuildMarkerIsSet("MGITEST_PIPE_PUSH_BUILD")) { + GTEST_SKIP() << "the " << ArmName(m_arm) + << " arm needs a library built with MOBILEGL_PIPE_PUSH, and this one " + "was not: the {slot, gen} re-key and Features.PipeHandleAbaControl " + "are both #if MOBILEGL_PIPE_PUSH (Config.h, ConfigLoader.cpp), so " + "there is nothing here for either arm to assert against. The lane " + "stays registered so that `ctest -L integration-gpu` names the same " + "tests in the pull build and the push build (gate G2); the Legacy " + "arm is the one that is meaningful here, and it runs."; + } switch (m_arm) { case Arm::Handles: if (!ThisBackendsRekeyHasLanded()) { GTEST_SKIP() << "the Handles arm needs the backend's {slot, gen} re-key, and this " - "build does not have it: no source under MobileGL/MG_Backend/" + "build does not have it: the build's capability probe found no " + "slot table and no Track H subsystem constant under " + "MobileGL/MG_Backend/" << Gl().BackendName() - << " mentions the Track H subsystem constant (P2 package C for " - "DirectGLES, package D for DirectVulkan). The arm is registered " - "and visible, and arms itself when that package lands."; + << " (P2 package C for DirectGLES, package D for DirectVulkan). The " + "arm is registered and visible, and arms itself when that " + "package lands in a push build."; } return; case Arm::AbaControl: