Files
MobileGL/MobileGL/MG_IntegrationTest/CMakeLists.txt
T
swung0x48 91b889888e [Test] (.github/workflows, docs/ci, MG_IntegrationTest, MG_IntegrationTest/Harness, scripts/data): assert each split entry's transport in-test
- the split fixture fails an armed Split/Spawn/Tcp entry whose process did not resolve the transport its registration claims, or whose server is not where the claim says: a thread here for inproc, a process of its own for spawn and tcp (SplitRuntimePeek now names spawn, reads ClientSession::PeerServerPid() and this process's pid, _getpid() on Windows; an unknown own pid leaves only that comparison unjudged)
- SplitLaneHarness.TransportClaimRule, a GL-free unit test, proves the rule red and green on canned inputs; skip census: added to junit-integration-split-strict/unit-strict (test/unit and unit-split borrow that list)
- integration-split drops its post-hoc grep of one entry's log; the step keeps the IPC default proof as 'Default-knob resolution check', on the disaggregation branches only, and a strict default that changed falls back to Transport A/B like the other two
2026-10-17 16:08:42 -04:00

3708 lines
260 KiB
CMake

# MobileGL - MobileGL/MG_IntegrationTest/CMakeLists.txt
# Copyright (c) 2025-2026 MobileGL-Dev
# Licensed under the GNU Lesser General Public License v3.0:
# https://www.gnu.org/licenses/gpl-3.0.txt
# https://www.gnu.org/licenses/lgpl-3.0.txt
# SPDX-License-Identifier: LGPL-3.0-only
# End of Source File Header
cmake_minimum_required(VERSION 3.24)
# MobileGL headless GPU integration tests. Each scenario brings up a real EGL pbuffer context,
# renders through a real backend and asserts on glReadPixels output. The module is OFF by default
# (MOBILEGL_BUILD_INTEGRATION_TEST). Without a GPU every scenario skips: the harness runs bring-up
# in a forked child first, because MobileGL aborts on an unusable platform (HeadlessGL.cpp).
# A skip looks like a pass, so set MOBILEGL_ITEST_REQUIRE_GPU where a GPU is expected.
# MOBILEGL_BACKEND_TYPE is latched at initialization, so one process is one backend and each
# scenario is registered once per backend under the `integration-gpu` label.
message(STATUS "Generating build files for MobileGL Integration Test...")
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(MGL_ITEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..)
# Desktop links the static implementation directly. Android runs the same
# executable from adb shell and links the shipping shared library instead.
if (ANDROID)
set(MGL_ITEST_MOBILEGL_TARGET MobileGL)
elseif (TARGET MobileGL_s)
set(MGL_ITEST_MOBILEGL_TARGET MobileGL_s)
else()
message(STATUS "No MobileGL library target is available; skipping the integration test module")
return()
endif()
# MG_Test already pulls googletest in when MOBILEGL_BUILD_TEST is ON. Stand on
# our own feet when it is not, so this module can be built by itself.
if (NOT TARGET GTest::gtest)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.17.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
endif()
add_executable(MobileGLIntegrationTest
Main.cpp
Harness/HeadlessGL.cpp
Harness/BackendCapsPeek.cpp
Harness/PipeSlotPeek.cpp
Harness/PipeApplyPeek.cpp
Harness/P4aSeamPeek.cpp
Harness/P4aFinalFixPeek.cpp
Harness/PersistentMapPeek.cpp
Harness/SplitRuntimePeek.cpp
# ---- P3b/P4b wave 2-D package D2 (own block; D1 edits this file elsewhere) --------------
# The client texture emitter's own counters, for TextureViewAliasScenario's half of the
# owner-keyed emission cursor gate: a drain that ran and emitted nothing is invisible in
# pixels and is exactly the two-cursor regression's signature.
Harness/TextureEmitPeek.cpp
Scenarios/OrientationScenario.cpp
Scenarios/CrossFrameBufferScenario.cpp
Scenarios/ResidentIndexScenario.cpp
Scenarios/MultiDrawScenario.cpp
Scenarios/IndexedDrawFamilyScenario.cpp
Scenarios/SyncWireScenario.cpp
Scenarios/DrawParametersScenario.cpp
Scenarios/AsyncCompileScenario.cpp
Scenarios/ArbShaderObjectsScenario.cpp
Scenarios/CubeFaceReadbackScenario.cpp
Scenarios/TextureUnitSamplingScenario.cpp
Scenarios/XfbAfterClipDistanceScenario.cpp
Scenarios/UnwrittenPositionOutputScenario.cpp
Scenarios/SampleMaskScopeScenario.cpp
Scenarios/SampledSetStalenessScenario.cpp
Scenarios/ThreeChannelAttachmentScenario.cpp
Scenarios/SnormAttachmentScenario.cpp
Scenarios/MonolithAttachmentClearScenario.cpp
Scenarios/PipelineFailureScenario.cpp
Scenarios/AdvertisedLimitsScenario.cpp
Scenarios/PixelStoreSweepScenario.cpp
# P7 gate 5 (g5-readback): readbacks larger than one reply slot. Ordinary GL cases, so the
# monolith arms run them too - that is the parity half of the split registrations below.
Scenarios/BandedReadbackScenario.cpp
# P9 (W1): reads into a pixel pack buffer answered by nothing. Ordinary GL cases too, plus
# one wire case that skips where there is no wire.
Scenarios/PackBufferReadbackScenario.cpp
# P10 (A): fence polls answered from the server's reports.
Scenarios/FencePollScenario.cpp
Scenarios/PrimitiveRestartScenario.cpp
Scenarios/FragCoordOriginScenario.cpp
Scenarios/ClipControlScenario.cpp
Scenarios/ClientVertexArrayScenario.cpp
Scenarios/ClearThenReadPixelsScenario.cpp
# P15 S0: a clear between draws into the same framebuffer (DirectVulkan records it in the pass).
Scenarios/InPassClearScenario.cpp
# P15 S0: a CPU-converted vertex stream reused and re-written within a frame (DirectVulkan caches it).
Scenarios/ConvertedVertexStreamScenario.cpp
Scenarios/WriteRenameScenario.cpp
Scenarios/VaoShadowScenario.cpp
Scenarios/SampleVariablesScenario.cpp
Scenarios/DepthStencilReadbackScenario.cpp
Scenarios/DepthStencilReadbackMatrixScenario.cpp
Scenarios/DepthStencilReadbackAttachmentShapeScenario.cpp
Scenarios/ClipDistanceScenario.cpp
Scenarios/ViewportArrayScenario.cpp
Scenarios/SsboArrayLengthScenario.cpp
Scenarios/DoublePrecisionScenario.cpp
Scenarios/UniformInitializerScenario.cpp
Scenarios/SwizzleAccessRoutineScenario.cpp
Scenarios/IterationRPFirstReductionScenario.cpp
Scenarios/IterationRPProgram203Scenario.cpp
Scenarios/IterationRPScratchFixScenario.cpp
Scenarios/ProgramPipelineScenario.cpp
Scenarios/ImageLoadStoreSsoScenario.cpp
Scenarios/ImageTargetKindScenario.cpp
Scenarios/ImageFormatQualifierScenario.cpp
Scenarios/TextureRemintPullScenario.cpp
# P8-B: glGenerateMipmap's window and RGB16F / RGB32F on Espryt's server. Ordinary GL, so the
# monolith arms of both backends run it too.
Scenarios/GenerateMipmapServerScenario.cpp
Scenarios/NonCoreImageFormatScenario.cpp
Scenarios/ImageSizeAfterRespecScenario.cpp
Scenarios/EmptyTextureLevelScenario.cpp
Scenarios/ImageBindableRemintScenario.cpp
Scenarios/PartialUploadOverGpuContentScenario.cpp
Scenarios/SsboDeclarationFormScenario.cpp
Scenarios/Glsl420DeclarationScenario.cpp
Scenarios/IoBlockNameCollisionScenario.cpp
Scenarios/UnlocatedIoBlockScenario.cpp
Scenarios/TessellationDrawModeScenario.cpp
Scenarios/GeometryDrawModeScenario.cpp
Scenarios/PostLinkAttachScenario.cpp
Scenarios/FormatlessImageBakeScenario.cpp
Scenarios/FragmentOutputArrayIndexScenario.cpp
Scenarios/BufferTextureScenario.cpp
Scenarios/VertexAttribBindingScenario.cpp
Scenarios/XfbCaptureBufferReuseScenario.cpp
Scenarios/XfbPrimitiveQueryScenario.cpp
Scenarios/PrimitivesGeneratedNoXfbScenario.cpp
Scenarios/XfbRepeatedCaptureScenario.cpp
Scenarios/TessellationXfbCaptureScenario.cpp
Scenarios/PointSizeDemotionScenario.cpp
Scenarios/VertexArrayEnableDisableScenario.cpp
Scenarios/CopyImageLevelRangeScenario.cpp
Scenarios/CopyImageLayeredScenario.cpp
Scenarios/CopyImagePacked16Scenario.cpp
Scenarios/CopyImageStoreReadbackScenario.cpp
Scenarios/TextureViewScenario.cpp
# P3b/P4b wave 2-D package D2: the verification half of the owner-keyed emission cursor.
Scenarios/TextureViewAliasScenario.cpp
Scenarios/PackedWordReadbackScenario.cpp
Scenarios/LayeredAttachmentBarrierScenario.cpp
Scenarios/LayeredAttachmentShapeScenario.cpp
Scenarios/LayeredTextureReadbackScenario.cpp
Scenarios/AtomicCounterScenario.cpp
Scenarios/LargeArenaAdoptionScenario.cpp
Scenarios/DeletedBoundBufferScenario.cpp
Scenarios/SsboArrayDynamicIndexScenario.cpp
Scenarios/StorageBufferRegrowScenario.cpp
Scenarios/SpirvShaderBinaryScenario.cpp
Scenarios/RelinkStageSetScenario.cpp
Scenarios/GuiBatchScenario.cpp
Scenarios/UnboundImageDescriptorScenario.cpp
# P7 gate 5: an image unit naming a (level, layer) its texture lacks (GL 4.6 core 8.26).
Scenarios/InvalidImageUnitScenario.cpp
Scenarios/UnboundUniformBlockScenario.cpp
Scenarios/IntegerBorderColorScenario.cpp
Scenarios/ClearTexImageUndefinedLevelZeroScenario.cpp
Scenarios/RenderbufferBlendFormatScenario.cpp
Scenarios/DualSourceBlendScenario.cpp
Scenarios/OversizedRecordingSplitScenario.cpp
Scenarios/DescriptorPoolGrowthScenario.cpp
Scenarios/PipeVerifyArmingScenario.cpp
Scenarios/PoisonOmissionScenario.cpp
Scenarios/HandleRecycleScenario.cpp
Scenarios/CsoContentAddressingScenario.cpp
Scenarios/ResourceSubsystemControlScenario.cpp
Scenarios/TextureParamsWithoutASamplerViewScenario.cpp
# P13 W3b: the GL-level successor of the borrowed-slot unit-memo probe.
Scenarios/DsaUpdateKeepsTheBoundUnitScenario.cpp
# C8: a shared object changed through a sibling context reaches the other's next draw.
Scenarios/SharedContextChangeScenario.cpp
Scenarios/TextureUploadShapeScenario.cpp
Scenarios/ObjectSubsystemControlScenario.cpp
Scenarios/P4aSeamAuditScenario.cpp
Scenarios/P4aFinalFixScenario.cpp
# P5's two new scenarios, targets B and C of the reduced path (BRIEF-P5 4). Both are
# ORDINARY GL scenarios that run in every lane; the DirectGLES.Split. entries further down
# run the same cases with MOBILEGL_TRANSPORT=inproc.
Scenarios/TriangleScenario.cpp
Scenarios/PersistentCoherentMapScenario.cpp
Scenarios/DefaultFramebufferAcrossSwapScenario.cpp
# P15 present wait: a frame's off-screen work is submitted ahead of its window pass.
Scenarios/WindowPassGateScenario.cpp
Scenarios/ScenarioIsolationScenario.cpp
Scenarios/UploadOrderScenario.cpp
Scenarios/PassOrderScenario.cpp
Scenarios/DefaultScissorFollowsSurfaceScenario.cpp
Scenarios/ClearOrderScenario.cpp
)
target_include_directories(MobileGLIntegrationTest PRIVATE
${MGL_ITEST_ROOT}/include
${MGL_ITEST_ROOT}/MobileGL
)
# gtest, not gtest_main: Main.cpp installs the harness banner itself.
target_link_libraries(MobileGLIntegrationTest PRIVATE
GTest::gtest
${MGL_ITEST_MOBILEGL_TARGET}
)
if (ANDROID)
find_library(MGL_ITEST_ANDROID_LIBRARY android REQUIRED)
find_library(MGL_ITEST_LOG_LIBRARY log REQUIRED)
find_library(MGL_ITEST_MEDIANDK_LIBRARY mediandk REQUIRED)
target_link_libraries(MobileGLIntegrationTest PRIVATE
${MGL_ITEST_ANDROID_LIBRARY}
${MGL_ITEST_LOG_LIBRARY}
${MGL_ITEST_MEDIANDK_LIBRARY}
)
endif()
if (MSVC)
# Same reason as MG_Test/Backend/DirectVulkan: the GLES headers declare gl*
# as dllimport on Windows, so the in-library GL entry-point definitions only
# resolve if the whole static library is part of the link.
target_link_options(MobileGLIntegrationTest PRIVATE /WHOLEARCHIVE:MobileGL_s)
endif()
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DNOMINMAX)
if (ANDROID)
return()
endif()
# --- ctest wiring --------------------------------------------------------
# A bare libEGL on a glvnd box resolves to whatever vendor comes first, which is
# usually llvmpipe - a software rasteriser silently replacing the GPU under
# a GPU test. Pin the vendor/ICD json the same way MG_Benchmark's
# run_driver_bench.sh does.
#
# Leaving these empty is not a neutral default, it is the failure mode: an
# unpinned libEGL lands on llvmpipe and the suite goes green having tested a
# software rasteriser. So they are DETECTED here rather than defaulted to empty,
# and an empty result is a loud warning.
#
# mgl_itest_find_driver_json(<outVar> <description> <glob> [<glob>...])
# Picks the first json a real hardware vendor owns, in preference order, and
# never picks a software rasteriser (llvmpipe / lavapipe / swrast) - landing on
# one of those silently is the exact accident this pinning exists to prevent.
function(mgl_itest_find_driver_json outVar)
set(candidates "")
foreach(pattern IN LISTS ARGN)
file(GLOB matches "${pattern}")
list(APPEND candidates ${matches})
endforeach()
list(SORT candidates)
# Vendors ship an i686 json beside the x86_64 one and it sorts first. Pinning
# the wrong word size is worse than not pinning at all - the loader finds no
# driver and the whole suite skips - so drop the mismatched ones outright.
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
list(FILTER candidates EXCLUDE REGEX "i686|i386")
else()
list(FILTER candidates EXCLUDE REGEX "x86_64|aarch64")
endif()
set(software "")
foreach(vendor IN ITEMS nvidia amdgpu amd radeon intel_hasvk intel broadcom freedreno panfrost)
foreach(candidate IN LISTS candidates)
get_filename_component(leaf "${candidate}" NAME)
string(TOLOWER "${leaf}" leaf)
if (leaf MATCHES "${vendor}")
set(${outVar} "${candidate}" PARENT_SCOPE)
return()
endif()
endforeach()
endforeach()
# Nothing recognised as hardware. Report the first non-software entry if there
# is one; otherwise report nothing, so the warning below fires.
foreach(candidate IN LISTS candidates)
get_filename_component(leaf "${candidate}" NAME)
string(TOLOWER "${leaf}" leaf)
if (NOT leaf MATCHES "lvp|llvmpipe|lavapipe|swrast|softpipe")
set(${outVar} "${candidate}" PARENT_SCOPE)
return()
endif()
set(software "${candidate}")
endforeach()
set(${outVar} "" PARENT_SCOPE)
endfunction()
set(MGL_ITEST_DETECTED_EGL_VENDOR "")
set(MGL_ITEST_DETECTED_VK_ICD "")
if (UNIX AND NOT APPLE AND NOT ANDROID)
mgl_itest_find_driver_json(MGL_ITEST_DETECTED_EGL_VENDOR
"/usr/share/glvnd/egl_vendor.d/*.json"
"/etc/glvnd/egl_vendor.d/*.json")
mgl_itest_find_driver_json(MGL_ITEST_DETECTED_VK_ICD
"/usr/share/vulkan/icd.d/*.json"
"/etc/vulkan/icd.d/*.json")
endif()
set(MOBILEGL_ITEST_EGL_VENDOR "${MGL_ITEST_DETECTED_EGL_VENDOR}" CACHE FILEPATH
"glvnd EGL vendor json to pin for the integration tests (empty: leave the loader alone)")
set(MOBILEGL_ITEST_VK_ICD "${MGL_ITEST_DETECTED_VK_ICD}" CACHE FILEPATH
"Vulkan ICD json to pin for the DirectVulkan integration tests (empty: leave the loader alone)")
if (MOBILEGL_ITEST_EGL_VENDOR)
message(STATUS "Integration tests: pinning EGL vendor ${MOBILEGL_ITEST_EGL_VENDOR}")
else()
message(WARNING
"Integration tests: no EGL vendor json found or configured (MOBILEGL_ITEST_EGL_VENDOR is empty). "
"An unpinned libEGL on a glvnd system resolves to whichever vendor comes first, which is usually "
"llvmpipe - the scenarios would then go green against a software rasteriser instead of the GPU. "
"Set -DMOBILEGL_ITEST_EGL_VENDOR=/usr/share/glvnd/egl_vendor.d/<vendor>.json.")
endif()
if (MOBILEGL_ITEST_VK_ICD)
message(STATUS "Integration tests: pinning Vulkan ICD ${MOBILEGL_ITEST_VK_ICD}")
else()
message(WARNING
"Integration tests: no Vulkan ICD json found or configured (MOBILEGL_ITEST_VK_ICD is empty). "
"DirectVulkan would then load whichever ICD the loader enumerates first, quite possibly lavapipe. "
"Set -DMOBILEGL_ITEST_VK_ICD=/usr/share/vulkan/icd.d/<vendor>.json.")
endif()
# Turns "no usable GPU" from a clean skip into a failure - see ScenarioFixture.h.
# Without it the integration-gpu label is unfalsifiable: a run that skipped every
# scenario and a run that passed every scenario are the same green in ctest.
option(MOBILEGL_ITEST_REQUIRE_GPU
"Fail (rather than skip) the integration scenarios when the headless harness is unusable" OFF)
# No EGL_PLATFORM knob here on purpose. The harness pins EGL_PLATFORM=surfaceless
# itself before its first EGL call (HeadlessGL.cpp, EnsureHeadlessPlatform) so a
# developer's machine and a CI runner take the SAME path whether or not a window
# system happens to be running. This used to inject "x11", which is how the lane
# came up green on a workstation with WSLg and died on a runner with no X server.
#
# A build-system knob would not just be redundant, it would be a trap: `set(...
# CACHE ...)` does not rewrite an existing cache, so every build directory
# configured before this change would keep injecting EGL_PLATFORM=x11 and go on
# binding to a window system - silently, and only on the machines that have one.
# Someone reproducing a platform-specific bug sets EGL_PLATFORM in their own
# environment, which the harness still honours.
set(MGL_ITEST_COMMON_ENV "")
if (MOBILEGL_ITEST_EGL_VENDOR)
list(APPEND MGL_ITEST_COMMON_ENV "__EGL_VENDOR_LIBRARY_FILENAMES=${MOBILEGL_ITEST_EGL_VENDOR}")
endif()
unset(MOBILEGL_ITEST_EGL_PLATFORM CACHE) # see above: an old cache must not resurrect x11
if (MOBILEGL_ITEST_REQUIRE_GPU)
list(APPEND MGL_ITEST_COMMON_ENV "MOBILEGL_ITEST_REQUIRE_GPU=1")
endif()
set(MGL_ITEST_VULKAN_ENV ${MGL_ITEST_COMMON_ENV})
if (MOBILEGL_ITEST_VK_ICD)
list(APPEND MGL_ITEST_VULKAN_ENV "VK_ICD_FILENAMES=${MOBILEGL_ITEST_VK_ICD}")
# The three iterationRP repairs are tri-state quirks that default to device
# auto-detection, and lavapipe is not on any auto list - so on lavapipe the
# iterationRP scenarios run unrepaired and Program 203 misses its golden
# output. CI's integration-gpu job exports these three by hand; pinning them
# to the ICD instead means a local `ctest -L integration-gpu` measures the
# same thing the gate does, with no environment to remember.
if (MOBILEGL_ITEST_VK_ICD MATCHES "lvp_icd|lavapipe")
message(STATUS "Integration tests: lavapipe ICD - forcing the iterationRP repairs on")
list(APPEND MGL_ITEST_VULKAN_ENV
"MOBILEGL_ITRP_FIX=ForceOn")
endif()
endif()
# The ENVIRONMENT test property is itself a `;`-list, and gtest_discover_tests
# forwards PROPERTIES as a flat list - so a plain `;`-joined value arrives as
# four separate arguments and everything after the first is silently read as
# another property name. Escaping the separators keeps the whole thing one list
# element until set_tests_properties expands it back. Without this only
# MOBILEGL_BACKEND_TYPE reaches the test and the vendor/ICD pinning is lost.
function(mgl_itest_join_environment outVar)
set(joined "")
foreach(entry IN LISTS ARGN)
if (joined)
string(APPEND joined "\\;${entry}")
else()
set(joined "${entry}")
endif()
endforeach()
set(${outVar} "${joined}" PARENT_SCOPE)
endfunction()
# One lane of the integration binary: the cases FILTER selects, registered as PREFIX<suite.case>
# with the environment ENV (a list joined by mgl_itest_join_environment). LABELS defaults to
# integration-gpu and TIMEOUT to the scenario timeout. LOCK serialises lanes that share a resource
# such as a log file; TEST_LIST names the ctest-time variable listing the lane's tests; FAIL_REGEX
# and FIXTURES set those properties; PROPERTIES passes further property pairs through.
function(mgl_itest_lane prefix filter)
cmake_parse_arguments(PARSE_ARGV 2 lane "" "ENV;LABELS;TIMEOUT;LOCK;TEST_LIST;FAIL_REGEX;FIXTURES"
"PROPERTIES")
set(labels integration-gpu)
if (DEFINED lane_LABELS)
set(labels "${lane_LABELS}")
endif()
set(timeout ${MGL_ITEST_TIMEOUT})
if (DEFINED lane_TIMEOUT)
set(timeout ${lane_TIMEOUT})
endif()
# A value may itself be a list (two resource locks); escaped, it stays one argument when the
# optional pair is expanded into the call below.
set(listArgs "")
if (lane_TEST_LIST)
set(listArgs TEST_LIST "${lane_TEST_LIST}")
endif()
set(lockArgs "")
if (lane_LOCK)
string(REPLACE ";" "\;" lock "${lane_LOCK}")
set(lockArgs RESOURCE_LOCK "${lock}")
endif()
set(fixtureArgs "")
if (lane_FIXTURES)
string(REPLACE ";" "\;" fixtures "${lane_FIXTURES}")
set(fixtureArgs FIXTURES_REQUIRED "${fixtures}")
endif()
if (DEFINED lane_FAIL_REGEX)
gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "${prefix}" ${listArgs}
TEST_FILTER "${filter}"
DISCOVERY_TIMEOUT 30
PROPERTIES LABELS "${labels}" TIMEOUT ${timeout} ${lockArgs} ${fixtureArgs}
FAIL_REGULAR_EXPRESSION "${lane_FAIL_REGEX}" ${lane_PROPERTIES} ENVIRONMENT "${lane_ENV}")
else()
gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "${prefix}" ${listArgs}
TEST_FILTER "${filter}"
DISCOVERY_TIMEOUT 30
PROPERTIES LABELS "${labels}" TIMEOUT ${timeout} ${lockArgs} ${fixtureArgs}
${lane_PROPERTIES} ENVIRONMENT "${lane_ENV}")
endif()
endfunction()
# --- capability markers: what this tree implements, answered at configure time ----------
#
# Some scenarios assert on a mechanism a backend may not implement (a {slot, gen} key, a consumer
# of MOBILEGL_PIPE_HANDLE_ABA_CONTROL, a counter's emitter). The probes below add an MGITEST_*
# marker to MGL_ITEST_CAPABILITY_ENV for each one found; a scenario without its marker skips and
# names what is missing, instead of being unregistered or passing vacuously. Every verdict is
# printed at configure time, so a probe that wrongly answers "no" is visible.
set(MGL_ITEST_CAPABILITY_ENV "")
# Whether the library under test compiled the push arm. Passed in rather than inferred, because
# the two CSO counters and the cso[] bracket of the stats line are #if MOBILEGL_PIPE_PUSH: in a
# pull build there is no CSO to mint and no channel to read, so the control has nothing to say -
# and "nothing to say" must be a SKIP that names the reason, not an assertion failure about a
# missing bracket.
#
# The lanes themselves are registered in BOTH builds even so. `ctest -L integration-gpu` has to
# be name-for-name identical between the pull build and the push build (P2 gate G2), and a lane
# that exists in only one of them breaks that comparison for every future package - a much worse
# outcome than four entries that skip. P13 W3b: every build pushes, so the marker is unconditional.
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_PUSH_BUILD=1")
# mgl_itest_probe_for_symbol: outVar is the first source under `directory` with a line matching
# symbolRegex, or "" when none has one. Markers probe content, never a file name: the owning code
# picks its own file layout, and a file probe would answer "no" forever after a move, leaving its
# arm skipping for a reason that is no longer true. The glob is CONFIGURE_DEPENDS (adding or
# removing a file reconfigures) and every file found joins CMAKE_CONFIGURE_DEPENDS (editing one
# reconfigures).
function(mgl_itest_probe_for_symbol outVar directory symbolRegex)
file(GLOB_RECURSE mglItestProbeSources CONFIGURE_DEPENDS
"${directory}/*.h" "${directory}/*.hpp" "${directory}/*.cpp" "${directory}/*.c")
set(mglItestProbeHit "")
foreach(mglItestProbeSource IN LISTS mglItestProbeSources)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mglItestProbeSource}")
file(STRINGS "${mglItestProbeSource}" mglItestProbeLines REGEX "${symbolRegex}")
if (mglItestProbeLines AND NOT mglItestProbeHit)
set(mglItestProbeHit "${mglItestProbeSource}")
endif()
endforeach()
set(${outVar} "${mglItestProbeHit}" PARENT_SCOPE)
endfunction()
# mgl_itest_probe_for_two_symbols: the same probe over a conjunction. outVar is
# "<file matching A> + <file matching B>" when both regexes match somewhere under the directory,
# not necessarily in one file, and "" otherwise. It decides whether the ABA knob steers a
# backend's object keys: reading Features.PipeHandleAbaControl alone is not enough (a backend may
# consume it for other keys only), and requiring one file to hold both would tie the answer to
# file layout and leave the controls unarmed without notice; a false positive shows up as a red
# lane. Staleness is handled as in mgl_itest_probe_for_symbol.
function(mgl_itest_probe_for_two_symbols outVar directory symbolRegexA symbolRegexB)
file(GLOB_RECURSE mglItestProbeSources CONFIGURE_DEPENDS
"${directory}/*.h" "${directory}/*.hpp" "${directory}/*.cpp" "${directory}/*.c")
set(mglItestProbeHitA "")
set(mglItestProbeHitB "")
foreach(mglItestProbeSource IN LISTS mglItestProbeSources)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mglItestProbeSource}")
if (NOT mglItestProbeHitA)
file(STRINGS "${mglItestProbeSource}" mglItestProbeLinesA REGEX "${symbolRegexA}")
if (mglItestProbeLinesA)
set(mglItestProbeHitA "${mglItestProbeSource}")
endif()
endif()
if (NOT mglItestProbeHitB)
file(STRINGS "${mglItestProbeSource}" mglItestProbeLinesB REGEX "${symbolRegexB}")
if (mglItestProbeLinesB)
set(mglItestProbeHitB "${mglItestProbeSource}")
endif()
endif()
endforeach()
if (mglItestProbeHitA AND mglItestProbeHitB)
set(${outVar} "${mglItestProbeHitA} + ${mglItestProbeHitB}" PARENT_SCOPE)
else()
set(${outVar} "" PARENT_SCOPE)
endif()
endfunction()
# DirectGLES' Track H arm, probed by the subsystem bit it is gated on rather than by
# SlotTables.h existing: the bit is declared in the contract (kMGPipeSubsystemEsprytSlots, MG_Pipe/MGPipe.h) and the
# backend has to name it to honour MOBILEGL_PIPE_PUSH's default mask, whatever files package C
# spreads the slot tables across.
mgl_itest_probe_for_symbol(MGL_ITEST_ESPRYT_SLOTS
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectGLES" "kMGPipeSubsystemEsprytSlots")
if (MGL_ITEST_ESPRYT_SLOTS)
message(STATUS "Integration tests: DirectGLES is keyed on {slot, gen} (${MGL_ITEST_ESPRYT_SLOTS})")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectGLES=1")
else()
message(STATUS "Integration tests: no DirectGLES source names kMGPipeSubsystemEsprytSlots - "
"HandleRecycle.Handles will SKIP on it")
endif()
# The CSO counters' EMITTER. The tracker package may implement the tracker and the cache
# header-only - today it does (MG_Impl/Pipe/{Tracker,CsoCache}.h, no Tracker.cpp) - so what is
# looked for is what the control actually reads: a source emitting the two counters.
mgl_itest_probe_for_symbol(MGL_ITEST_CSO_EMITTER
"${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe" "RenderStateCso(Mints|Binds)")
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: no MG_Impl/Pipe source emits RenderStateCsoMints/Binds - "
"CsoContentAddressing will SKIP")
endif()
# DirectVulkan's Track H arm, and the ABA knob's consumer. Both over the whole backend
# directory: the re-key is subsystem 4's bit wherever package D reads it, and the knob has a
# consumer if ANY DirectVulkan source reverts a guard on it - today two do, in two files.
mgl_itest_probe_for_symbol(MGL_ITEST_MAGMA_REKEY
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectVulkan" "kMGPipeSubsystemMagmaVertexInput")
if (MGL_ITEST_MAGMA_REKEY)
message(STATUS "Integration tests: DirectVulkan's vertex input is keyed on {slot, gen} "
"(${MGL_ITEST_MAGMA_REKEY})")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_REKEY_DirectVulkan=1")
else()
message(STATUS "Integration tests: no DirectVulkan source names kMGPipeSubsystemMagmaVertexInput - "
"HandleRecycle.Handles will SKIP on it")
endif()
# P3a's buffer question, and it is NOT the two above. Bits 5/6 re-keyed each backend's
# VERTEX-INPUT memos; a BUFFER only travels as a handle once the resource_* family does
# (P3a for DirectGLES, P7 for DirectVulkan), and until then a buffer's backend twin is
# still reached from the frontend BufferObject. HandleRecycle's buffer case therefore has
# a marker of its own: reading the P2 one would arm its Handles arm on a tree where
# nothing about a buffer is keyed on a handle.
#
# Probed by MGPipeResourceOps - the op table PipeApply.h declares and a backend registers -
# over each backend's whole directory, for the reason the block above gives: the owning
# package picks its own file layout, and a filename probe would answer "no" forever the
# moment it moved the code.
foreach(mglItestResourceBackend DirectGLES DirectVulkan)
mgl_itest_probe_for_symbol(MGL_ITEST_RESOURCE_OPS
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/${mglItestResourceBackend}" "MGPipeResourceOps")
if (MGL_ITEST_RESOURCE_OPS)
message(STATUS "Integration tests: ${mglItestResourceBackend} registers a resource op table "
"(${MGL_ITEST_RESOURCE_OPS})")
list(APPEND MGL_ITEST_CAPABILITY_ENV
"MGITEST_HANDLE_REKEY_RESOURCES_${mglItestResourceBackend}=1")
else()
message(STATUS "Integration tests: no ${mglItestResourceBackend} source names "
"MGPipeResourceOps - HandleRecycle.Handles' buffer case will SKIP on it")
endif()
endforeach()
# The client-side emitter of P3a's map-persistent-roundtrips counter, which is what
# StorageBufferRegrow, LargeArenaAdoption and ResourceSubsystemControl read. Same shape and
# same reason as the CSO emitter probe above: the counter is minted in MG_Impl/Pipe (package
# B), header-only today, so the question is "does any source there emit it", not "does a
# named file exist". Until it does, mpr= is structurally zero and an assertion about it
# would be a statement about nothing.
mgl_itest_probe_for_symbol(MGL_ITEST_RESOURCE_EMITTER
"${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe" "MapPersistentRoundtrips")
if (MGL_ITEST_RESOURCE_EMITTER)
message(STATUS "Integration tests: map-persistent-roundtrips has an emitter "
"(${MGL_ITEST_RESOURCE_EMITTER})")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_RESOURCE_EMITTER_PRESENT=1")
else()
message(STATUS "Integration tests: no MG_Impl/Pipe source emits MapPersistentRoundtrips - "
"StorageBufferRegrow's, LargeArenaAdoption's and ResourceSubsystemControl's "
"mpr entries will SKIP")
endif()
# P4a's THIRD re-key question, per backend, and it is not either of the two above. Bits 5/6
# re-keyed each backend's VERTEX-INPUT memos and bit 7/8 the BUFFER's; P4a re-keys six OBJECT
# classes - texture, renderbuffer, framebuffer, sampler CSO, sampler view, shader CSO - behind
# four new subsystem bits, and a backend has to NAME one of those constants to honour
# MOBILEGL_PIPE_PUSH's default mask. A P4a case that read either older marker would arm its
# Handles arm on a tree where nothing about a texture is keyed on a handle: green for a re-key
# that does not exist. Probed by the constants rather than by a file, for the reason the block
# above gives - packages D and E choose their own file layout.
foreach(mglItestObjectBackend DirectGLES DirectVulkan)
mgl_itest_probe_for_symbol(MGL_ITEST_OBJECT_REKEY
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/${mglItestObjectBackend}"
"kMGPipeSubsystem(Framebuffer|TextureResources|Samplers|Programs)")
if (MGL_ITEST_OBJECT_REKEY)
message(STATUS "Integration tests: ${mglItestObjectBackend} is keyed on {slot, gen} for "
"P4a's object families (${MGL_ITEST_OBJECT_REKEY})")
list(APPEND MGL_ITEST_CAPABILITY_ENV
"MGITEST_HANDLE_REKEY_OBJECTS_${mglItestObjectBackend}=1")
else()
message(STATUS "Integration tests: no ${mglItestObjectBackend} source names "
"kMGPipeSubsystem{Framebuffer,TextureResources,Samplers,Programs} - "
"HandleRecycle's six P4a cases will SKIP their Handles arm on it")
endif()
# ...and whether the ABA knob reaches those kinds THERE. A conjunction over the WHOLE
# backend directory, for the reason mgl_itest_probe_for_two_symbols states: reading the
# knob is not the same as steering P4a's keys with it, so the weaker single-regex evidence
# would turn six always-on entries into a permanent red - but requiring one FILE to carry
# both halves would let a backend satisfy the question and miss the probe, and the six
# controls would then keep asserting the correct pixels with nothing recording that the
# flip was forgotten (F-M5).
mgl_itest_probe_for_two_symbols(MGL_ITEST_OBJECT_ABA
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/${mglItestObjectBackend}"
"PipeHandleAbaControl"
"kMGPipeSubsystem(Framebuffer|TextureResources|Samplers|Programs)")
if (MGL_ITEST_OBJECT_ABA)
message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL steers "
"${mglItestObjectBackend}'s P4a object keys (${MGL_ITEST_OBJECT_ABA})")
list(APPEND MGL_ITEST_CAPABILITY_ENV
"MGITEST_HANDLE_ABA_OBJECTS_${mglItestObjectBackend}=1")
else()
message(STATUS "Integration tests: MobileGL/MG_Backend/${mglItestObjectBackend} does not "
"both read Features.PipeHandleAbaControl and name a P4a subsystem bit "
"somewhere under it - HandleRecycle's six P4a cases will assert the "
"CORRECT pixels on the AbaControl arm and say that it is not a control "
"for them yet")
endif()
endforeach()
# The client-side emitter of P4a's four suppressor-visible emission counters, which is what
# ObjectSubsystemControl reads. Same shape and same reason as the CSO and map-persistent
# emitter probes above: the emitters are minted in MG_Impl/Pipe (packages B and C), header-only
# by design (D-P), so the question is "does any source there emit it", not "does a named file
# exist". Until one does, every counter in the emit[] bracket is structurally zero in BOTH arms
# of the A/B and an assertion about their difference would be a statement about nothing.
mgl_itest_probe_for_symbol(MGL_ITEST_OBJECT_EMITTER
"${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe" "FramebufferEmissions")
if (MGL_ITEST_OBJECT_EMITTER)
message(STATUS "Integration tests: P4a's object emissions have an emitter "
"(${MGL_ITEST_OBJECT_EMITTER})")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_OBJECT_EMITTER_PRESENT=1")
else()
message(STATUS "Integration tests: no MG_Impl/Pipe source emits FramebufferEmissions - "
"ObjectSubsystemControl's emission case will SKIP")
endif()
# ...and the CLIENT half of the texture-upload shape, separately, because it is a different
# question with a different owner's file behind it (review F-m7). ctu= is emitted by PipeStats
# in EVERY push build whether or not anything increments it, so "the counter read zero" and
# "no client emitter exists" are the same number and TextureUploadShape cannot tell them apart
# from the summary line alone. An emitter that STOPPED emitting reads exactly like no emitter
# at all in that field, and the two-sided assertion the scenario exists for would pass while
# comparing nothing. This probe is what separates them.
#
# THAT EMITTER HAS LANDED (P3b/P4b wave 2-D corrects this comment's tense):
# MG_Impl/Pipe/TextureEmit.h's EmitOneLevel increments
# CallClass::ClientTextureUploadEmissions on every piece it emits, so the probe hits and the
# else() arm below is unreachable on this tree. It is KEPT rather than deleted because it is
# what makes the arming decision falsifiable in both directions - a probe that cannot report
# "absent" cannot be shown to be looking for the right symbol.
mgl_itest_probe_for_symbol(MGL_ITEST_CLIENT_TEXTURE_UPLOAD_EMITTER
"${MGL_ITEST_ROOT}/MobileGL/MG_Impl/Pipe" "ClientTextureUploadEmissions")
if (MGL_ITEST_CLIENT_TEXTURE_UPLOAD_EMITTER)
message(STATUS "Integration tests: the client texture-upload counter has an emitter "
"(${MGL_ITEST_CLIENT_TEXTURE_UPLOAD_EMITTER}) - TextureUploadShape's "
"two-sided assertion is live")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_PIPE_CLIENT_TEXTURE_UPLOAD_EMITTER_PRESENT=1")
else()
message(STATUS "Integration tests: no MG_Impl/Pipe source emits "
"ClientTextureUploadEmissions - TextureUploadShape records the SERVER shape "
"only and asserts that ctu= is zero")
endif()
mgl_itest_probe_for_symbol(MGL_ITEST_MAGMA_ABA
"${MGL_ITEST_ROOT}/MobileGL/MG_Backend/DirectVulkan" "PipeHandleAbaControl")
if (MGL_ITEST_MAGMA_ABA)
message(STATUS "Integration tests: MOBILEGL_PIPE_HANDLE_ABA_CONTROL has a consumer "
"(${MGL_ITEST_MAGMA_ABA})")
list(APPEND MGL_ITEST_CAPABILITY_ENV "MGITEST_HANDLE_ABA_IMPLEMENTED=1")
else()
message(STATUS "Integration tests: no DirectVulkan source names PipeHandleAbaControl - "
"HandleRecycle.AbaControl will SKIP")
endif()
mgl_itest_join_environment(MGL_ITEST_GLES_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_ASYNC_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ASYNC_SHADER_COMPILE=1" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_FORCED_DS_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1" ${MGL_ITEST_COMMON_ENV})
# Environments for AsyncCompileScenario's compiler configurations and ViewportArrayScenario's
# negative control. Each setting is per process: part of what it decides is latched before the
# first GL call (the compile pool, the advertised extension list), and on Android this module
# links the shipping library, which exports no MG_Config, so each is an environment variable on
# a ctest entry of its own. Every list appends MGL_ITEST_COMMON_ENV / MGL_ITEST_VULKAN_ENV; an
# entry naming only its own variable would lose the EGL vendor and Vulkan ICD pinning.
mgl_itest_join_environment(MGL_ITEST_GLES_ASYNC_ON_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ASYNC_SHADER_COMPILE=1" ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_ASYNC_OFF_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ASYNC_SHADER_COMPILE=0" ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_ASYNC_ON_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ASYNC_SHADER_COMPILE=1" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_ASYNC_OFF_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ASYNC_SHADER_COMPILE=0" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_OPTIMISTIC_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ASYNC_SHADER_COMPILE=1"
"MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS=1" ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_OPTIMISTIC_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ASYNC_SHADER_COMPILE=1"
"MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS=1" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_NO_VIEWPORT_EMULATION_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ESPRYT_FORCE_VIEWPORT_ARRAY_EMULATION=0" ${MGL_ITEST_COMMON_ENV})
# MOBILEGL_LOG_FILE_PATH alongside the pin, because the arming assertion needs somewhere to
# read the library's own report from. The strip's arming signal is a latched MGLOG_I and there
# is no other way for a test process to learn that it fired - MG_Config is not reachable from
# this module on Android, where it links the shipping library. The path is per-lane so nothing
# else appends to it, and the case only trusts the bytes written after it started.
mgl_itest_join_environment(MGL_ITEST_GLES_UNLOCATED_IO_BLOCKS_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ESPRYT_UNLOCATED_IO_BLOCKS=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/unlocated-io-blocks.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_WIDENED_PACKED16_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_ESPRYT_WIDEN_PACKED16_STORAGE=1" ${MGL_ITEST_COMMON_ENV})
# Same shape as the UnlocatedIoBlocks entry: the log path is where the reroute's latched
# MGLOG_I lands, and the arming case only trusts the bytes written after it started.
mgl_itest_join_environment(MGL_ITEST_VULKAN_PRIMGEN_REROUTE_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_MAGMA_PRIMGEN_QUERY_REROUTE=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/primgen-query-reroute.log"
${MGL_ITEST_VULKAN_ENV})
# The command-buffer draw budget pinned far below one frame's draws, with the log path for the
# split's latched MGLOG_I - the same arming reasoning as the two entries above.
mgl_itest_join_environment(MGL_ITEST_VULKAN_WINDOW_PASS_GATE_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_WINDOW_PASS_GATE_LANE=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/window-pass-gate.log"
${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_SPLIT_RECORDING_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER=64"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/oversized-recording-split.log"
${MGL_ITEST_VULKAN_ENV})
# The point-size demotion pinned on, per backend, with a per-lane log file for the arming
# assertion - the same MOBILEGL_LOG_FILE_PATH reasoning as the UnlocatedIoBlocks lane above.
# Two lanes because the demotion runs in the SHARED phase-B chain and each backend then
# consumes it differently (Espryt respells the driver-side capture request, Magma binds the
# SPIR-V Xfb decorations to the carrier).
mgl_itest_join_environment(MGL_ITEST_GLES_POINT_SIZE_DEMOTION_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_POINT_SIZE_DEMOTION=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/point-size-demotion-gles.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_POINT_SIZE_DEMOTION_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_POINT_SIZE_DEMOTION=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/point-size-demotion-vulkan.log"
${MGL_ITEST_VULKAN_ENV})
# TIMEOUT on every entry: a GPU test that wedges must fail the run, not hang it.
set(MGL_ITEST_TIMEOUT 120)
include(GoogleTest)
# Discovery runs `--gtest_list_tests`, which does not construct the harness and
# so needs no GPU. One registration per backend; TEST_PREFIX keeps the two sets
# of ctest names apart.
# The original ambient names remain present, but mechanism oracles must not
# inherit an inproc environment: they deliberately toggle process-local push
# masks, pointer-key ABA, direct persistent adoption or synchronous stats slots.
# Keep ordinary pixel/byte cases (including the other arena/regrow cases) on the
# caller's transport. A separate label makes every exception inspectable.
set(MGL_ITEST_MONOLITH_MECHANISM_FILTER
"HandleRecycleScenario.*:CsoContentAddressingScenario.*:ResourceSubsystemControlScenario.*:ObjectSubsystemControlScenario.*:PipeVerifyArmingScenario.*:PoisonOmissionScenario.*:StorageBufferRegrowScenario.NStorageDefinitionsCostNMapPersistentRoundtripsNotOnePerDraw:LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip")
# P3b/P4b wave 2-D package D2: TextureViewAliasScenario joins this list rather than the ambient
# one for the same reason TextureViewScenario is in it - every case it carries constructs a
# glTextureView, which DirectGLES withholds unless MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1
# (ConfigLoader.cpp:213). In the ambient entry it would green-skip on every run and the gate
# would be a name in a list.
set(MGL_ITEST_GLES_VIEW_FILTER
"TextureViewScenario.*:TextureViewAliasScenario.*:F1WireScenario.ComputeImageStoreThroughViewPreservesOtherRootLayer:F1WireScenario.TextureViewClearTargetsItsRootMipAndLayer:F1WireScenario.GenerateMipmapThroughViewKeepsOwnerLevelsAndLayersOutsideItsWindow:InvalidImageUnitScenario.ALevelPastATextureViewsWindowReadsZero")
mgl_itest_lane("DirectGLES." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:${MGL_ITEST_GLES_VIEW_FILTER}"
ENV "${MGL_ITEST_GLES_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}"
ENV "${MGL_ITEST_VULKAN_ENVIRONMENT}")
# ScenarioIsolationScenario's two cases in ONE process: the first leaves a deleted program current
# and an error unread, the second asserts ScenarioTest::SetUp cleared both. The entries above run
# one case per process, where both cases are green either way. add_test because
# gtest_discover_tests registers cases one at a time; a PLAIN list (see the arena pair below).
foreach(backend DirectGLES DirectVulkan)
if (backend STREQUAL "DirectGLES")
string(REPLACE "\\;" ";" mglItestIsolationEnv "${MGL_ITEST_GLES_ENVIRONMENT}")
else()
string(REPLACE "\\;" ";" mglItestIsolationEnv "${MGL_ITEST_VULKAN_ENVIRONMENT}")
endif()
add_test(NAME "${backend}.ScenarioIsolationScenario.CasesInOneProcess"
COMMAND MobileGLIntegrationTest "--gtest_filter=ScenarioIsolationScenario.*"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties("${backend}.ScenarioIsolationScenario.CasesInOneProcess" PROPERTIES
LABELS integration-gpu
TIMEOUT ${MGL_ITEST_TIMEOUT}
ENVIRONMENT "${mglItestIsolationEnv}")
endforeach()
foreach(backend DirectGLES DirectVulkan)
if (backend STREQUAL "DirectGLES")
set(mechanism_environment "${MGL_ITEST_GLES_ENVIRONMENT}")
else()
set(mechanism_environment "${MGL_ITEST_VULKAN_ENVIRONMENT}")
endif()
mgl_itest_lane("${backend}." "${MGL_ITEST_MONOLITH_MECHANISM_FILTER}"
LABELS "integration-gpu\;integration-monolith-control" ENV "${mechanism_environment}\;MOBILEGL_TRANSPORT=monolith")
endforeach()
# This is Espryt's existing feature opt-in, independent of transport. Exercise
# the actual view implementation instead of inheriting its default-off skip.
mgl_itest_lane("DirectGLES." "${MGL_ITEST_GLES_VIEW_FILTER}"
ENV "${MGL_ITEST_GLES_ENVIRONMENT}\;MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1")
# A third registration, of ONE scenario, with asynchronous shader compilation
# pinned on. Not a second code path in the renderer: a second ALLOCATION pattern.
# The async pipeline's job objects change which of the freed blocks the capture
# phase is handed, and that is what decides whether the destroyed-VAO address is
# reached at all - on the ablated (pre-fix) tree async=1 reproduced 3 runs out of
# 3 where the ambient default reproduced 2 of 3. Pinning it here means the
# high-signal configuration runs whatever the shipped default becomes, instead of
# the suite quietly weakening the day that default flips. It must be process-wide
# (the ENVIRONMENT property), not an in-process scope: the compile pool and its
# threads are stood up at initialization, and their allocations are half the
# point. DirectVulkan only - the memo this pins is DirectVulkan's.
mgl_itest_lane("DirectVulkan.AsyncCompile." "XfbAfterClipDistanceScenario.*"
ENV "${MGL_ITEST_VULKAN_ASYNC_ENVIRONMENT}")
# A fourth registration, of the depth/stencil readback scenarios, with the ES
# shader-sampling emulation forced on. Not paranoia - without it these scenarios are
# UNFALSIFIABLE on the machines this suite runs on: OpenGL ES has no depth or stencil
# readback in core, but llvmpipe accepts the reads anyway, so every one of them
# goes green through a native path that the Adreno device does not have. Deleting the
# entire emulation left all of them passing. With the flag the native spellings are off
# the table and only the path the device actually takes remains. DirectGLES only - the
# emulation is DirectGLES's.
mgl_itest_lane("DirectGLES.ForcedDepthStencilEmulation." "DepthStencilReadback*Scenario.*"
ENV "${MGL_ITEST_GLES_FORCED_DS_ENVIRONMENT}")
# UnlocatedIoBlockScenario with the interface-block location strip pinned on. llvmpipe handles
# located interface blocks, so the driver check that arms the strip on Mali passes here and the
# ambient entry never runs the strip. This lane, and each lane below that reads its log back,
# holds a RESOURCE_LOCK named after the log: every process in a lane truncates the shared
# MOBILEGL_LOG_FILE_PATH (fopen "w"), so under ctest -j a sibling could empty it while the
# arming case reads it. A narrower filter would also work but would rename entries, and ctest
# names must not disappear (ARCHITECTURE.md §13.2).
mgl_itest_lane("DirectGLES.UnlocatedIoBlocks." "UnlocatedIoBlockScenario.*"
LOCK unlocated-io-blocks.log ENV "${MGL_ITEST_GLES_UNLOCATED_IO_BLOCKS_ENVIRONMENT}")
# P4a final review C-2: the dirty-then-delete case, with the allocator SCRIBBLING every freed
# block. The defect this pins was a client emitter resolving a dead-but-not-recycled texture
# handle to the freed ITextureObject* and calling a virtual on it from the next verb's drain;
# whether that reads the object's ghost or faults depends on what the allocator did with the
# block, so the ambient registrations above run the case as the application would see it and
# these two run it with MALLOC_PERTURB_ set, where a resolved-but-dead pointer faults rather
# than passes. Both backends: the death path is backend-neutral by ruling (ID-8).
mgl_itest_join_environment(MGL_ITEST_GLES_MALLOC_PERTURB_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MALLOC_PERTURB_=165" ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_MALLOC_PERTURB_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MALLOC_PERTURB_=165" ${MGL_ITEST_VULKAN_ENV})
mgl_itest_lane("DirectGLES.MallocPerturb." "P4aFinalFixScenario.ADirtyTextureDeletedBeforeAnyVerbIsWalkedByTheNextDrain"
ENV "${MGL_ITEST_GLES_MALLOC_PERTURB_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.MallocPerturb." "P4aFinalFixScenario.ADirtyTextureDeletedBeforeAnyVerbIsWalkedByTheNextDrain"
ENV "${MGL_ITEST_VULKAN_MALLOC_PERTURB_ENVIRONMENT}")
# AsyncCompileScenario, with asynchronous compilation PINNED ON per backend.
#
# Not a duplicate of what the two ambient registrations already run: they run whatever
# MobileGL's built-in default happens to be, and the day that default flips they would
# stop covering the asynchronous path without anything going red. These entries are the
# ones that keep the asynchronous half tested no matter what ships. They are also the
# only place ExtensionStringMatchesTheConfiguration can assert that the extension IS
# advertised - the case derives its expectation from this variable and nothing else, and
# skips where it is unset, precisely so that it is not asserting the implementation
# against itself.
mgl_itest_lane("DirectGLES.AsyncOn." "AsyncCompileScenario.*" ENV "${MGL_ITEST_GLES_ASYNC_ON_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.AsyncOn." "AsyncCompileScenario.*"
ENV "${MGL_ITEST_VULKAN_ASYNC_ON_ENVIRONMENT}")
# The other side of the same switch: asynchronous compilation OFF, so
# GL_KHR_parallel_shader_compile must be WITHDRAWN from both spellings of the extension
# list and GL_MAX_SHADER_COMPILER_THREADS_KHR must read 0. Only that one case is
# registered here because it is the only one that has anything to say in this
# configuration - the other four exist to observe worker-built artifacts, and there are
# none - so registering the whole scenario would buy four guaranteed skips per backend.
# Together with the AsyncOn. entries above, one ctest run still covers both flag states,
# which is what the in-process forcing used to be for.
mgl_itest_lane("DirectGLES.AsyncOff." "AsyncCompileScenario.ExtensionStringMatchesTheConfiguration"
ENV "${MGL_ITEST_GLES_ASYNC_OFF_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.AsyncOff." "AsyncCompileScenario.ExtensionStringMatchesTheConfiguration"
ENV "${MGL_ITEST_VULKAN_ASYNC_OFF_ENVIRONMENT}")
# The optimistic-status quirk's end-to-end shape. Its own entries and not part of the
# AsyncOn. ones because the quirk is not neutral for the rest of the scenario: with it in
# force glGetShaderiv(GL_COMPILE_STATUS) deliberately answers without joining, which is
# exactly what CompletionStatusPollingThenForcedJoin asserts must NOT happen. Off by
# default and never advertised, so - unlike asynchronous compilation, which announces
# itself through the extension string - the variable is the only thing that can tell the
# case it is in force.
mgl_itest_lane("DirectGLES.OptimisticShaderStatus." "AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly"
ENV "${MGL_ITEST_GLES_OPTIMISTIC_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.OptimisticShaderStatus." "AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly"
ENV "${MGL_ITEST_VULKAN_OPTIMISTIC_ENVIRONMENT}")
# The negative control for the DirectGLES gl_ViewportIndex emulation, in a process that
# has it switched off. One case, because it is the only one the switch may touch: with
# the emulation off the three positive cases in the same fixture describe behaviour the
# backend does not have, so a whole-scenario registration would be three guaranteed reds.
# DirectGLES only - the flag steers nothing on DirectVulkan, which routes natively.
mgl_itest_lane("DirectGLES.NoViewportArrayEmulation." "ViewportArrayScenario.WithoutTheEmulationEveryIndexCollapsesOntoViewportZero"
ENV "${MGL_ITEST_GLES_NO_VIEWPORT_EMULATION_ENVIRONMENT}")
# PrimitivesGeneratedNoXfbScenario again, with the GL_PRIMITIVES_GENERATED statistics
# reroute PINNED ON. The ambient DirectVulkan registration runs the same cases under the
# bring-up probe's Auto verdict, so between the two entries both accounting paths answer
# the same GL questions and must produce the same numbers - the "two pools must agree"
# gate this machine can hold that the affected device cannot. The pinned entry is also
# the only one whose arming case runs: it asserts the renderer's latched MGLOG_I, so a
# silently-disarmed reroute (an inverted override mapping, a lost gate) fails here
# instead of leaving every equality case vacuously green. DirectVulkan only - the flag
# steers nothing on DirectGLES.
mgl_itest_lane("DirectVulkan.PrimGenReroute." "PrimitivesGeneratedNoXfbScenario.*"
LOCK primgen-query-reroute.log ENV "${MGL_ITEST_VULKAN_PRIMGEN_REROUTE_ENVIRONMENT}")
# OversizedRecordingSplitScenario with the command-buffer draw budget PINNED to 64. The ambient
# registrations run its cases under the default budget, which never splits them; this entry
# splits every case at least once between the draws that leave state (depth, stencil, a blend
# destination, multisample samples, an open query, an active capture) and the draws that
# consume it, so the results prove a split carries all of it, and the arming assertion (the
# renderer's latched MGLOG_I) proves the split happened at all - on a renderer that records the
# whole frame into one command buffer, it is the assertion that goes red. DirectVulkan only:
# the budget steers nothing on DirectGLES, whose driver flushes on its own.
#
# P11 M2: RESOURCE_LOCK, for the UnlocatedIoBlocks lane's reason above - EVERY case here reads the
# lane's one log back, and the library truncates it at each process's start, so under `ctest -j` a
# sibling case wipes the line a reading case is looking for (measured on this tree: 5 of 8 red at
# -j 6, a different five on the monolith and inproc runs).
mgl_itest_lane("DirectVulkan.SplitRecording." "OversizedRecordingSplitScenario.*"
LOCK oversized-recording-split.log ENV "${MGL_ITEST_VULKAN_SPLIT_RECORDING_ENVIRONMENT}")
# P15 present wait: WindowPassGateScenario with its arming assertion on. The ambient entries run
# its cases for the pixels; this one also reads the renderer's latched MGLOG_I back, which a
# renderer that submits each frame as one batch behind the acquire wait never writes. DirectVulkan
# only: Espryt's flush ahead of the window buffer needs a window surface, which this harness does
# not have. RESOURCE_LOCK for SplitRecording's reason: every case reads the lane's one log back.
mgl_itest_lane("DirectVulkan.WindowPassGate." "WindowPassGateScenario.*"
LOCK window-pass-gate.log ENV "${MGL_ITEST_VULKAN_WINDOW_PASS_GATE_ENVIRONMENT}")
# The packed16 copy scenarios again, with the 8-bit storage widening PINNED ON. The ambient
# registrations above cover the narrow storage - on every CI driver the widening's POST
# probe finds no field-order mirror, so Auto keeps the native 16-bit path - which means the
# storage every AFFECTED device will actually run would otherwise execute nowhere at all:
# no CI driver has the Mali bug that arms it. This lane is what proves the widened storage
# is client-invisible (same packed words in and out on every leg the 18 failing CTS bodies
# used, the renderbuffer one included). DirectGLES only - the flag steers nothing on
# DirectVulkan, which has always stored these formats widened.
mgl_itest_lane("DirectGLES.WidenedPacked16." "CopyImagePacked16Scenario.*"
ENV "${MGL_ITEST_GLES_WIDENED_PACKED16_ENVIRONMENT}")
# PointSizeDemotionScenario with the demotion PINNED ON, per backend, for the reason every
# pinned lane above exists: llvmpipe and lavapipe both HOST gl_PointSize in tessellation and
# geometry stages, so the ambient registrations run these captures through the built-in and
# the demotion - the path every affected Mali device actually takes - would execute nowhere.
# The ambient runs stay the negative control: same scenario, same CPU-computed bytes, native
# path. Both backends, because the demotion is shared phase-B work with two different
# consumers (the ESSL capture respelling vs the SPIR-V Xfb carrier binding).
mgl_itest_lane("DirectGLES.PointSizeDemotion." "PointSizeDemotionScenario.*"
LOCK point-size-demotion-gles.log ENV "${MGL_ITEST_GLES_POINT_SIZE_DEMOTION_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.PointSizeDemotion." "PointSizeDemotionScenario.*"
LOCK point-size-demotion-vulkan.log ENV "${MGL_ITEST_VULKAN_POINT_SIZE_DEMOTION_ENVIRONMENT}")
# --- MOBILEGL_PIPE_VERIFY lanes (ARCHITECTURE.md §13.2), under `if (MOBILEGL_PIPE_VERIFY)` below
# They exist only in a -DMOBILEGL_PIPE_VERIFY=ON build, so `ctest -L integration-verify
# --no-tests=error` fails when the option is lost. The ambient Verify. entries must not set
# MOBILEGL_PIPE_VERIFY_CORRUPT or MOBILEGL_PIPE_POISON_OMIT: CI's negative controls export them
# in the job environment, and a property of the same name would override them.
# --- handle ABA: one always-on monolith HandleRecycle.Handles lane per backend -------------
# MGITEST_HANDLE_ARM selects the arm (the scenario skips in the ambient entries). The lanes run
# the default subsystem mask, so they measure the {slot, gen} key that ships.
set(MGL_ITEST_HANDLES_ARM_KNOBS "")
mgl_itest_join_environment(MGL_ITEST_GLES_HANDLE_HANDLES_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"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_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_HANDLE_ARM=handles" ${MGL_ITEST_HANDLES_ARM_KNOBS}
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
# P13 W6: the AbaControlHandles lane pinned the frontend arm (its knob defeats the identity half
# of Magma's frontend-arm keys); that arm is deleted, and the record arm keys every object on the
# applier's {slot, gen}, which the knob does not reach. Removed (docs/ci/ci-cleanup-log.md).
mgl_itest_lane("DirectGLES.HandleRecycle.Handles." "HandleRecycleScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_HANDLE_HANDLES_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.HandleRecycle.Handles." "HandleRecycleScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_HANDLE_HANDLES_ENVIRONMENT}")
# --- CSO content-addressing negative control ----------------------------------------------
# The On and Off masks differ only in bit 63 of MOBILEGL_PIPE_PUSH
# (kMGPipeBehaviourNoCsoContentAddressing); the rest of each mask is the default 0x3fff, because
# subsystem bits 0-13 are fixed on. The cases read the csom / csob counters from the library log,
# so each lane has a private log path (ARCHITECTURE.md §16.1). MOBILEGL_PIPE_STATS_PERIOD=1 writes
# one summary line per eglSwapBuffers, so two swaps bracket exactly the workload.
mgl_itest_join_environment(MGL_ITEST_GLES_CSO_ON_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_CSO_LANE=content-addressed"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/cso-content-addressed-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_CSO_OFF_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_CSO_LANE=no-content-addressing"
"MOBILEGL_PIPE_PUSH=0x8000000000003fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/cso-no-content-addressing-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_CSO_ON_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_CSO_LANE=content-addressed"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/cso-content-addressed-DirectVulkan.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_CSO_OFF_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_CSO_LANE=no-content-addressing"
"MOBILEGL_PIPE_PUSH=0x8000000000003fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/cso-no-content-addressing-DirectVulkan.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
mgl_itest_lane("DirectGLES.CsoContentAddressing.On." "CsoContentAddressingScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_CSO_ON_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.CsoContentAddressing.Off." "CsoContentAddressingScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_CSO_OFF_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.CsoContentAddressing.On." "CsoContentAddressingScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_CSO_ON_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.CsoContentAddressing.Off." "CsoContentAddressingScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_CSO_OFF_ENVIRONMENT}")
# P13 W3b (ID-P13-3): THE NAMED-REFUSAL CONTROLS that replace the Off / Refused A/B lanes. Subsystem
# bits 0-13 are fixed on: their pre-handle arms are gone, so a mask that clears one has nothing to
# run and ConfigLoader refuses it at startup with Fatal{PipeSubsystemsFixedOn}. Each control runs
# the scenario its lane used to run, with the lane's own environment and mask, and passes only if
# the process went red AND its log names that refusal with that mask - a loader failure, a missing
# GPU or any other red is not this control's red (Harness/RefusedMaskControl.cmake).
function(mgl_itest_add_refused_mask_control name mask gtestFilter environment)
string(MAKE_C_IDENTIFIER "${name}" mglRefusedLog)
set(mglRefusedLog "${CMAKE_CURRENT_BINARY_DIR}/refused-${mglRefusedLog}.log")
add_test(NAME "${name}" COMMAND "${CMAKE_COMMAND}"
"-DEXE=$<TARGET_FILE:MobileGLIntegrationTest>" "-DFILTER=${gtestFilter}"
"-DMASK=${mask}" "-DLOG=${mglRefusedLog}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/Harness/RefusedMaskControl.cmake")
# The lane's environment (joined with escaped separators for gtest_discover_tests), as a plain
# list, with its log path pointed at a file this control alone reads.
string(REPLACE "\\;" ";" environment "${environment}")
string(REGEX REPLACE "MOBILEGL_LOG_FILE_PATH=[^;]*" "MOBILEGL_LOG_FILE_PATH=${mglRefusedLog}"
environment "${environment}")
if (NOT environment MATCHES "MOBILEGL_LOG_FILE_PATH=")
set(environment "${environment};MOBILEGL_LOG_FILE_PATH=${mglRefusedLog}")
endif()
# Without it an unusable harness SKIPS and exits 0, which would read as "the mask ran green";
# with it the refusal's abort is a red the script can then attribute by the log.
set(environment "${environment};MOBILEGL_ITEST_REQUIRE_GPU=1")
set_tests_properties("${name}" PROPERTIES
LABELS "integration-gpu;integration-monolith-control"
TIMEOUT ${MGL_ITEST_TIMEOUT}
RESOURCE_LOCK "refused-${name}.log"
ENVIRONMENT "${environment}")
endfunction()
# --- Resource subsystem control and the map-persistent-roundtrip counting lanes ------------
# DirectGLES only, as ResourceSubsystemControlScenario.cpp explains. The On lane runs the default
# mask; 0x7f clears fixed-on subsystem bits, so it is registered as a refused-mask control
# (mgl_itest_add_refused_mask_control above). Each log-reading entry has a private log path and a
# single-case filter (ARCHITECTURE.md §16.1). MOBILEGL_PIPE_STATS_PERIOD=1 writes one summary line
# per eglSwapBuffers, so two swaps bracket exactly the workload.
mgl_itest_join_environment(MGL_ITEST_GLES_RESOURCE_SUBSYSTEM_ON_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_RESOURCE_SUBSYSTEM_LANE=on"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/resource-subsystem-on-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_RESOURCE_SUBSYSTEM_OFF_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_RESOURCE_SUBSYSTEM_LANE=off"
"MOBILEGL_PIPE_PUSH=0x7f" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/resource-subsystem-off-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.ResourceSubsystemControl.On." "ResourceSubsystemControlScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_RESOURCE_SUBSYSTEM_ON_ENVIRONMENT}")
mgl_itest_add_refused_mask_control("DirectGLES.SubsystemMaskRefusal.ResourceSubsystemControlOff" "0x7f" "ResourceSubsystemControlScenario.*"
"${MGL_ITEST_GLES_RESOURCE_SUBSYSTEM_OFF_ENVIRONMENT}")
# G10's two counting entries, one per scenario that has a claim about the counter:
# StorageBufferRegrow asserts N definitions cost N round trips (never one per draw), and
# LargeArenaAdoption asserts one adoption costs exactly one. Each names a single case in its
# TEST_FILTER, for the private-log reason above.
mgl_itest_join_environment(MGL_ITEST_GLES_MPR_REGROW_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_MPR_LANE=storage-buffer-regrow"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/mpr-storage-buffer-regrow-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_MPR_ARENA_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_MPR_LANE=large-arena-adoption"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/mpr-large-arena-adoption-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.MapPersistentRoundtrips." "StorageBufferRegrowScenario.NStorageDefinitionsCostNMapPersistentRoundtripsNotOnePerDraw"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_MPR_REGROW_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.MapPersistentRoundtrips." "LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_MPR_ARENA_ENVIRONMENT}")
# LargeArenaAdoption's three behavioural cases under BOTH arms of the same A/B. This file is where
# an adopted store's whole life is exercised - the NULL-data definition that adopts it, an
# in-flight SubData, a readback and a GPU write - so if the handle path and the legacy
# BufferBackendOps path disagree about any of it, one of these two lanes goes red and names which.
# 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" "MGITEST_PERSISTENT_MAP_ARM=adopted"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_PUSH=0x3fff"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_ARENA_SUBSYSTEM_OFF_ENVIRONMENT
"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})
mgl_itest_lane("DirectGLES.ResourceSubsystemOn." "LargeArenaAdoptionScenario.*"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_ARENA_SUBSYSTEM_ON_ENVIRONMENT}")
mgl_itest_add_refused_mask_control("DirectGLES.SubsystemMaskRefusal.ResourceSubsystemOff" "0x7f" "LargeArenaAdoptionScenario.*"
"${MGL_ITEST_GLES_ARENA_SUBSYSTEM_OFF_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.ArenaArm." "LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_ARENA_ARM_ENVIRONMENT}")
# --- Object subsystem control, its refused masks and the no-consumer check -----------------
# The On lane runs the default mask. 0x1ff, 0x9ff (samplers without texture resources) and 0x5ff
# (texture resources without samplers) clear fixed-on subsystem bits, so they are refused-mask
# controls. The Consumer (DirectGLES) and NoConsumer (DirectVulkan) lanes run the default mask and
# assert that MGPipeApplierState::RefusedNoConsumer never moves: a count there means the client
# emitted an object record no backend consumes, and an accepted one would clear dirty flags that
# nothing uploads. The DirectGLES lane keeps the check from passing on a tree that emits nothing.
# Each log-reading lane has a private log path and one case (ARCHITECTURE.md §16.1).
mgl_itest_join_environment(MGL_ITEST_GLES_OBJECT_SUBSYSTEM_ON_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_OBJECT_SUBSYSTEM_LANE=on"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-on-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_OBJECT_SUBSYSTEM_OFF_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_OBJECT_SUBSYSTEM_LANE=off"
"MOBILEGL_PIPE_PUSH=0x1ff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-off-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_OBJECT_SUBSYSTEM_REFUSED_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_OBJECT_SUBSYSTEM_LANE=refused"
"MOBILEGL_PIPE_PUSH=0x9ff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-refused-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_OBJECT_SUBSYSTEM_REFUSED_TEXTURE_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_OBJECT_SUBSYSTEM_LANE=refused-texture"
"MOBILEGL_PIPE_PUSH=0x5ff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-refused-texture-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_OBJECT_SUBSYSTEM_CONSUMER_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_OBJECT_SUBSYSTEM_LANE=consumer"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-consumer-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_OBJECT_SUBSYSTEM_NO_CONSUMER_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MGITEST_OBJECT_SUBSYSTEM_LANE=no-consumer"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/object-subsystem-no-consumer-DirectVulkan.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
mgl_itest_lane("DirectGLES.ObjectSubsystemControl.On." "ObjectSubsystemControlScenario.ClearingTheP4aBitsStopsTheEmissionsAndNotThePixels"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_OBJECT_SUBSYSTEM_ON_ENVIRONMENT}")
mgl_itest_add_refused_mask_control("DirectGLES.SubsystemMaskRefusal.ObjectSubsystemControlOff" "0x1ff" "ObjectSubsystemControlScenario.ClearingTheP4aBitsStopsTheEmissionsAndNotThePixels"
"${MGL_ITEST_GLES_OBJECT_SUBSYSTEM_OFF_ENVIRONMENT}")
mgl_itest_add_refused_mask_control("DirectGLES.SubsystemMaskRefusal.ObjectSubsystemControlRefused" "0x9ff" "ObjectSubsystemControlScenario.ASamplerBitWithoutTheTextureBitIsRefusedAndNamed"
"${MGL_ITEST_GLES_OBJECT_SUBSYSTEM_REFUSED_ENVIRONMENT}")
mgl_itest_add_refused_mask_control("DirectGLES.SubsystemMaskRefusal.ObjectSubsystemControlRefusedTexture" "0x5ff" "ObjectSubsystemControlScenario.ATextureBitWithoutTheSamplerBitIsRefusedAndNamed"
"${MGL_ITEST_GLES_OBJECT_SUBSYSTEM_REFUSED_TEXTURE_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.ObjectSubsystemControl.Consumer." "ObjectSubsystemControlScenario.TheAppliersNoConsumerBeltNeverFiresBehindTheClientsGate"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_GLES_OBJECT_SUBSYSTEM_CONSUMER_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.ObjectSubsystemControl.NoConsumer." "ObjectSubsystemControlScenario.TheAppliersNoConsumerBeltNeverFiresBehindTheClientsGate"
LABELS "integration-gpu\;integration-monolith-control" ENV "${MGL_ITEST_VULKAN_OBJECT_SUBSYSTEM_NO_CONSUMER_ENVIRONMENT}")
# --- Texture upload shape gate (ARCHITECTURE.md §6) ----------------------------------------
# SSIM cannot tell one union box from many rects, so TextureUploadShapeScenario checks the
# server's tex[emit= box= rect= jobs=] and the client's emit[ctu=] against a measured gold row.
# One texture in the counted window is written through a glTextureView, so
# MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1 is required; without it the case skips rather than run a
# smaller workload against the same gold row. Private log path and single-case filter:
# ARCHITECTURE.md §16.1.
mgl_itest_join_environment(MGL_ITEST_GLES_TEXTURE_UPLOAD_SHAPE_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_TEXTURE_UPLOAD_SHAPE_LANE=1"
"MOBILEGL_PIPE_PUSH=0x3fff" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/texture-upload-shape-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.TextureUploadShape." "TextureUploadShapeScenario.TheEmittedUploadShapeIsRecordedAndTheTwoSidesAgree"
LOCK texture-upload-shape-DirectGLES.log ENV "${MGL_ITEST_GLES_TEXTURE_UPLOAD_SHAPE_ENVIRONMENT}")
# --- TextureParamsWithoutASamplerViewScenario needs no lane of its own ---------------------
# Its observable is a sampled colour, not a counter or a log, and its claim is about the shipping
# configuration, so the ambient DirectGLES. / DirectVulkan. registrations at the default mask are
# the arms it needs. The DirectVulkan entries skip in SetUp: the scenario covers Espryt's
# SyncAttachmentObject / SyncNeccessaryTextures pair.
if (MOBILEGL_PIPE_VERIFY)
# 900s, not the ambient 120: the comparator re-reads every field of the fill mask at the verb
# boundary and again at every accessor read, which the design budgets at 5-10x.
set(MGL_ITEST_VERIFY_TIMEOUT 900)
mgl_itest_join_environment(MGL_ITEST_GLES_VERIFY_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-DirectGLES.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_VERIFY_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-DirectVulkan.log"
${MGL_ITEST_VULKAN_ENV})
# The arming assertion's own lane, one case per backend, with a log path nothing else writes to.
#
# PipeVerifyArmingScenario.Armed reads the library's log, and the log is a per-LANE resource: it
# is opened fopen(path, "w"), so every process in a lane truncates it. In the ambient Verify.
# lane that is 400-odd processes on one path, run `-j 4` in CI, and a whole-file read there
# races a neighbour's bring-up. Every other log-reading scenario in this file (UnlocatedIoBlocks,
# the primgen reroute, the point-size demotion) is registered exactly like this for the same
# reason. MGITEST_PIPE_ARMING_LANE is a harness marker - the library never reads it - and it is
# what makes the case skip in the ambient lane instead of racing there.
mgl_itest_join_environment(MGL_ITEST_GLES_VERIFY_ARMING_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_VERIFY=1" "MGITEST_PIPE_ARMING_LANE=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-arming-DirectGLES.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_VERIFY_ARMING_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_PIPE_VERIFY=1" "MGITEST_PIPE_ARMING_LANE=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-arming-DirectVulkan.log"
${MGL_ITEST_VULKAN_ENV})
# Negative control A (G4). MOBILEGL_PIPE_VERIFY_FATAL=0 so the process SURVIVES its own
# divergence and the case can read the report back out of the log; the CI step that exports
# the same corruption against the ambient lane, where FATAL keeps its default of 1, asserts
# the other half - that a divergence aborts and reds the entry.
mgl_itest_join_environment(MGL_ITEST_GLES_VERIFY_CORRUPT_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters" "MOBILEGL_PIPE_VERIFY_FATAL=0"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-corrupt-DirectGLES.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_VERIFY_CORRUPT_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters" "MOBILEGL_PIPE_VERIFY_FATAL=0"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-corrupt-DirectVulkan.log"
${MGL_ITEST_VULKAN_ENV})
# Negative control B (G5). The omission skips the STAMP of one field for one verb while still
# copying its value, which is indistinguishable from a fill row nobody wrote; the scenario
# forks, so the resulting std::abort() is a datum in waitpid() rather than a dead lane.
# P13 W5: the pair is the pack state at ReadPixels, which monolith's record arm still reads from
# the fill (around its neutral-pack readback). GetActiveTextureUnit at GenerateMipmap was the
# frontend arm's read; the record arm resolves the texture from the verb's own handle and never
# reads it, so that pair could no longer go red for its own reason.
mgl_itest_join_environment(MGL_ITEST_GLES_POISON_OMIT_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_PIPE_POISON_OMIT=ReadPixels:GetPixelStoreParameters"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-poison-omit-DirectGLES.log"
${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_POISON_OMIT_ENVIRONMENT
"MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_PIPE_VERIFY=1"
"MOBILEGL_PIPE_POISON_OMIT=ReadPixels:GetPixelStoreParameters"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-poison-omit-DirectVulkan.log"
${MGL_ITEST_VULKAN_ENV})
# The whole suite again, per backend, with the comparator armed. Same scenarios, same
# assertions, but every backend read of frontend state is now checked against a snapshot taken
# from the live context at the verb boundary - which is what "the 742 integration entries
# prove push equals pull" means. Labelled integration-gpu as well so a verify build's
# `ctest -L integration-gpu` still describes the whole registration set.
mgl_itest_lane("DirectGLES.Verify." "-${MGL_ITEST_GLES_VIEW_FILTER}"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_VERIFY_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.Verify." "${MGL_ITEST_GLES_VIEW_FILTER}"
LABELS "integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_VERIFY_ENVIRONMENT}\;MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1")
gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "DirectVulkan.Verify."
DISCOVERY_TIMEOUT 30
PROPERTIES
LABELS "integration-gpu\;integration-verify\;integration-monolith-control"
TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT}
ENVIRONMENT "${MGL_ITEST_VULKAN_VERIFY_ENVIRONMENT}"
)
# The arming assertion, one entry per backend. This is the entry that fails a lane whose library
# never armed: it runs the same library and the same MOBILEGL_PIPE_VERIFY=1 as the ambient
# entries above, but unlike them it cannot be green against a library with no comparator
# compiled in. Its log is its own, so `-j 4` cannot make it flake.
mgl_itest_lane("DirectGLES.VerifyArming." "PipeVerifyArmingScenario.Armed"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_VERIFY_ARMING_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.VerifyArming." "PipeVerifyArmingScenario.Armed"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_VULKAN_VERIFY_ARMING_ENVIRONMENT}")
# One case each: the knobs are process-wide, so a corrupted or poisoned process cannot also be
# running the ambient assertions. These four entries are the ones that assert the RED - they
# pass when the comparator and the poison report, and go red when either stops.
mgl_itest_lane("DirectGLES.VerifyCorrupted." "PipeVerifyArmingScenario.CorruptedFieldIsReported"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_VERIFY_CORRUPT_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.VerifyCorrupted." "PipeVerifyArmingScenario.CorruptedFieldIsReported"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_VULKAN_VERIFY_CORRUPT_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.PoisonOmitted." "PoisonOmissionScenario.OmittedFieldAbortsOnThatVerb"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_GLES_POISON_OMIT_ENVIRONMENT}")
mgl_itest_lane("DirectVulkan.PoisonOmitted." "PoisonOmissionScenario.OmittedFieldAbortsOnThatVerb"
LABELS "integration-gpu\;integration-verify\;integration-monolith-control" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} ENV "${MGL_ITEST_VULKAN_POISON_OMIT_ENVIRONMENT}")
endif()
# --- Split lanes and the persistent-map arm lanes ------------------------------------------
# Split entries are registered only in a MOBILEGL_BUILD_DISAGGREGATED build and carry a second
# label, so `ctest -L integration-split --no-tests=error` fails in any other build instead of
# passing with no entries (ARCHITECTURE.md §16.1). A Split case arms from the running process,
# not from source text that an edit could change: Harness/SplitRuntimePeek.cpp reads
# MG_Config::Transport, ClientSession::Active() and ImplementedVerbCount(), and the case skips
# naming the first that does not hold. The record-arm half of the peek (dataArmIsRecord) needs no
# transport, so it is compiled in every build.
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_RECORD_ARM_PEEK=1)
if (MOBILEGL_BUILD_DISAGGREGATED)
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_SPLIT_RUNTIME_PEEK=1)
message(STATUS "Integration tests: the DirectGLES.Split. lanes arm from the RUNNING PROCESS - "
"MG_Config::Transport, ClientSession::Active() and ImplementedVerbCount() - and "
"skip naming the first of those that is not true")
endif()
# Q-3, made a build fact rather than documentation. ConfigLoader logs the resolved transport at
# INFO, and that line is what run_trace_case.cmake reads back as proof a retrace really went
# split. At WARN or above it is compiled out and every split retrace reds for a reason that is not
# a defect; at DEBUG, ConfigLoader's unconditional env dump prints a confusable KEY=VALUE line in
# a PULL build too (review M-5, also fixed on the reading side). The integration lanes themselves
# no longer depend on the log level at all - they read the variable - so this is a WARNING.
if (MOBILEGL_BUILD_DISAGGREGATED AND DEFINED MOBILEGL_LOG_ACTIVE_LEVEL
AND NOT MOBILEGL_LOG_ACTIVE_LEVEL STREQUAL "MOBILEGL_LOG_LEVEL_INFO"
AND NOT MOBILEGL_LOG_ACTIVE_LEVEL STREQUAL "MOBILEGL_LOG_LEVEL_DEBUG")
message(WARNING "Integration tests: MOBILEGL_LOG_ACTIVE_LEVEL=${MOBILEGL_LOG_ACTIVE_LEVEL} in a "
"disaggregated build. ConfigLoader's transport line is MGLOG_I, so the "
"trace-replay split arm's transport-resolution assertion cannot see it and every "
"split retrace will red. Use MOBILEGL_LOG_LEVEL_INFO.")
endif()
# Package b1's client-side persistent-map tracker, which is what Harness/PersistentMapPeek.cpp
# asks the membership question of (b1-v1.md 4.1 item 2). Every library builds it since P13 W5 (the
# tracker is record core, MG_Record); the content probe stays so a tree without it skips by name.
mgl_itest_probe_for_symbol(MGL_ITEST_PERSISTENT_MAP_TRACKER
"${MGL_ITEST_ROOT}/MobileGL/MG_State/GLState/BufferState" "IsLivePersistentMap")
if (MGL_ITEST_PERSISTENT_MAP_TRACKER)
message(STATUS "Integration tests: package b1's persistent-map tracker is present "
"(${MGL_ITEST_PERSISTENT_MAP_TRACKER}) - PersistentCoherentMapScenario's "
"membership assertion is live")
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_PERSISTENT_MAP_TRACKER=1)
else()
message(STATUS "Integration tests: no source under MobileGL/MG_State/GLState/BufferState names "
"IsLivePersistentMap - package b1's tracker has not landed, so "
"PersistentCoherentMapScenario's membership assertion SKIPS")
endif()
# ARCHITECTURE.md §15.1 - every new ctest ENVIRONMENT and add_trace_replay_test's SPLIT branch
# carries MOBILEGL_IPC_SERVER_PATH, because the dladdr fallback cannot find the server from a
# binary that links MobileGL_s statically. P5 is inproc-only and nothing reads the value yet; it
# is carried now so that an unparsed variable and a parsed-and-ignored one stop being
# indistinguishable the day P6 consumes it. A generator expression is deliberately NOT used here:
# a $<TARGET_FILE_DIR:...> inside a gtest_discover_tests PROPERTIES value is written into the
# generated ctest include file verbatim and never expanded.
set(MGL_ITEST_SPLIT_SERVER_PATH "${CMAKE_BINARY_DIR}/libMobileGLServer.so")
# The shared GLES split, spawn and tcp environments carry MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1:
# Espryt advertises GL_ARB_texture_view only with it, and without it the texture-view scenarios
# skip on every arm. The DirectVulkan lists leave it out because only Espryt reads it.
# MOBILEGL_ITEST_REQUIRE_GPU=1 makes a failed harness pre-flight (such as a handshake-time Fatal)
# fail every case instead of skipping it, since ctest counts a skip as a pass
# (ARCHITECTURE.md §16.1).
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
# The persistent-map counting pair: this monolith entry and DirectGLES.Split.PersistentMapArm.
# further down run the same workload and assert the same mpr constant, so the split arm's count
# equals the monolith arm's while each half can fail on its own. The monolith entry declares no
# arm (which arm a small coherent map takes depends on driver and build; the case records it);
# the split entry declares the emulated one. MOBILEGL_TRANSPORT=monolith is pinned so a job-level
# transport export cannot turn this half into a split entry. Private log, single case and
# RESOURCE_LOCK: ARCHITECTURE.md §16.1.
mgl_itest_join_environment(MGL_ITEST_GLES_PMAP_ARM_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_PMAP_LANE=1" "MOBILEGL_TRANSPORT=monolith"
"MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/persistent-map-arm-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.PersistentMapArm." "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares"
LOCK persistent-map-arm-DirectGLES.log ENV "${MGL_ITEST_GLES_PMAP_ARM_ENVIRONMENT}")
if (MOBILEGL_BUILD_DISAGGREGATED)
# The spawn arm. mgl_itest_register_split_arms emits the inproc, spawn and tcp arms from one
# filter, so their case-name sets stay identical (scripts/ci/spawn_lane_parity.py checks it).
# MOBILEGL_TRANSPORT=spawn makes the test process the client and has it launch the server from
# MOBILEGL_IPC_SERVER_PATH. The server inherits this environment minus the transport and IPC
# keys (ServerSpawn.cpp), so the texture-view opt-in (see MGL_ITEST_GLES_SPLIT_ENVIRONMENT)
# reaches both processes.
mgl_itest_join_environment(MGL_ITEST_GLES_SPAWN_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
# A single supervisor serves one session at a time. Fixture teardown also reaps a
# child left behind by a failed client; the resource lock serializes this lane.
# Below Linux's ephemeral range (32768-60999): thousands of loopback client connections run
# in this lane, and an outgoing socket that happens to own the port makes the supervisor's
# bind fail (TcpServer.Start.* red in 0.07 s on a hosted runner).
set(MOBILEGL_ITEST_TCP_ENDPOINT "tcp://127.0.0.1:30613" CACHE STRING "Loopback TCP integration endpoint")
set(MOBILEGL_ITEST_TCP_DEVICE_ENDPOINT "" CACHE STRING "Optional device TCP integration endpoint")
set(MOBILEGL_ITEST_TCP_DEVICE_TOKEN "" CACHE STRING "Token for the device TCP integration endpoint")
find_package(Python3 REQUIRED COMPONENTS Interpreter)
set(MGL_TCP_FIXTURE "${CMAKE_SOURCE_DIR}/scripts/ci/tcp_server_fixture.py")
set(MGL_TCP_STATE "${CMAKE_CURRENT_BINARY_DIR}/tcp-server.json")
add_test(NAME TcpServer.Start COMMAND "${Python3_EXECUTABLE}" "${MGL_TCP_FIXTURE}"
start --server "${MGL_ITEST_SPLIT_SERVER_PATH}" --endpoint "${MOBILEGL_ITEST_TCP_ENDPOINT}"
--state "${MGL_TCP_STATE}")
add_test(NAME TcpServer.Stop COMMAND "${Python3_EXECUTABLE}" "${MGL_TCP_FIXTURE}"
stop --state "${MGL_TCP_STATE}")
# TcpServer.Start pre-starts the tcp arm's server, so it does not inherit a client entry's
# environment. tcp_server_fixture.py passes ctest's environment to the supervisor minus its
# transport and backend keys, so every knob the server reads is set on this entry: the
# texture-view opt-in and the MGL_ITEST_VULKAN_ENV pins (ICD, lavapipe repair) that Magma reads
# in the server. Without them the Tcp arm skips texture-view cases or misses goldens that the
# Split and Spawn arms pass. Espryt's server ignores the Vulkan pins.
set(mglItestTcpServerEnvironment "MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1")
foreach(mglItestVulkanPin IN LISTS MGL_ITEST_VULKAN_ENV)
if(NOT mglItestVulkanPin IN_LIST MGL_ITEST_COMMON_ENV)
list(APPEND mglItestTcpServerEnvironment "${mglItestVulkanPin}")
endif()
endforeach()
set_tests_properties(TcpServer.Start PROPERTIES FIXTURES_SETUP mobilegl-tcp TIMEOUT 20
ENVIRONMENT "${mglItestTcpServerEnvironment}")
set_tests_properties(TcpServer.Stop PROPERTIES FIXTURES_CLEANUP mobilegl-tcp TIMEOUT 20)
add_test(NAME TcpLane.AccountingControls COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/ci/tcp_lane_tools_test.py")
set_tests_properties(TcpLane.AccountingControls PROPERTIES LABELS unit TIMEOUT 20)
# P7 wave 0. scripts/ci/tcp_supervisor_smoke.py has been in the tree since P6.5 asserting the
# supervisor's Busy / token / wire-version / build-policy controls, and was referenced by
# NOTHING - no ctest entry, no workflow step - so nobody could have said whether those four
# controls were green. It also now carries Ph slice (1)'s negative control: a session child
# killed with SIGABRT must be NAMED in the supervisor log by pid and signal, and the next
# Hello must still be welcomed. It starts its own supervisor on its own free port, so it
# needs no fixture, but it takes the lane's resource lock because a second supervisor's
# session child would answer the other's peers with Busy and make both lanes flaky.
# It needs flatc, which is deliberately not in the build graph (gen_protocol.py's header):
# with none found the script exits 77 and this entry reports SKIP rather than passing on a
# test that did not run.
add_test(NAME TcpLane.SupervisorProtocolControls COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/ci/tcp_supervisor_smoke.py"
--server "${MGL_ITEST_SPLIT_SERVER_PATH}"
--out "${CMAKE_CURRENT_BINARY_DIR}/tcp-supervisor-smoke")
set_tests_properties(TcpLane.SupervisorProtocolControls PROPERTIES
LABELS integration-tcp TIMEOUT 180 SKIP_RETURN_CODE 77
RESOURCE_LOCK mobilegl-tcp)
# Ph fuzz arm 3 (PH-6, ID-P7-2): A PEER THAT STOPS DRAINING SEG_EVENT, on both data planes.
# MG_Test/Wire/EventForfeitPeerTest.cpp starts its own `--serve` supervisor (an abstract unix
# endpoint for the shm plane, a free loopback port for the stream plane) with a NON-default
# MOBILEGL_IPC_EVENT_WAIT_MS, and asserts the whole arm: the server forfeits the reverse
# channel by name against that knob, counts the drop, ends the session with exit 0 inside the
# knob rather than the old 30 s Fatal, and the SAME supervisor welcomes the next connection.
# Each entry also runs the kill-the-peer case (knob 8000, over ServerLoop's 5000 ms join: the
# server must say PeerGone at once and exit 0), and the shm entry the LaunchServer case (the
# knob must cross ServerSpawn.cpp's scrub). The gtest filters are per plane. The names are not
# `DirectGLES.<Arm>.<Scenario>` on purpose: spawn_lane_parity.py compares the scenario arms and
# this is a supervisor control, one per lane, like the entry above. It needs no fixture (its
# supervisor is its own), and the CI lane names both entries to junit_tally.py
# --require-entry-passed so a missing build or a skip is red by name. The Tcp entry still takes
# the lane's resource lock, as the entry above does: every integration-tcp entry must, or
# spawn_lane_parity.py and junit_tally.py --discovery refuse the registration.
if (TARGET EventForfeitPeerTest)
# A plain list, not mgl_itest_join_environment's escaped one: that form is for values that
# travel through gtest_discover_tests' PROPERTIES, and these two entries are add_test's.
set(MGL_ITEST_EVENT_FORFEIT_ENVIRONMENT
"MOBILEGL_TEST_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" ${MGL_ITEST_COMMON_ENV})
add_test(NAME SpawnLane.EventForfeitPeer COMMAND $<TARGET_FILE:EventForfeitPeerTest>
--gtest_filter=EventForfeitPeer.Shm*)
set_tests_properties(SpawnLane.EventForfeitPeer PROPERTIES
LABELS integration-spawn TIMEOUT 120
ENVIRONMENT "${MGL_ITEST_EVENT_FORFEIT_ENVIRONMENT}")
add_test(NAME TcpLane.EventForfeitPeer COMMAND $<TARGET_FILE:EventForfeitPeerTest>
--gtest_filter=EventForfeitPeer.Stream*)
set_tests_properties(TcpLane.EventForfeitPeer PROPERTIES
LABELS integration-tcp TIMEOUT 120 RESOURCE_LOCK mobilegl-tcp
ENVIRONMENT "${MGL_ITEST_EVENT_FORFEIT_ENVIRONMENT}")
endif()
# P7 F2: Ph fuzz arm 1 (PLAN-PH-P34B-P7.md §1.1, "gate: fuzz arm 1 malformed control
# frames"). Five malformed control frames - bad identifier, truncated length prefix, oversize
# length, garbage flatbuffer, wrong union type - sent to a live supervisor as a FIRST frame
# (answered by name with no child forked, PH-7 (5)) and inside an established session (the
# session ends with a named line and is not counted as a fault), with a well-formed client
# welcomed after each phase. Same shape as TcpLane.SupervisorProtocolControls: its own supervisor on its own
# port, the lane's resource lock, flatc or SKIP (77) - and CI requires it PASSED by name.
add_test(NAME TcpLane.ControlFrameFuzz COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_SOURCE_DIR}/scripts/ci/ph_fuzz_control_frames.py"
--server "${MGL_ITEST_SPLIT_SERVER_PATH}"
--out "${CMAKE_CURRENT_BINARY_DIR}/tcp-control-frame-fuzz")
set_tests_properties(TcpLane.ControlFrameFuzz PROPERTIES
LABELS integration-tcp TIMEOUT 180 SKIP_RETURN_CODE 77
RESOURCE_LOCK mobilegl-tcp)
# D2: the texture-view opt-in on both TCP lists too, so the three shared GLES split arms
# carry one configuration. See the note over MGL_ITEST_GLES_SPLIT_ENVIRONMENT.
#
# THE TWO TCP LISTS ARE THE CLIENT'S HALF ONLY, and the two arms differ in whether that is
# enough. The loopback Tcp arm's server is started by the TcpServer.Start fixture below,
# which this package also gives the knob - so both halves have it. The TcpDevice arm's
# server is a supervisor already running ON A DEVICE, started outside this build by whoever
# set MOBILEGL_ITEST_TCP_DEVICE_ENDPOINT; nothing here can reach its environment. If that
# supervisor was started without MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1 it will withhold
# GL_ARB_texture_view and every texture-view case in the TcpDevice arm will GREEN-SKIP,
# exactly as the loopback arm did before the fixture entry got the knob. The arm is
# unconfigured in this repo (the endpoint defaults empty, so the arm is not created at all),
# so this is a note for whoever configures it rather than a defect today.
mgl_itest_join_environment(MGL_ITEST_GLES_TCP_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT}" "MOBILEGL_IPC_DATA=stream"
"MOBILEGL_IPC_REQUIRE_SAME_BUILD=1" "MOBILEGL_IPC_LOG_FORWARD=1" "MOBILEGL_IPC_TOKEN="
"MGITEST_SPLIT_LANE=1" "MGITEST_TCP_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_GLES_TCPDEVICE_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_DEVICE_ENDPOINT}" "MOBILEGL_IPC_DATA=stream"
"MOBILEGL_IPC_REQUIRE_SAME_BUILD=1" "MOBILEGL_IPC_LOG_FORWARD=1"
"MOBILEGL_IPC_TOKEN=${MOBILEGL_ITEST_TCP_DEVICE_TOKEN}"
"MGITEST_SPLIT_LANE=1" "MGITEST_TCP_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
# ---- The same three arms for Magma ----------------------------------------------------
# They append ${MGL_ITEST_VULKAN_ENV} rather than the common list, so they run on the same ICD
# and lavapipe repair as the monolith DirectVulkan. arm instead of whatever ICD the loader
# finds first. MOBILEGL_ITEST_REQUIRE_GPU=1 makes a host without usable Vulkan fail rather than
# skip every entry (ARCHITECTURE.md §16.1).
mgl_itest_join_environment(MGL_ITEST_VULKAN_SPLIT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_TRANSPORT=inproc"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_SPAWN_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_TCP_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT}" "MOBILEGL_IPC_DATA=stream"
"MOBILEGL_IPC_REQUIRE_SAME_BUILD=1" "MOBILEGL_IPC_LOG_FORWARD=1" "MOBILEGL_IPC_TOKEN="
"MGITEST_SPLIT_LANE=1" "MGITEST_TCP_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
# THE TRANSPORT DIMENSION ON ITS OWN, for the Magma lanes further down this file that already
# have an environment of their OWN FLAVOUR (strict + dual-block state, run-ahead credits,
# cache pinning) and need only the transport half re-stated per arm. Kept beside the joined
# lists above so there is ONE place that says what "the spawn arm" and "the tcp arm" mean.
set(MGL_ITEST_SPLIT_ARM_TRANSPORT_ENV "MOBILEGL_TRANSPORT=inproc"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}")
set(MGL_ITEST_SPAWN_ARM_TRANSPORT_ENV "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}")
set(MGL_ITEST_TCP_ARM_TRANSPORT_ENV "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT}" "MOBILEGL_IPC_DATA=stream"
"MOBILEGL_IPC_REQUIRE_SAME_BUILD=1" "MOBILEGL_IPC_LOG_FORWARD=1" "MOBILEGL_IPC_TOKEN="
"MGITEST_TCP_LANE=1")
# mgl_itest_magma_arm_shape(<Split|Spawn|Tcp> [words...]) sets, for a hand-laid Magma lane:
# mglItestArmLower split | spawn | tcp
# mglItestArmLogTag "" on Split, "-spawn" / "-tcp" otherwise, for private log names
# mglItestArmProperties the tcp fixture and resource lock; empty otherwise
# mglItestArmTransportEnv the arm's transport pins, appended to the lane's own environment
# mglItestMagmaLabels integration-magma-<arm> plus one label per word: Split keeps
# integration-magma-<word>, the others integration-magma-<arm>-<word>
# with the arm first (ARCHITECTURE.md §16.1)
macro(mgl_itest_magma_arm_shape mglItestArm)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
set(mglItestArmProperties)
set(mglItestArmLogTag "")
set(mglItestArmTransportEnv ${MGL_ITEST_SPLIT_ARM_TRANSPORT_ENV})
if("${mglItestArm}" STREQUAL "Spawn")
set(mglItestArmTransportEnv ${MGL_ITEST_SPAWN_ARM_TRANSPORT_ENV})
set(mglItestArmLogTag "-spawn")
elseif("${mglItestArm}" STREQUAL "Tcp")
set(mglItestArmTransportEnv ${MGL_ITEST_TCP_ARM_TRANSPORT_ENV})
set(mglItestArmLogTag "-tcp")
set(mglItestArmProperties FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp)
endif()
set(mglItestMagmaLabels "integration-magma-${mglItestArmLower}")
foreach(mglItestWord IN ITEMS ${ARGN})
if("${mglItestArm}" STREQUAL "Split")
set(mglItestMagmaLabels "${mglItestMagmaLabels}\\;integration-magma-${mglItestWord}")
else()
set(mglItestMagmaLabels
"${mglItestMagmaLabels}\\;integration-magma-${mglItestArmLower}-${mglItestWord}")
endif()
endforeach()
endmacro()
set(MGL_ITEST_MAGMA_ARMS Split Spawn Tcp)
# scenarioFilter, a PREFIX TAIL (empty for most; "F1." and "Ct." for the two per-slot loops,
# which name their sub-lane in the test name), then zero or more EXTRA LABEL STEMS. A stem is
# suffixed with the arm's own name (`integration-clientarrays` becomes `-split` on one arm and
# `-spawn` on the other), so a lane-scoped label cannot accidentally name both lanes at once.
# A UNIQUE LIST VARIABLE PER CALL, and the names collected for SplitLogPaths to walk.
#
# gtest_discover_tests' TEST_LIST is not a configure-time value: the generated include file
# sets it when CTEST runs. So one shared variable would hold only the LAST block's entries -
# measured, and it is why the first version of this macro silently unhooked every private log
# path but one. The counter gives each call its own name; the names are what get walked.
set(MGL_SPLIT_ARM_TESTS "")
set(MGL_SPLIT_ARM_TEST_LISTS "")
set(MGL_SPLIT_ARM_INDEX 0)
set(MGL_SPLIT_ARM_CALLS "")
# mgl_itest_register_split_arms_for_backend registers one filter on every transport arm.
# DirectGLES entries carry the gated labels: integration-gpu, integration-<arm> and the
# caller's stems suffixed with the arm. DirectVulkan entries carry only
# integration-magma-all-<arm>, an informational tier outside every CI gate, with no extra stems
# (they would collide with the DirectGLES labels) and no TcpDevice arm; Magma's gated lanes are
# laid out by hand below. `all` sits mid-label so `ctest -L integration-magma-<arm>` cannot
# select this tier (ARCHITECTURE.md §16.1). mgl_itest_generic_split_filter leaves out
# configuration-specific cases, which run only in their pinned entries.
function(mgl_itest_generic_split_filter input backend output)
set(excluded
LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip
LargeArenaAdoptionScenario.AStreamSessionRefusesTheAdoptTierOnceByName
LargeArenaAdoptionScenario.ASessionThatCannotRunT0FallsBackToT2OnceByName
FencePollScenario.PacedFrameFenceWaitsBench
GenerateMipmapServerScenario.Rgb16fDrawnBaseWithARefusedReadbackDeclinesByName
GenerateMipmapServerScenario.Rgb16fUploadedBaseWithARefusedReadbackIsFilteredFromTheStore
ViewportArrayScenario.WithoutTheEmulationEveryIndexCollapsesOntoViewportZero
UnlocatedIoBlockScenario.TheEmulationIsActuallyArmedWhenTheEnvironmentPinsItOn
PointSizeDemotionScenario.TheDemotionIsActuallyArmedWhenTheEnvironmentPinsItOn
WireMipShapeScenario.Depth1DChainWithoutANativeBlitDeclinesByName)
if(backend STREQUAL "DirectGLES")
list(APPEND excluded
PointSizeDemotionScenario.ACaptureSurvivesAStageThatOnlyReadsThePointSize
WireIndirectDrawScenario.ShaderWrittenCommandsWithoutAnApplicationBarrierAreOrdered
WireIndirectDispatchScenario.ShaderWrittenGroupCountsWithoutAnApplicationBarrierAreOrdered)
endif()
list(JOIN excluded ":" excluded)
if(input MATCHES "-")
set(${output} "${input}:${excluded}" PARENT_SCOPE)
else()
set(${output} "${input}-${excluded}" PARENT_SCOPE)
endif()
endfunction()
macro(mgl_itest_register_split_arms_for_backend mglItestBackend scenarioFilter prefixTail)
# QUOTED DEREFERENCE, NOT A BARE NAME: a macro parameter is a textual substitution and
# not a variable, so `if(mglItestBackend STREQUAL ...)` would compare the literal string
# "mglItestBackend" and take the else branch on every call.
if("${mglItestBackend}" STREQUAL "DirectGLES")
set(mglItestEnvStem GLES)
set(mglItestLaneStem "integration")
set(mglItestSharedLabels "integration-gpu\\;")
set(mglItestArms Split Spawn Tcp)
if(MOBILEGL_ITEST_TCP_DEVICE_ENDPOINT)
list(APPEND mglItestArms TcpDevice)
endif()
else()
set(mglItestEnvStem VULKAN)
set(mglItestLaneStem "integration-magma-all")
set(mglItestSharedLabels "")
set(mglItestArms Split Spawn Tcp)
endif()
foreach(mglItestArm IN LISTS mglItestArms)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
set(mglItestArmProperties)
if(mglItestArm STREQUAL "Tcp")
set(mglItestArmProperties FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp)
elseif(mglItestArm STREQUAL "TcpDevice")
set(mglItestArmLower tcp-device)
set(mglItestArmProperties RESOURCE_LOCK mobilegl-tcp-device)
endif()
set(mglItestArmLabels "${mglItestSharedLabels}${mglItestLaneStem}-${mglItestArmLower}")
foreach(mglItestExtra IN ITEMS ${ARGN})
set(mglItestArmLabels "${mglItestArmLabels}\\;${mglItestExtra}-${mglItestArmLower}")
endforeach()
# ONE ACCUMULATED LIST FOR BOTH ARMS, and it is not bookkeeping: SplitLogPaths gives
# every entry a PRIVATE MOBILEGL_LOG_FILE_PATH, and SplitLogPaths.PrivateAndDistinct
# reds if one is missing or shared. The per-block TEST_LIST variables this macro
# replaced were what fed it - dropping them silently unhooked the whole mechanism,
# which that gate caught. Accumulating here means a block added later is covered
# without anyone remembering to add its variable to the list.
#
# IT MATTERS MORE ON THE SPAWN ARM: there TWO PROCESSES append to the path, so two
# entries sharing one file is not merely ambiguous, it interleaves two sessions.
mgl_itest_generic_split_filter("${scenarioFilter}" "${mglItestBackend}" mglItestGenericFilter)
mgl_itest_lane("${mglItestBackend}.${mglItestArm}.${prefixTail}" "${mglItestGenericFilter}"
LABELS "${mglItestArmLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_${mglItestEnvStem}_${mglItestArmUpper}_ENVIRONMENT}")
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")
endforeach()
endmacro()
# THE NAME EVERY EXISTING CALL SITE USES, unchanged in signature so that this package adds a
# dimension without touching one registration. It also RECORDS its call, which is what makes
# tier 2 structural rather than maintained: the informational Magma tier at the end of this
# file replays the recorded list, so a scenario added to the split arms tomorrow joins the
# Magma census the same day and nobody has to remember a second edit. `-` stands for an empty
# prefix tail, because a CMake list cannot tell "one empty element" from "no elements".
macro(mgl_itest_register_split_arms scenarioFilter prefixTail)
if("${prefixTail}" STREQUAL "")
set(mglItestRecordedTail "-")
else()
set(mglItestRecordedTail "${prefixTail}")
endif()
list(APPEND MGL_SPLIT_ARM_CALLS "${scenarioFilter}|${mglItestRecordedTail}")
mgl_itest_register_split_arms_for_backend(DirectGLES "${scenarioFilter}" "${prefixTail}"
${ARGN})
endmacro()
mgl_itest_register_split_arms("ClearThenReadPixelsScenario.*" "")
mgl_itest_register_split_arms("InPassClearScenario.*" "")
mgl_itest_register_split_arms("ConvertedVertexStreamScenario.*" "")
mgl_itest_register_split_arms("WriteRenameScenario.*" "")
mgl_itest_register_split_arms("VaoShadowScenario.*" "")
# GL_ARB_shader_objects (P13): ARB-created shader / program objects travel the record arm like
# core-created ones, so the scenario runs on every split arm as well as both monoliths.
mgl_itest_register_split_arms("ArbShaderObjectsScenario.*" "")
# P13 W6: the cube-face readback rules the deleted frontend-arm unit cases pinned.
mgl_itest_register_split_arms("CubeFaceReadbackScenario.*" "")
mgl_itest_register_split_arms("TextureUnitSamplingScenario.*" "")
mgl_itest_register_split_arms("TriangleScenario.*" "")
mgl_itest_register_split_arms("PersistentCoherentMapScenario.*" "")
# P11 A2 (docs/archive/contracts/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 assertions are registered only in their configured controls. The Magma arms are the
# gated `Arena.` block beside `Pbo.` / `Fence.` below, plus the Buffers lane.
mgl_itest_register_split_arms("LargeArenaAdoptionScenario.*" "")
# P11 B2 addendum: A BUFFER DELETED WHILE BOUND TO AN INDEXED POINT (storage, uniform, atomic
# counter, transform feedback), then its slot reused at a newer generation and a draw and a
# dispatch that declare the point. The monolith half is the ambient DirectGLES./DirectVulkan.
# entries; Magma's three arms are the `DeletedBound.` block beside `Arena.` below.
mgl_itest_register_split_arms("DeletedBoundBufferScenario.*" "")
# ...and the repro it came from, kept as a regression: the two arena cases in ONE process (the
# harness has one context per process, so the first case's storage binding outlives it). ctest
# runs one case per process everywhere else, which is how this stayed hidden; add_test because
# gtest_discover_tests registers cases one at a time. A PLAIN list for add_test: the joined
# environments are escaped for gtest_discover_tests' PROPERTIES (see SpawnLane.EventForfeitPeer).
foreach(mglItestPairArm IN ITEMS Split Spawn Tcp)
string(TOLOWER "${mglItestPairArm}" mglItestPairArmLower)
string(TOUPPER "${mglItestPairArm}" mglItestPairArmUpper)
set(mglItestPairProperties)
if(mglItestPairArm STREQUAL "Tcp")
set(mglItestPairProperties FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp)
endif()
string(REPLACE "\\;" ";" mglItestPairEnv "${MGL_ITEST_GLES_${mglItestPairArmUpper}_ENVIRONMENT}")
list(APPEND mglItestPairEnv "MGITEST_PERSISTENT_MAP_ARM=emulated"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/split-logs/DirectGLES.${mglItestPairArm}.ArenaPairInOneProcess.log")
set(mglItestPairName "DirectGLES.${mglItestPairArm}.LargeArenaAdoptionScenario.GpuWriteThenTheTierCaseInOneProcess")
add_test(NAME "${mglItestPairName}"
COMMAND MobileGLIntegrationTest
"--gtest_filter=LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack:LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties("${mglItestPairName}" PROPERTIES
LABELS "integration-gpu;integration-${mglItestPairArmLower}"
TIMEOUT ${MGL_ITEST_TIMEOUT}
ENVIRONMENT "${mglItestPairEnv}"
${mglItestPairProperties})
endforeach()
# 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).
mgl_itest_lane("DirectGLES.Tcp.AdoptTier0." "LargeArenaAdoptionScenario.*-LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip:LargeArenaAdoptionScenario.ASessionThatCannotRunT0FallsBackToT2OnceByName"
LABELS "integration-gpu\;integration-tcp" LOCK mobilegl-tcp FIXTURES mobilegl-tcp TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" ENV "${MGL_ITEST_GLES_TCP_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_TIER_REFUSAL=T0")
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")
# P11 B2 (CONTRACT-P11 B2): THE SHARED-SEGMENT ARMS WITH MOBILEGL_IPC_ADOPT_TIER=0, which A1 made
# die at the handshake and B2 turned into an ASK. A host has no AHardwareBuffer (and Espryt cannot
# hand an in-process client its stores), so the server refuses T0 by name at its first native bind, publishes no kCapAdoptT0, and the client falls
# back to T2 with one named line at its first map_persistent: every case green, the arena
# emulated (SplitLogPaths declares it), ASessionThatCannotRunT0FallsBackToT2OnceByName reading
# the two lines. `.AdoptT0Disallowed.` is the same on the spawn arm with the server's allow switch
# off - which also proves MOBILEGL_IPC_ALLOW_ADOPT_T0 survives ServerSpawn's environment scrub.
foreach(mglT0Arm IN ITEMS Split Spawn)
string(TOUPPER "${mglT0Arm}" mglT0ArmUpper)
string(TOLOWER "${mglT0Arm}" mglT0ArmLower)
# Split is one process: Espryt cannot hand its client its stores, and an in-process session
# never takes the AHardwareBuffer arm, so the refusal names that rather than the platform.
if(mglT0Arm STREQUAL "Split")
set(mglT0Fallback "in process")
else()
set(mglT0Fallback "no AHardwareBuffer")
endif()
mgl_itest_lane("DirectGLES.${mglT0Arm}.AdoptTier0." "LargeArenaAdoptionScenario.*-LargeArenaAdoptionScenario.AnAdoptionCostsExactlyOneMapPersistentRoundtrip:LargeArenaAdoptionScenario.AStreamSessionRefusesTheAdoptTierOnceByName"
LABELS "integration-gpu\;integration-${mglT0ArmLower}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" ENV "${MGL_ITEST_GLES_${mglT0ArmUpper}_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_T0_FALLBACK=${mglT0Fallback}")
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")
endforeach()
mgl_itest_lane("DirectGLES.Spawn.AdoptT0Disallowed." "LargeArenaAdoptionScenario.ASessionThatCannotRunT0FallsBackToT2OnceByName:LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares"
LABELS "integration-gpu\;integration-spawn" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" ENV "${MGL_ITEST_GLES_SPAWN_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MOBILEGL_IPC_ALLOW_ADOPT_T0=0\;MGITEST_ADOPT_T0_FALLBACK=disallowed")
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")
# ---- Transform feedback on the split arms (ARCHITECTURE.md §8.5) ----------------------
# The scatter reads the destination's pre-capture bytes from the server's staged shadow, so
# the OrphanedCaptureTarget cases (glBufferData(size, NULL), then a partial or no upload) cover
# a target the shadow cannot fully answer for. The writeback crosses SEG_EVENT in slices, which
# the megabyte-range cases exercise, and XfbRepeatedCaptureScenario's gl_NextBuffer case checks
# the stride and varyings the server takes from the program archive.
mgl_itest_register_split_arms("XfbAfterClipDistanceScenario.*" "Xfb.")
mgl_itest_register_split_arms("XfbRepeatedCaptureScenario.*" "Xfb.")
# ---- P3b/P4b espryt D1 slice 4: THE READBACK FAMILY ON THE SPLIT ARMS -------------------
#
# The 回读 half of the plan's "回读 / pack state" row. The PACK half is done - server reads
# with neutral pack state, the client scatters (ID-49, EmitTables.cpp) - and the READBACK half
# had no split coverage at all, so every claim about it was a reading of the code rather than
# a run of it. Arming the lanes is the first half of the work and the census is the second:
# what reds here IS the remaining worklist, and each red is either D1's to fix or excluded BY
# NAME with the Fatal quoted, the way the d1 draw block excluded
# MultiDrawScenario.ClientSideIndicesBatchMatchesUnrolledDraws until P8-A.
mgl_itest_register_split_arms("PixelStoreSweepScenario.*" "")
mgl_itest_register_split_arms("PackedWordReadbackScenario.*" "")
mgl_itest_register_split_arms("LayeredTextureReadbackScenario.*" "")
# FramebufferChurnScenario shares PixelStoreSweepScenario.cpp and is the other half of its
# story - ~3300 texture+framebuffer pairs per case, the shape that walked an Adreno driver to
# 1.2 GB of leaked objects and then answered every readback with someone else's pixels. Under
# a transport every one of those pairs is also a create/destroy record round trip, so this is
# the only entry in the readback block that says anything about the OBJECT lifetime half.
mgl_itest_register_split_arms("FramebufferChurnScenario.*" "")
# P7 gate 5 (g5-readback): a glReadPixels larger than one SEG_REPLY slot is banded by the
# client instead of Fatal{ReplyTooLarge}. Exactly 2 MiB (the CTS renderbuffers_storage shape),
# 2 MiB + one row, more than the whole 16 MiB segment, a non-neutral pack state, a pack
# buffer, depth and packed depth/stencil, and glGetTexImage's windowed twin. The Magma arms of
# the same cases are the `Band.` block after the Fm lane below.
mgl_itest_register_split_arms("BandedReadbackScenario.*" "")
# P9 (W1, docs/archive/contracts/CONTRACT-P9.md §1): the pack-buffer readback crosses as
# read_pixels_to_buffer / get_texture_image_to_buffer and is answered by nothing. Six parity
# cases (every pack parameter, the map reader, a byte swap, a layered glGetTexImage, an upload
# FROM the buffer the read filled, a later write over part of it) and the wire case (no
# reply-slot record, one mark per read). The Magma arms are the `Pbo.` block after the Band
# block below.
mgl_itest_register_split_arms("PackBufferReadbackScenario.*" "")
# P10 (A, docs/archive/contracts/CONTRACT-P10.md §1): fence polls answered from kEventFenceSignaled - the
# idiom's poll loops end (with and without the flush bit), a signaled fence's polls cross
# nothing, and an idle server still reports a fence that finishes. The Magma arms are the
# `Fence.` block beside the `Pbo.` block below.
mgl_itest_register_split_arms("FencePollScenario.*" "")
# DepthStencilReadbackMatrixScenario GETS ITS OWN BLOCK, because it needs a variable the
# registration macro cannot carry: MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1.
#
# Without it this scenario is UNFALSIFIABLE on the machines this suite runs on, and the
# ambient DirectGLES.ForcedDepthStencilEmulation. lane says why: OpenGL ES has no depth or
# stencil readback in core, but llvmpipe accepts the reads anyway, so every case goes
# green through a native path the Adreno device does not have - deleting the whole emulation
# left all of them passing. A split arm that inherited the ambient default would be measuring
# the same non-path three more times. So the pin is MIRRORED here, which is what the plan's
# row asks for.
#
# P8-A: SPLIT AND SPAWN ONLY HERE. The knob is read by the server, and the Tcp arm's server
# is the lane-wide fixture, so a Tcp copy registered here ran the NATIVE readback under the
# ForcedDs. name. The same-named Tcp entries are registered in P8Coverage.cmake, against a
# supervisor started with the knob.
foreach(mglItestDsArm Split Spawn)
mgl_itest_magma_arm_shape(${mglItestDsArm})
string(TOLOWER "${mglItestDsArm}" mglItestDsArmLower)
mgl_itest_join_environment(mglItestDsEnvironment
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MGITEST_SPLIT_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1"
${mglItestArmTransportEnv} ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.${mglItestDsArm}.ForcedDs." "DepthStencilReadbackMatrixScenario.*"
LABELS "integration-gpu\;integration-${mglItestDsArmLower}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestDsEnvironment}")
# Into the accumulated list like every other arm entry, so SplitLogPaths gives each of
# these a private MOBILEGL_LOG_FILE_PATH too - two processes append on the spawn arm.
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")
endforeach()
# ---- AsyncCompileScenario on the split arms, in its three pinned configurations ---------
# Each case needs a whole-process compiler configuration (MOBILEGL_ASYNC_SHADER_COMPILE,
# MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS), so the monolith AsyncOn. / AsyncOff. /
# OptimisticShaderStatus. entries are mirrored per arm; AsyncOn. leaves out the Iris case, which
# has its own entry. Only pinned configurations are registered, so a change of the shipped
# default cannot drop either half. On these arms the server links and draws the worker-built
# program archives. The switch is client state (ReconcileClientCompileExtensions in
# CapsMirror.cpp), so the tcp fixture server not seeing it is part of what the Tcp arm checks.
foreach(mglItestAsyncArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestAsyncArm})
string(TOUPPER "${mglItestAsyncArm}" mglItestAsyncArmUpper)
foreach(mglItestAsyncShape AsyncOn AsyncOff OptimisticShaderStatus)
if(mglItestAsyncShape STREQUAL "AsyncOn")
set(mglItestAsyncFilter
"AsyncCompileScenario.*-AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly")
set(mglItestAsyncKnobs "MOBILEGL_ASYNC_SHADER_COMPILE=1")
elseif(mglItestAsyncShape STREQUAL "AsyncOff")
set(mglItestAsyncFilter "AsyncCompileScenario.ExtensionStringMatchesTheConfiguration")
set(mglItestAsyncKnobs "MOBILEGL_ASYNC_SHADER_COMPILE=0")
else()
set(mglItestAsyncFilter "AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly")
set(mglItestAsyncKnobs
"MOBILEGL_ASYNC_SHADER_COMPILE=1\;MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS=1")
endif()
mgl_itest_lane("DirectGLES.${mglItestAsyncArm}.${mglItestAsyncShape}." "${mglItestAsyncFilter}"
LABELS "integration-gpu\;integration-${mglItestArmLower}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_GLES_${mglItestAsyncArmUpper}_ENVIRONMENT}\;${mglItestAsyncKnobs}")
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")
endforeach()
endforeach()
# ---- The monolith arm inside the gated set --------------------------------------------
# integration-split names the set the disaggregation gate runs, not "runs inproc". A
# disaggregated build serves either arm, chosen at runtime by MG_Config::Transport; the Split
# blocks above all pin inproc and split-only scenarios skip under monolith, so this entry is
# the monolith arm's case in that set. MOBILEGL_TRANSPORT=monolith is pinned so a job-level
# transport export cannot turn it into another split entry (ARCHITECTURE.md §16.1). The
# ambient MonolithAttachmentClearScenario entries stay in integration-gpu.
mgl_itest_join_environment(MGL_ITEST_GLES_PUSH_MONOLITH_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=monolith"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.PushMonolithArm." "MonolithAttachmentClearScenario.*"
LABELS "integration-gpu\;integration-split" TEST_LIST MGL_PUSH_MONOLITH_ATTACHMENT_TESTS ENV "${MGL_ITEST_GLES_PUSH_MONOLITH_ENVIRONMENT}")
# ---- Indexed, instanced, multi-draw and indirect draws on the split arms ----------------
# Every IndexedDrawFamilyScenario case's picture depends on one of these draws, so a record
# that did not cross shows as a wrong picture. MultiDrawScenario runs whole: the client
# flattens every sub-draw's client indices into one owned element buffer before emitting
# (EmitMultiIndexedDraw in MG_Impl/Pipe/Verb/VerbPort.cpp, OwnedDrawInputs.h), and
# ClientSideIndicesBatchMatchesUnrolledDraws fails if a multi-draw with client indices reaches
# the server again (the +CLIENT_INDICES latch in RecordVerbSink.cpp).
set(MGL_SPLIT_D1_SCENARIOS IndexedDrawFamilyScenario MultiDrawScenario DrawParametersScenario
PrimitiveRestartScenario)
set(MGL_SPLIT_D1_FILTER_IndexedDrawFamilyScenario "IndexedDrawFamilyScenario.*")
set(MGL_SPLIT_D1_FILTER_MultiDrawScenario "MultiDrawScenario.*")
set(MGL_SPLIT_D1_FILTER_DrawParametersScenario "DrawParametersScenario.*")
set(MGL_SPLIT_D1_FILTER_PrimitiveRestartScenario "PrimitiveRestartScenario.*")
foreach(mglItestD1Scenario IN LISTS MGL_SPLIT_D1_SCENARIOS)
mgl_itest_register_split_arms("${MGL_SPLIT_D1_FILTER_${mglItestD1Scenario}}" "")
endforeach()
# ---- Multi-draw tiers pinned on the split arm -----------------------------------------
# MultiDrawImpl asks the bound index buffer three questions, and five of the six Espryt tiers
# ask them from a different point in the batch. MOBILEGL_ESPRYT_MULTIDRAW_MODE is read once per
# process (ResolveTierOnce in MultiDraw.cpp) and `auto` resolves to `ext` on the CI drivers,
# which the lanes above cover, so each other tier gets an entry with the mode pinned as a ctest
# ENVIRONMENT property (ARCHITECTURE.md §16.1). Drop a tier the driver refuses: it would run
# `ext` again. Each entry gets a private log under split-logs/ (SplitLogPaths.cmake.in), so the
# tier marker is read from the run that pinned it.
set(MGL_SPLIT_MULTIDRAW_TIERS basevertex multiindirect indirect drawelements compute)
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_basevertex "BaseVertex")
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_multiindirect "MultiIndirect")
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_indirect "Indirect")
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_drawelements "DrawElements")
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_compute "Compute")
set(MGL_SPLIT_MULTIDRAW_TIER_FILTER
"MultiDrawScenario.*:IndexedDrawFamilyScenario.MultiDraw*")
foreach(mglItestMultiDrawTier IN LISTS MGL_SPLIT_MULTIDRAW_TIERS)
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_MULTIDRAW_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ESPRYT_MULTIDRAW_MODE=${mglItestMultiDrawTier}"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.Split.MultiDrawTier${MGL_SPLIT_MULTIDRAW_TIER_PREFIX_${mglItestMultiDrawTier}}." "${MGL_SPLIT_MULTIDRAW_TIER_FILTER}"
LABELS "integration-gpu\;integration-split" TEST_LIST "MGL_SPLIT_MULTIDRAW_${mglItestMultiDrawTier}_TESTS" ENV "${MGL_ITEST_GLES_SPLIT_MULTIDRAW_ENVIRONMENT}")
endforeach()
# ---- Image units, compute, copy-image, atomic counters and storage-block bindings -------
# Each scenario asserts a value that exists only if its verb ran on the apply thread (a texel
# stored through an image unit, a word glCopyImageSubData moved, an atomic counter, a rebound
# storage block), so a declined record or a client that fell through to the driver reads back
# the wrong number. Filters keep only cases that emit records: an armed DirectGLES.Split. case
# must move the client encoder's record ordinal (ScenarioFixture.h), and the other
# ProgramPipelineScenario cases draw nothing.
set(MGL_SPLIT_I1_SCENARIOS
UnboundImageDescriptorScenario # bind_shader_image (72) + launch_grid (60), 13 cases
CopyImageLayeredScenario # resource_copy_region (53), 6 cases
AtomicCounterScenario # launch_grid (60) + memory_barrier (61), 3 cases
ProgramPipelineScenario) # set_storage_block_binding (75), the 2 storage-block cases
set(MGL_SPLIT_I1_FILTER_UnboundImageDescriptorScenario "UnboundImageDescriptorScenario.*")
set(MGL_SPLIT_I1_FILTER_CopyImageLayeredScenario "CopyImageLayeredScenario.*")
set(MGL_SPLIT_I1_FILTER_AtomicCounterScenario "AtomicCounterScenario.*")
set(MGL_SPLIT_I1_FILTER_ProgramPipelineScenario "ProgramPipelineScenario.*StorageBlock*")
foreach(mglItestI1Scenario IN LISTS MGL_SPLIT_I1_SCENARIOS)
mgl_itest_register_split_arms("${MGL_SPLIT_I1_FILTER_${mglItestI1Scenario}}" "")
endforeach()
# ---- P7 gate 5 (g5-imgwin): AN IMAGE UNIT NAMING A (LEVEL, LAYER) ITS TEXTURE LACKS ------
#
# GL 4.6 core 8.26 makes every access through such a unit invalid - zero loads, dropped
# stores - and never an error. Espryt forwards the unit to the driver, which applies the rule
# itself; these are its three arms. The Magma arms of the same scenario are laid by hand in
# the Fm block (`DirectVulkan.<arm>.ImageUnitWindow.`), because that is where the wire arm's
# gated flavour lives.
#
# ALL SIX CASES, THE TEXTURE-VIEW ONE INCLUDED. The view case waited outside the gated arms
# while every glTextureView under a transport ended the session at Fatal{ProtocolCorruption,
# "StagedTextureStore.NoteLevelDefined"} (p7/f2's view descriptor carried internalFormat
# 0xFFFFFFFF); the F2 repair (f2-latch slice A) retired that death, so it joins them here and
# in the Magma ImageUnitWindow foreach. The shared GLES split lists carry
# MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1, so it does not green-skip on these arms.
mgl_itest_register_split_arms("InvalidImageUnitScenario.*" "")
# ---- P9 W2: A TEXTURE RE-MINT NEEDS NO BYTE FROM THE CLIENT ------------------------------
#
# The ROADMAP's `TextureRemintPullScenario`, and it proves the opposite of its name: the
# server re-mints from the GPU level and its own staged store, so no pull protocol exists
# (docs/archive/notes/p9/W2-REMINT.md). Espryt's three arms here; the Magma arms are laid by hand in the
# `DirectVulkan.<arm>.Remint.` block, where the gated Magma flavour lives; both backends' monolith
# entries come from the whole-binary discovery.
mgl_itest_register_split_arms("TextureRemintPullScenario.*" "")
# W2-b's control. MGITEST_ESPRYT_FORCE_REMINT_READBACK_FAILURE is read by the SERVER (the
# promotion in RequireImageBindableStorageByHandle), so it reaches inproc and spawn - the spawned
# server copies this process's environment - and never the tcp lane's shared fixture: split and
# spawn only, the MAGMA_SERVER_ENV_KNOB_NO_TCP reason. The entries carry their own label rather
# than integration-{split,spawn} so the Espryt spawn-parity sets stay knob-free; integration-gpu
# is what runs them in the gate. Only the uploaded-only cases: their levels are covered by the
# store, so the fallback must still produce the right texels and the server log must name it.
foreach(mglItestArm IN ITEMS Split Spawn)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
foreach(remintFallbackCase AnUploadedMutableTextureKeepsItsTexelsAcrossTheImageBind
AWidenedFormatsUploadSurvivesTheCarrierRemint
AnRgba8SnormUploadSurvivesTheImageBind
AnRgba16SnormImmutableUploadSurvivesTheCarrierRemint)
mgl_itest_lane("DirectGLES.${mglItestArm}.RemintFallback." "TextureRemintPullScenario.${remintFallbackCase}"
LABELS "integration-gpu\\;integration-remint-fallback-${mglItestArmLower}" ENV "${MGL_ITEST_GLES_${mglItestArmUpper}_ENVIRONMENT}\;MGITEST_ESPRYT_FORCE_REMINT_READBACK_FAILURE=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p9-remint-fallback-${mglItestArmLower}-${remintFallbackCase}.log")
endforeach()
endforeach()
# ---- P11 M: DEV'S TWO GPU-CONTENT SCENARIOS ON THE SPLIT ARMS ----------------------------
#
# Merged from dev@c60639db with their monolith entries. ImageBindableRemintScenario is the
# monolith twin of TextureRemintPullScenario above (568090f0 made Espryt's frontend re-mint
# read the GPU level back as the server's already did); PartialUploadOverGpuContentScenario is
# 8966d5c2's sub-rect upload over converted/reshaped levels (the server's handle arm cuts the
# region from the conversion's buffer too) plus b1d60523's Magma ordering cases, which are
# plain GL and run here on Espryt as well. The Magma arms are laid by hand in the
# `DirectVulkan.<arm>.GpuContent.` block, where the gated Magma flavour lives.
mgl_itest_register_split_arms("ImageBindableRemintScenario.*" "")
mgl_itest_register_split_arms("PartialUploadOverGpuContentScenario.*" "")
# ---- P11 M2: DEV'S TWO MAGMA SCENARIOS ON THE SPLIT ARMS ---------------------------------
#
# Merged from dev@c14283ee with their monolith entries. Both are plain GL and check pixels on
# every backend: OversizedRecordingSplitScenario draws frames whose state spans a command-buffer
# split (d20c2d01), DescriptorPoolGrowthScenario draws a 4096-distinct-set frame and gives it
# back (c2de6576/53fa16c2). On Espryt neither has anything else to check - the draw budget and
# the pool census are Magma's - so every case runs on all three arms. The Magma arms are laid by
# hand below (`DirectVulkan.<arm>.Oversized.`, `.SplitRecording64.`, `DirectVulkan.Split.DescPool.`),
# where the gated flavour and the per-arm exceptions live.
mgl_itest_register_split_arms("OversizedRecordingSplitScenario.*" "")
# P15 present wait: plain GL, pixel checks on every backend and arm (the frame shapes that
# split around the window pass); the arming assertion stays in DirectVulkan.WindowPassGate.
mgl_itest_register_split_arms("WindowPassGateScenario.*" "")
mgl_itest_register_split_arms("DescriptorPoolGrowthScenario.*" "")
# ---- Transform-feedback spans, the XFB object bind and the patch parameter --------------
# Both scenarios pass only if those records crossed and the backend applied them.
# TessellationXfbCaptureScenario sets GL_PATCH_VERTICES, draws GL_PATCHES into a capture and
# checks the captured bytes: a lost patch parameter changes the capture length, a lost span
# leaves the scenario's poison value. XfbCaptureBufferReuseScenario runs spans over four buffer
# lifetimes, so a span whose end crossed without its begin shows the previous span's bytes.
# On Magma's split arms they run only in the informational integration-magma-all-* and
# integration-magma-full-* tiers.
mgl_itest_register_split_arms("TessellationXfbCaptureScenario.*" "")
mgl_itest_register_split_arms("XfbCaptureBufferReuseScenario.*" "")
# ---- GL_PRIMITIVES_GENERATED and GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN on one span ----
# XfbPrimitiveQueryScenario captures with and without overflow (4 generated, 3 written) and
# reads the records back. Under a transport both answers are server query replies, so a swapped
# count or a result read before the span's end was applied shows as a wrong number.
# PrimitivesGeneratedNoXfbScenario is not registered here: every case skips on DirectGLES.
# The Magma arms of both are the DirectVulkan.<arm>.XfbQuery. / PrimGen. / PrimGenReroute.
# entries further down.
mgl_itest_register_split_arms("XfbPrimitiveQueryScenario.*" "")
# ---- P5e package vi (docs/archive/contracts/CONTRACT-P5E.md §5.1, ID-82 / ruling 2) --------------------
#
# Client-memory vertices and indices now cross as owned ordinary buffers.
# Keep the original names and private logs; all six scenarios join the full
# GPU and split acceptance labels as well as their focused regression label.
mgl_itest_register_split_arms("ClientVertexArrayScenario.*" "" integration-clientarrays)
mgl_itest_register_split_arms("SyncWireScenario.*" "")
# ---- TextureViewAliasScenario: uploads through a texture view, on all three transports --
# The client keys every texture upload on the view's storage owner, and the applier keeps a
# pending-upload set per record, so a flag cleared on the wrong record leaves a server-side
# level without its texels. The server's clean gate reads the owner's record for a view
# (ARCHITECTURE.md §6.1.2), which TheOwnerUploadDrainRunsAgainAfterADrawBoundary checks on
# every arm. Every case draws, so each moves the record ordinal; the arm environments carry
# the texture-view opt-in.
mgl_itest_register_split_arms("TextureViewAliasScenario.*" "")
# ---- TextureUploadShapeScenario on the three transports -------------------------------
# Written out per arm rather than through mgl_itest_register_split_arms: the case needs
# MGITEST_TEXTURE_UPLOAD_SHAPE_LANE=1, the stats switches and a log path private to the arm,
# which the shared arm environments do not carry (ARCHITECTURE.md §16.1). Under spawn and tcp
# the client's emission count and the server's tex[] come from two processes with their own
# counters, so their agreement is a real comparison there; under inproc one process writes two
# role logs, which checks that each role's line is read from its own file.
set(MGL_D2_TUS_FILTER "TextureUploadShapeScenario.TheEmittedUploadShapeIsRecordedAndTheTwoSidesAgree")
foreach(mglD2Arm Split Spawn Tcp)
string(TOLOWER "${mglD2Arm}" mglD2ArmLower)
set(mglD2Log "${CMAKE_CURRENT_BINARY_DIR}/texture-upload-shape-${mglD2ArmLower}-DirectGLES.log")
get_filename_component(mglD2LogName "${mglD2Log}" NAME)
set(mglD2Locks "${mglD2LogName}")
set(mglD2Extra)
if(mglD2Arm STREQUAL "Split")
set(mglD2Transport "MOBILEGL_TRANSPORT=inproc"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}")
elseif(mglD2Arm STREQUAL "Spawn")
set(mglD2Transport "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}")
else()
set(mglD2Transport "MOBILEGL_TRANSPORT=spawn"
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT}"
"MOBILEGL_IPC_DATA=stream" "MOBILEGL_IPC_REQUIRE_SAME_BUILD=1"
"MOBILEGL_IPC_LOG_FORWARD=1" "MOBILEGL_IPC_TOKEN="
"MGITEST_TCP_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1")
set(mglD2Extra FIXTURES_REQUIRED mobilegl-tcp)
# The loopback supervisor serves ONE session (ServerMain.cpp answers a second Hello
# Refuse{Busy}), and every other Tcp entry holds this lock. Without it `ctest -L
# integration-gpu -j N` ran this case beside another Tcp case and whichever client
# connected second died Refuse{Busy} -> Fatal{CapsBeforeFirstSnapshot}.
# scripts/ci/spawn_lane_parity.py and junit_tally.py --discovery now refuse any entry
# on the fixture that does not hold it.
set(mglD2Locks "${mglD2LogName}\;mobilegl-tcp")
endif()
mgl_itest_join_environment(MGL_D2_TUS_ENV_${mglD2Arm}
"MOBILEGL_BACKEND_TYPE=DirectGLES" ${mglD2Transport} "MGITEST_SPLIT_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MGITEST_TEXTURE_UPLOAD_SHAPE_LANE=1" "MOBILEGL_PIPE_PUSH=0x3fff"
"MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1"
"MOBILEGL_LOG_FILE_PATH=${mglD2Log}"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.${mglD2Arm}.TextureUploadShape." "${MGL_D2_TUS_FILTER}"
LABELS "integration-gpu\;integration-${mglD2ArmLower}" LOCK "${mglD2Locks}" PROPERTIES ${mglD2Extra} ENV "${MGL_D2_TUS_ENV_${mglD2Arm}}")
endforeach()
# Cold-start bring-up on the spawn arm. A spawned server brings its native backend up lazily,
# inside the first surface creation (Espryt) or the first MakeCurrent (Magma).
# MOBILEGL_TEST_DELAY_FIRST_BRINGUP_MS holds both ops 6 s, past the 5 s steady reply bound;
# the client must wait them out (those ops run under MOBILEGL_IPC_COLD_START_MS, and each
# Wire::SurfaceProgress report restarts the budget). Unit check: ServerSpawnTest's
# cold-bring-up case. Both entries are expected green, so the Magma one carries integration-gpu
# too; ~25 s each, as the harness's pre-flight child brings a server up as well. Not on
# integration-spawn, whose name set must equal the split lane's (spawn_lane_parity.py).
set(MGL_COLDSTART_FILTER "ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels")
foreach(mglColdBackend DirectGLES DirectVulkan)
if(mglColdBackend STREQUAL "DirectVulkan")
set(mglColdDriverEnv ${MGL_ITEST_VULKAN_ENV})
else()
set(mglColdDriverEnv ${MGL_ITEST_COMMON_ENV})
endif()
mgl_itest_join_environment(MGL_COLDSTART_ENV_${mglColdBackend}
"MOBILEGL_BACKEND_TYPE=${mglColdBackend}" ${MGL_ITEST_SPAWN_ARM_TRANSPORT_ENV}
"MGITEST_SPLIT_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_TEST_DELAY_FIRST_BRINGUP_MS=6000"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/cold-start-spawn-${mglColdBackend}.log"
${MGL_ITEST_CAPABILITY_ENV} ${mglColdDriverEnv})
mgl_itest_lane("${mglColdBackend}.Spawn.ColdStart." "${MGL_COLDSTART_FILTER}"
LABELS "integration-gpu\;integration-cold-start" ENV "${MGL_COLDSTART_ENV_${mglColdBackend}}")
endforeach()
# Texture and program scenarios on the DirectGLES split arms. Every scenario listed passes on
# those arms whole; cases that do not are left out and named in the comments below. The
# readback scenarios (PixelStoreSweep, DepthStencilReadbackMatrix, PackedWordReadback,
# LayeredTextureReadback) are registered with the readback blocks above. Their DirectVulkan
# split arms are only the ungated Magma census lanes (integration-magma-all-* replays this
# call, integration-magma-full-* runs the whole binary).
set(MGL_SPLIT_D2_SCENARIOS
TextureViewScenario # glTextureView end to end, 10 cases
TextureParamsWithoutASamplerViewScenario # G9's four cases; also settles the debt-table row
SampledSetStalenessScenario # 2 cases
ImageSizeAfterRespecScenario # 1 case
ImageTargetKindScenario # 29 cases
IntegerBorderColorScenario # 6 cases
SwizzleAccessRoutineScenario # 4 cases
PostLinkAttachScenario # 2 cases
RelinkStageSetScenario # 3 cases
UniformInitializerScenario # 3 cases
Glsl420DeclarationScenario # 6 cases
IoBlockNameCollisionScenario) # 2 cases
# NO PER-SCENARIO FILTER INDIRECTION, unlike the d1 and i1 blocks above: every scenario here
# is registered whole, because the census found no case in any of them that needs excluding.
# The d1/i1 shape (a MGL_SPLIT_<pkg>_FILTER_<Scenario> variable consulted per entry) is what
# to reach for the day one does; carrying an `if(DEFINED ...)` that is never true today would
# be a branch no run takes.
foreach(mglItestD2Scenario IN LISTS MGL_SPLIT_D2_SCENARIOS)
mgl_itest_register_split_arms("${mglItestD2Scenario}.*" "")
endforeach()
# P7 gate 5 (g5-msrbo): AN EMPTY TEXTURE LEVEL. glTexImage*D with a zero extent is legal GL
# and dEQP's resetStateGLCore issues it for every target after every case; PH-4's extent
# carrier refused it as ProtocolCorruption on the apply thread, so no KHR-GL case could
# finish on a split arm. Backend-agnostic (the refusal was in MGPipeApplyResourceRespecify),
# so the DirectGLES gated arms carry it here and the Magma gated lanes below carry it too.
mgl_itest_register_split_arms("EmptyTextureLevelScenario.*" "")
# ProgramPipelineScenario BEYOND *StorageBlock*: the i1 block above already registers the two
# storage-block cases. The other nine are pure name/state cases that draw nothing, and
# ScenarioFixture's armed-lane rule (the client encoder's record ordinal must move) makes a
# case that produced no record RED for having nothing to say - which is the rule working, not
# a defect. They stay unregistered for that reason and are listed in the census note.
# P8-E (docs/archive/notes/p8/E.md): glCopyImageSubData into a level the driver will not
# read back, then glGetTexImage. The knob-free cases on Espryt's three arms; Magma's gated arms
# are the `DirectVulkan.<arm>.CopyStore.` entries just below; both monolith entries come from
# the whole-binary discovery.
mgl_itest_register_split_arms("CopyImageStoreReadbackScenario.*" "")
# The same cases with MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT pinned: the Espryt server
# treats a colour level of exactly that extent as one the driver refused to attach (what an
# Adreno does for RGB16 and 16-bit SNORM), so its staged store has to answer. READ BY THE
# SERVER, and it reaches all three arms: inproc sees this entry's environment, spawn copies it
# into the child, and the tcp supervisor carries it in its own fixture ENVIRONMENT (appended to
# TcpServer.Start here). Lane-wide there is harmless because the knob names an EXTENT and the
# scenario builds that extent only when it finds the knob in its own environment - every other
# tcp entry, this scenario's knob-free ones included, reads levels of other sizes. The
# renderbuffer case is not here: a renderbuffer's texels were never uploaded, so no store can
# answer for them, and a declined readback ends the session by design.
set(MGL_P8E_REFUSE_EXTENT "13x7")
set_property(TEST TcpServer.Start APPEND PROPERTY ENVIRONMENT
"MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=${MGL_P8E_REFUSE_EXTENT}")
foreach(mglItestArm IN ITEMS Split Spawn Tcp)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
set(mglItestStoreReadProperties)
if(mglItestArm STREQUAL "Tcp")
set(mglItestStoreReadProperties FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp)
endif()
foreach(storeReadCase AnUploadedLevelReadsBackExactly
ACopiedWindowReadsBackOverTheUpload
ACopiedWindowReadsBackFromAnImmutableLevel
APartialUploadAfterTheCopyKeepsTheCopiedWindow)
mgl_itest_lane("DirectGLES.${mglItestArm}.StoreRead." "CopyImageStoreReadbackScenario.${storeReadCase}"
LABELS "integration-gpu\\;integration-${mglItestArmLower}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestStoreReadProperties} ENV "${MGL_ITEST_GLES_${mglItestArmUpper}_ENVIRONMENT}\;MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=${MGL_P8E_REFUSE_EXTENT}")
# Into the accumulated list, so SplitLogPaths gives each entry a private log: the
# store-route assertion reads the server's half of it.
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")
endforeach()
# Magma, the control backend, GATED on its three arms: it reads the VkImage back whatever
# the format, so it has no store route and no knob - only the copy and the read.
set(mglItestMagmaCopyStoreProperties)
if(mglItestArm STREQUAL "Tcp")
set(mglItestMagmaCopyStoreProperties FIXTURES_REQUIRED mobilegl-tcp RESOURCE_LOCK mobilegl-tcp)
endif()
mgl_itest_lane("DirectVulkan.${mglItestArm}.CopyStore." "CopyImageStoreReadbackScenario.*"
LABELS "integration-magma-${mglItestArmLower}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestMagmaCopyStoreProperties} ENV "${MGL_ITEST_VULKAN_${mglItestArmUpper}_ENVIRONMENT}")
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")
endforeach()
# The split half of the counting pair. MGITEST_PERSISTENT_MAP_ARM=emulated is R-6: under split
# the adopt tier is pinned at T2, the resource owner declines every acquisition and the client
# pushes the mapping's dirty blocks - so pmap must be non-zero and mpr must be the monolith
# lane's number. pmap == 0 here means MGPipeApplyMapPersistent handed back a pointer, which is
# exit gate E3(d) failing, and under inproc that failure is invisible in pixels.
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_PMAP_ARM_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MGITEST_PMAP_LANE=1" "MGITEST_PERSISTENT_MAP_ARM=emulated"
"MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/persistent-map-arm-split-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.Split.PersistentMapArm." "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares"
LABELS "integration-gpu\;integration-split" LOCK persistent-map-arm-split-DirectGLES.log ENV "${MGL_ITEST_GLES_SPLIT_PMAP_ARM_ENVIRONMENT}")
# Small-ring lane: the same split scenarios with SEG_CMD and SEG_STAGE at ConfigLoader's 1 MiB
# floor (defaults 8 / 32 MiB), so the workload has to wrap the ring and wait for staging space.
# A ring caps one record at half its size, and 512 KiB is far above any record on these paths.
# Under MGITEST_SMALL_RING_LANE, TriangleScenario drives past the ring size and asserts a
# SEG_CMD wrap and a staging back-pressure wait (WireLedger::ExpectSmallRingWrappedAtLeastOnce).
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_SMALL_RING_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MGITEST_SMALL_RING_LANE=1" "MOBILEGL_IPC_RING_MB=1" "MOBILEGL_IPC_STAGE_MB=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
set(MGL_SPLIT_SMALL_RING_SCENARIOS ClearThenReadPixelsScenario TriangleScenario
PersistentCoherentMapScenario)
foreach(mglItestSmallRingScenario IN LISTS MGL_SPLIT_SMALL_RING_SCENARIOS)
mgl_itest_lane("DirectGLES.Split.SmallRing." "${mglItestSmallRingScenario}.*"
LABELS "integration-gpu\;integration-split" TEST_LIST "MGL_SPLIT_SMALL_${mglItestSmallRingScenario}_TESTS" ENV "${MGL_ITEST_GLES_SPLIT_SMALL_RING_ENVIRONMENT}")
endforeach()
# Strict-errors entries for the per-draw and per-dispatch program reads. Under
# MOBILEGL_IPC_STRICT_ERRORS=1 a barrier-pulled pipe-input read is Fatal{UnmigratedPipeInput},
# so these go red if GetProgramForDraw@DrawArrays or GetProgramForDispatch@DispatchCompute is
# pulled again. The MultiDrawScenario case prepares a draw and checks GL errors, not pixels;
# AtomicCounterScenario dispatches. Labelled integration-split only: on the monolith arm the
# knob cannot trip, because the client fills every field at every verb.
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_STRICT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_IPC_STRICT_ERRORS=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.Split.StrictProgramArm." "MultiDrawScenario.BaseVertexDrawsRejectMalformedArguments"
LABELS "integration-split" TEST_LIST MGL_SPLIT_PA_STRICT_DRAW_TESTS ENV "${MGL_ITEST_GLES_SPLIT_STRICT_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.Split.StrictProgramArm." "AtomicCounterScenario.*"
LABELS "integration-split" TEST_LIST MGL_SPLIT_PA_STRICT_DISPATCH_TESTS ENV "${MGL_ITEST_GLES_SPLIT_STRICT_ENVIRONMENT}")
# P8-B: glGenerateMipmap ON ESPRYT'S SERVER (docs/archive/notes/p8/B.md). The window a record names (B1) and
# RGB16F / RGB32F (B2) on the three Espryt arms; the Magma control is the gated
# `DirectVulkan.<arm>.Mip.` block beside `Remint.`, and both monoliths come from the ambient
# discovery. The FORCED-CPU entries run the three-channel cases with
# MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1, which the SERVER reads: inproc and spawn only, because the
# tcp arm's server is the one shared TcpServer.Start fixture and the knob there would take the
# native arm off every tcp entry. Their own label keeps the spawn-parity sets knob-free, as the
# `RemintFallback.` entries do; integration-gpu is what runs them in the gate.
mgl_itest_register_split_arms("GenerateMipmapServerScenario.*" "")
foreach(mglItestArm IN ITEMS Split Spawn)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
foreach(cpuMipCase Rgb16fUploadedBaseGeneratesItsChain
Rgb16fRenderTargetBaseGeneratesFromTheGpuWrittenLevel
Rgb32fUploadedBaseGeneratesItsChain
Rgb32fRenderTargetBaseGeneratesFromTheGpuWrittenLevel)
mgl_itest_lane("DirectGLES.${mglItestArm}.CpuMip." "GenerateMipmapServerScenario.${cpuMipCase}"
LABELS "integration-gpu\\;integration-cpu-mip-${mglItestArmLower}" ENV "${MGL_ITEST_GLES_${mglItestArmUpper}_ENVIRONMENT}\;MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8b-cpu-mip-${mglItestArmLower}-${cpuMipCase}.log")
endforeach()
endforeach()
# P13 W4b: MONOLITH TAKES THE SAME KNOBS. On the record arm the monolith runs the server's
# generation and the server's store read in its own process, so both knobs reach it and each
# entry asserts its arm's line in its private log. Ambient monolith environment; integration-gpu.
foreach(cpuMipCase Rgb16fUploadedBaseGeneratesItsChain
Rgb16fRenderTargetBaseGeneratesFromTheGpuWrittenLevel
Rgb32fUploadedBaseGeneratesItsChain
Rgb32fRenderTargetBaseGeneratesFromTheGpuWrittenLevel)
mgl_itest_lane("DirectGLES.Monolith.CpuMip." "GenerateMipmapServerScenario.${cpuMipCase}"
ENV "${MGL_ITEST_GLES_ENVIRONMENT}\;MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p13-cpu-mip-monolith-${cpuMipCase}.log")
endforeach()
foreach(storeReadCase AnUploadedLevelReadsBackExactly
ACopiedWindowReadsBackOverTheUpload
ACopiedWindowReadsBackFromAnImmutableLevel
APartialUploadAfterTheCopyKeepsTheCopiedWindow)
mgl_itest_lane("DirectGLES.Monolith.StoreRead." "CopyImageStoreReadbackScenario.${storeReadCase}"
ENV "${MGL_ITEST_GLES_ENVIRONMENT}\;MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=${MGL_P8E_REFUSE_EXTENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p13-store-read-monolith-${storeReadCase}.log")
endforeach()
# P8-C: draw inputs a compute shader wrote, read on the CPU by the side that applies the draw
# (restart rewrite, multi-draw rebase, *IndirectCount, indirect gl_BaseVertex, an orphaned
# indirect store). Split-only source, like F1: the ambient DirectGLES./DirectVulkan. entries
# of the split build are the monolith control and the inproc A/B; this call adds the three
# gated Espryt arms and records the Magma tier-2 replay (integration-magma-all-*).
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/GpuWrittenDrawInputScenario.cpp)
mgl_itest_register_split_arms("GpuWrittenDrawInputScenario.*" "")
# The configure_file that used to sit here MOVED TO THE END OF THIS FILE (P6 `t6`).
#
# It substitutes @MGL_SPLIT_ARM_TEST_LISTS@, which the registration macro appends to - so
# running it here captured only the calls made ABOVE this line and silently dropped the F1
# and Ct blocks below it. SplitLogPaths.PrivateAndDistinct is what caught that, naming
# F1WireScenario.ClearBufferfiPixels; the generated file now sees every call.
# Python is mandatory for this split integration gate (also used by the CI controls).
# Do not silently omit the ownership check when the interpreter is unavailable.
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_test(NAME SplitLogPaths.PrivateAndDistinct
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/Harness/split_log_paths.py"
check "${CMAKE_CTEST_COMMAND}" "${CMAKE_BINARY_DIR}")
set_tests_properties(SplitLogPaths.PrivateAndDistinct PROPERTIES LABELS "integration-split")
# P8-F: `Magma:mipmap-shader-format-or-shape` is a named DECLINE on the wire arm now
# (WireDeclines.def MipmapShaderFormatOrShape), as the monolith arm's GenerateMipmap already
# declines. The shape is a 3D chain with the native blit forced off - neither shader mip arm
# takes it - and the knob is read BY THE SERVER, so split + spawn only (the ShaderMip entries'
# tcp note below). Its own `.ShaderMipDecline.` tail is in MAGMA_SERVER_ENV_KNOB_NO_TCP
# (scripts/ci/spawn_lane_parity.py: one tail per knob case). The Fm lane's flavour plus the arm.
foreach(mglItestArm IN ITEMS Split Spawn)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestP8fMipEnvironment
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV} ${mglItestArmTransportEnv})
mgl_itest_lane("DirectVulkan.${mglItestArm}.ShaderMipDecline." "F1WireScenario.GenerateMipmapWithoutAShaderArmShapeDeclinesAndKeepsTheSession"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestP8fMipEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8f-shadermip-decline${mglItestArmLogTag}.log")
endforeach()
endif()
# f1: split-only source keeps the monolith registry unchanged.
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/F1WireScenario.cpp)
foreach(f1Slot ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D CopyTextureSubImage2D GenerateMipmap GenerateTextureMipmap GenerateMipmapPackedFloat GenerateMipmapDepth)
mgl_itest_register_split_arms("F1WireScenario.${f1Slot}Pixels" "F1.")
endforeach()
# THE TWO PACK-STATE CASES OF F1, which are not ${slot}Pixels-shaped (P3b/P4b espryt D1
# slice 4). ID-49 splits a readback in two: the server reads with NEUTRAL pack state into a
# tight w*h*bpp reply, and the CLIENT scatters those rows into the application's destination
# per its own GL_PACK_*. Only the client half has ever been exercised by a unit case over
# ScatterTightReadbackIntoPackState; these two run the whole seam, one for the padding a
# non-default pack state must leave untouched and one for the overflow that has to be refused
# BEFORE anything is written.
mgl_itest_register_split_arms(
"F1WireScenario.TextureAndFramebufferReadsPreservePackBufferPadding" "F1.")
mgl_itest_register_split_arms(
"F1WireScenario.TextureReadbackRejectsPackedDestinationOverflowBeforeWriting" "F1.")
# Magma's framebuffer, copy and mipmap verbs on the three transports (the Fm lane). The flavour
# is fixed and only the arm supplies the transport, so a red on one arm alone is a transport
# fact. The flavour appends ${MGL_ITEST_VULKAN_ENV}, not ${MGL_ITEST_COMMON_ENV}: it pins
# VK_ICD_FILENAMES and the lavapipe iterationRP repairs, so this lane measures the same driver
# as the monolith `DirectVulkan.` arm instead of whatever ICD the loader finds first.
set(MGL_ITEST_MAGMA_WIRE_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
# Every entry here is green on the inproc arm, R11F_G11F_B10F packed-float mip generation
# included: the GPU shader blit retires the named native-format refusal, so
# GenerateMipmapPackedFloatPixels carries its siblings' label and their pixels assertion.
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaWireEnvironment
${MGL_ITEST_MAGMA_WIRE_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(fmSlot ComputeImageStoreThroughViewPreservesOtherRootLayer EnabledVertexBufferDrawKeepsNamedP7Fatal SrgbDrawTracksFramebufferConversion Texture1DFramebufferClearPixels VertexIdSamplerAndScalarUniformPixels ComputeImageStoreFramebufferPixels PartialTextureUploadPreservesGpuClearPixels TextureViewClearTargetsItsRootMipAndLayer ClearBufferfiPixels ClearBufferfvPixels ClearBufferivPixels ClearBufferuivPixels ClearNamedFramebufferfiPixels ClearNamedFramebufferfvPixels ClearNamedFramebufferivPixels ClearNamedFramebufferuivPixels CopyTexImage2DPixels CopyTexSubImage2DPixels CopyTextureSubImage2DPixels GenerateMipmapPixels GenerateTextureMipmapPixels GenerateMipmapPackedFloatPixels GenerateMipmapDepthPixels)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fm." "F1WireScenario.${fmSlot}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5f-fm${mglItestArmLogTag}-${fmSlot}.log")
endforeach()
foreach(fmCase ClipDistanceScenario.AnEnabledClipDistanceRemovesTheNegativeHalf
ClipDistanceScenario.ADisabledClipDistanceRemovesNothing
ClipDistanceScenario.TheEnablesAreIndependentPerDistance
CopyImageLayeredScenario.ArrayToArrayCopiesEverySlice
CopyImageLayeredScenario.ArrayToArrayHonoursDifferentLayerOffsets
CopyImageLayeredScenario.VolumeToVolumeHonoursNonZeroZ
CopyImageLayeredScenario.VolumeToVolumeAtNonZeroMipLevel
CopyImageLayeredScenario.ArrayToVolumeCopiesEverySlice
CopyImageLayeredScenario.VolumeToArrayCopiesEverySlice)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fm." "${fmCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5f-fm${mglItestArmLogTag}-${fmCase}.log")
endforeach()
# P7 gate 5 (g5-msrbo): the empty-level pair, on the gated Magma lanes as well - the CTS
# arm gate 5 measures is inproc x DirectVulkan, and every one of its cases ends in the
# empty-level reset this scenario reproduces.
foreach(emptyLevelCase TheCtsStateResetDefinesEveryDefaultTargetEmpty
ALevelDefinedEmptyThenRedefinedCarriesTheNewTexels)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fm." "EmptyTextureLevelScenario.${emptyLevelCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-emptylevel${mglItestArmLogTag}-${emptyLevelCase}.log")
endforeach()
# ShaderMip<tier>: MGITEST_MAGMA_FORCE_SHADER_MIPMAP makes GenerateWireMipmap skip its
# native blit (tier 1) and also treat the level as not attachable (tier 2), the arms
# devices take. Lavapipe blits every colour format this binary creates, R11F_G11F_B10F
# included, so no format choice reaches them. Own entries: the knob retargets every mipmap
# in the process. A server-read knob set per entry never reaches the shared tcp fixture's
# server, so knob entries skip the Tcp arm (mglItestServerEnvKnobArm) and are listed in
# spawn_lane_parity.py's MAGMA_SERVER_ENV_KNOB_NO_TCP; knob=0 twins keep all three arms.
# ARCHITECTURE.md §16.1.
set(mglItestServerEnvKnobArm TRUE)
if(mglItestArm STREQUAL "Tcp")
set(mglItestServerEnvKnobArm FALSE)
endif()
foreach(shaderMipTier 1 2)
if(NOT mglItestServerEnvKnobArm)
continue()
endif()
mgl_itest_lane("DirectVulkan.${mglItestArm}.ShaderMip${shaderMipTier}." "F1WireScenario.GenerateMipmapWithoutNativeBlitPixels"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=${shaderMipTier}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-shadermip${mglItestArmLogTag}-tier${shaderMipTier}.log")
endforeach()
# The DEPTH half of the same knob (CONTRACT-P7 §5.1 (A)). ONE TIER, not two: the depth
# pass renders into a depth ATTACHMENT view of the destination level, which a depth
# image always has usage for - there is no "cannot be attached" shape to force, so
# tier 2 would register a second copy of tier 1's run. Split+spawn only, for the
# server-side-knob reason above.
if(mglItestServerEnvKnobArm)
mgl_itest_lane("DirectVulkan.${mglItestArm}.DepthMip." "F1WireScenario.GenerateMipmapDepthWithoutNativeBlitPixels"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-depthmip${mglItestArmLogTag}.log")
endif()
# NO KNOB on the decline: it is decided on the aspect before the blit-support question
# is asked, so the plain Fm environment reaches it on every driver.
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fm." "F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-dsmipdecline${mglItestArmLogTag}.log")
# The CopyInPlace. entries further down have no monolith `DirectVulkan.` twin: the monolith
# arm declines every in-place copy, the wire arm performs them.
# PipelineDelay: MOBILEGL_TEST_PIPELINE_CREATE_DELAY_MS=25 adds 25 ms to every pipeline the
# server creates, as a cold driver cache does (OpenRA's split readback diverged on a device
# only with a cold cache); the Tcp copy runs undelayed, its server being the lane fixture.
# They pin that a readback after a draw returns its pixels. A regression gate, not a
# reproduction: lavapipe does not diverge even without RecordWireReadbackWriteBarrier, as
# its default framebuffer is tracked GENERAL and that transition waits on ALL_COMMANDS.
foreach(pipelineDelayCase VertexIdSamplerAndScalarUniformPixels
ComputeImageStoreFramebufferPixels
GenerateMipmapPixels)
mgl_itest_lane("DirectVulkan.${mglItestArm}.PipelineDelay." "F1WireScenario.${pipelineDelayCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_TEST_PIPELINE_CREATE_DELAY_MS=25\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-pipelinedelay${mglItestArmLogTag}-${pipelineDelayCase}.log")
endforeach()
# ---- P7 wave 2 package B3, EXIT GATE 3: the streamed `glBufferSubData -> draw` pair.
#
# TWO ENTRIES OVER ONE CASE, for the reason the ShaderMip pair gives. The plain entry is
# a regression gate that runs on every driver. The forced entry is the red-once of the
# DEFENCE term: it makes the serial half of VkBufferManager's busy predicate answer
# "idle" - the state an unsound completed-serial floor put the wire arm into on the
# device - so only the submission-fence half is left to order the copy. It does NOT
# cover the floor fix itself (forcing the serial term bypasses the floor entirely); the
# floor's lane is the OpenRA split retrace, which run_trace_case.cmake reds on any
# "MGWIRE-FLOOR unsound-serial-complete" line.
#
# ITS OWN ENTRY RATHER THAN A LANE-WIDE FLIP: the knob changes how EVERY streamed buffer
# write in the process is answered, so putting it in the shared Fm environment would
# retarget every other draw case in this lane.
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fm." "F1WireScenario.StreamedBufferSubDataBeforeEachDrawIsOrdered"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-streamedsubdata${mglItestArmLogTag}.log")
# SPLIT AND SPAWN ONLY - a parity exception, named in scripts/ci/spawn_lane_parity.py
# (MAGMA_SERVER_ENV_KNOB_NO_TCP, the same list as the B2 knobs above), not an omission.
# The knob is read by the SERVER, and the tcp arm's server is the lane fixture
# (scripts/ci/tcp_server_fixture.py) started once from its own environment: an
# entry-level ENVIRONMENT never reaches it, so a Tcp.StaleSerial. entry would run the
# plain Fm case under a name that claims the knob, and could never red. The inproc arm
# hosts the server in the test process and the spawn arm's server inherits the client's
# environment, so on those two the knob is real.
if(mglItestServerEnvKnobArm)
mgl_itest_lane("DirectVulkan.${mglItestArm}.StaleSerial." "F1WireScenario.StreamedBufferSubDataBeforeEachDrawIsOrdered"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-streamedsubdata-stale${mglItestArmLogTag}.log")
endif()
# `default-color-blit-shape@P7`'s retirement. TWO ENTRIES OVER ONE CASE, and the second
# is the only way its arm is reachable here: every surface this lane creates reports
# VK_SURFACE_TRANSFORM_IDENTITY, so the scratch path that exists FOR rotation would
# never run. Forced onto an identity surface it must produce the SAME pixels as the
# fall-through, which is what the shared assertions check.
# The knob=1 copy is split+spawn only (the server-side-knob note above); knob=0 is the
# default and means the same thing on all three arms, so it keeps its tcp entry.
foreach(defaultBlitScratch 0 1)
if(defaultBlitScratch AND NOT mglItestServerEnvKnobArm)
continue()
endif()
mgl_itest_lane("DirectVulkan.${mglItestArm}.DefaultBlitShape${defaultBlitScratch}." "F1WireScenario.ColorBlitToDefaultFromANonSampleableSourceDegrades"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_DEFAULT_BLIT_SCRATCH=${defaultBlitScratch}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-defaultblit${mglItestArmLogTag}-scratch${defaultBlitScratch}.log")
endforeach()
# ---- P7 wave 2-B2: `multisample-blit-shape@P7`'s retirement and its decline ----------
#
# SPLIT-ONLY, like the in-place copy pair below and for the same reason: the retired arm
# does something the monolith arm does not. A scaled multisample colour blit takes the
# monolith's `vkCmdResolveImage` at 1:1 into the destination's offset - the scale is
# dropped - so there is no `DirectVulkan.` reading to register these beside, and an
# equality between the two arms would be an assertion that the wire arm is as wrong.
#
# NO KNOB: every driver that can host a 4x multisample RGBA8 renderbuffer reaches both
# entries with the plain Fm environment, and one that cannot says so as a SKIP.
foreach(multisampleBlitCase MultisampleColorBlitScalesAndFlipsThroughTheResolveScratch
MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeepsTheSession
MultisampleBlitFromASingleSampleSourceDeclinesAndKeepsTheSession)
mgl_itest_lane("DirectVulkan.${mglItestArm}.MsBlit." "F1WireScenario.${multisampleBlitCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-msblit${mglItestArmLogTag}-${multisampleBlitCase}.log")
endforeach()
# MsResolve<0|1>: a 4x multisample DEPTH24_STENCIL8 renderbuffer resolved with
# glBlitFramebuffer and both aspects read back; a one-aspect resolve must leave the other
# aspect of the destination alone. Lavapipe has VK_KHR_create_renderpass2 and
# VK_KHR_depth_stencil_resolve, so only MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=1 reaches
# the shader resolve that devices without them take; both entries expect sample zero.
# Separate entries rather than a lane-wide flip: the knob changes every depth/stencil
# resolve in the process.
foreach(shaderDepthResolve 0 1)
# Same split+spawn restriction, same reason, same parity exception: the knob is read
# on the server and the tcp server is the lane fixture. MsResolve0 stays everywhere.
if(shaderDepthResolve AND NOT mglItestServerEnvKnobArm)
continue()
endif()
# P7 gate 5 (g5-msprobe): MsResolve0 on split and spawn also pins the REAL resolve probe
# on this host - it must run at the server's bring-up, find lavapipe's no-draw resolve
# render pass clean, and leave that pass first. MGITEST_EXPECT_DEPTH_RESOLVE_PROBE is a
# marker only the scenario reads (DepthStencilReadbackMatrixScenario::
# ExpectTheResolveArmWhereTheLaneAsks), and only where the server's private log belongs
# to the entry - the tcp server is the lane fixture, so its MsResolve0 asserts pixels only.
set(msResolveProbeMarker "")
if(NOT shaderDepthResolve AND mglItestServerEnvKnobArm)
set(msResolveProbeMarker "\;MGITEST_EXPECT_DEPTH_RESOLVE_PROBE=clean")
endif()
mgl_itest_lane("DirectVulkan.${mglItestArm}.MsResolve${shaderDepthResolve}." "DepthStencilReadbackMatrixScenario.AResolvedMultisampleDepthReadsBackFromTheDestination"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=${shaderDepthResolve}${msResolveProbeMarker}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-msresolve${mglItestArmLogTag}-shader${shaderDepthResolve}.log")
endforeach()
# ---- P7 gate 5 (g5-msrbo): the SINGLE-SAMPLED depth/stencil blit ----------------------
#
# KHR-GL46.direct_state_access.renderbuffers_storage_multisample's samples == 0 leg, every
# depth/stencil format: the wire arm moved every single-sample depth/stencil aspect with
# vkCmdBlitImage where the monolith copies at 1:1, and for one aspect of a packed format
# that moved the whole native word (a depth-only blit replaced the destination's stencil
# on lavapipe). NO KNOB: the packed-aspect leg is red on lavapipe without the copy arm
# (measured there only - a driver whose one-aspect blit keeps the other aspect stays green
# without it), and nothing in it is read on the server, so all three arms carry it.
mgl_itest_lane("DirectVulkan.${mglItestArm}.DsBlit." "DepthStencilReadbackMatrixScenario.ASingleSampledDepthStencilBlitCopiesEveryAspect"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-dsblit${mglItestArmLogTag}.log")
# P7 wave 2-B2 (review round): a FLIPPED multisample depth/stencil resolve is a legal blit
# that used to end the session, and a SCALED one is INVALID_OPERATION that used to end it
# too. Both shapes are decided on the rectangles, before any capability question, so this
# entry needs NO knob and runs on all three arms.
mgl_itest_lane("DirectVulkan.${mglItestArm}.MsFlip." "DepthStencilReadbackMatrixScenario.AFlippedMultisampleResolveMirrorsTheBandsAndAScaleDeclines"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-msflip${mglItestArmLogTag}.log")
# ITS KNOB TWIN (review round 2). Knob-free, lavapipe answers every resolve through the
# VK_KHR_depth_stencil_resolve arm, so the shader arm's min-corner rectangle under a
# flip - the device WITHOUT the extension - never ran. MsFlip1 forces it. Split+spawn
# only, the server-side-knob reason above, named in spawn_lane_parity.py's
# MAGMA_SERVER_ENV_KNOB_NO_TCP; the knob-free MsFlip. keeps all three arms.
if(mglItestServerEnvKnobArm)
mgl_itest_lane("DirectVulkan.${mglItestArm}.MsFlip1." "DepthStencilReadbackMatrixScenario.AFlippedMultisampleResolveMirrorsTheBandsAndAScaleDeclines"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-msflip${mglItestArmLogTag}-shader1.log")
endif()
# MsResolveBug / MsFlipBug: the resolve arm order on a defective device. Where the server's
# resolve probe finds the no-draw VK_KHR_depth_stencil_resolve pass leaving its target
# unwritten (Adreno 830), ResolveWireDepthStencil tries the shader pass first. Lavapipe
# resolves through both arms, so MGITEST_MAGMA_DEPTH_RESOLVE_PROBE=bug hands the arm choice
# a canned defect measurement (VulkanRenderer::ArmWireDepthResolveOrder). The cases assert
# their pixels and, from the server log, that the shader pass resolved
# (DepthStencilReadbackMatrixScenario::ExpectTheResolveArmWhereTheLaneAsks).
# Server-read knob: split and spawn only (ARCHITECTURE.md §16.1).
if(mglItestServerEnvKnobArm)
foreach(probeBugCase MsResolveBug:AResolvedMultisampleDepthReadsBackFromTheDestination
MsFlipBug:AFlippedMultisampleResolveMirrorsTheBandsAndAScaleDeclines)
string(REPLACE ":" ";" probeBugPair "${probeBugCase}")
list(GET probeBugPair 0 probeBugTail)
list(GET probeBugPair 1 probeBugTest)
string(TOLOWER "${probeBugTail}" probeBugLog)
mgl_itest_lane("DirectVulkan.${mglItestArm}.${probeBugTail}." "DepthStencilReadbackMatrixScenario.${probeBugTest}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_DEPTH_RESOLVE_PROBE=bug\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-${probeBugLog}${mglItestArmLogTag}.log")
endforeach()
endif()
# MsResolveElide: negative control for the real probe (WireDepthResolveProbe.cpp), whose
# recording, readback and tally the canned Bug measurement never exercises.
# MGITEST_MAGMA_DEPTH_RESOLVE_PROBE=elide-subject runs it with the render-pass resolve left
# unrecorded, so the subject keeps its sentinel as on the Adreno 830 while the shader
# control resolves. The entry requires verdict=render-pass-resolve-broken and the shader
# arm (ExpectTheResolveArmWhereTheLaneAsks). Server-read knob: split and spawn only.
if(mglItestServerEnvKnobArm)
mgl_itest_lane("DirectVulkan.${mglItestArm}.MsResolveElide." "DepthStencilReadbackMatrixScenario.AResolvedMultisampleDepthReadsBackFromTheDestination"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_DEPTH_RESOLVE_PROBE=elide-subject\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-msresolveelide${mglItestArmLogTag}.log")
endif()
foreach(inPlaceCase CopyImageInPlaceAcrossLevelsAndLayers
CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone)
mgl_itest_lane("DirectVulkan.${mglItestArm}.CopyInPlace." "F1WireScenario.${inPlaceCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-copyinplace${mglItestArmLogTag}-${inPlaceCase}.log")
endforeach()
# ---- P7 gate 5 (g5-imgwin): the image-unit window ------------------------------------
#
# KHR-GL46.shader_image_load_store.incomplete_textures ended the inproc Redmi run at
# Fatal{UnmigratedVerb, "Magma:image-view-window"}: the unit named level 2 of a texture
# with only level 0, a monolith Fail turned into a SIGABRT. GL 4.6 core 8.26 makes that
# access invalid - zero loads, dropped stores - and the wire descriptor resolve now binds a
# null storage descriptor for it where the device enables VK_EXT_robustness2 nullDescriptor,
# else a placeholder private to the unit (UniformManager::ResolveWireImageDescriptor and
# ResolveWirePlaceholderImage). One entry per case for the private log path; NO KNOB,
# so all three arms. The monolith `DirectVulkan.` entry of the same scenario skips by
# name (it drops the draw: the gate 5 BASE, and a pull-build .text change to move).
# ALevelPastATextureViewsWindowReadsZero joined once the F2 repair retired the transport
# glTextureView death (see the split-arm registration of this scenario).
foreach(imageWindowCase AMissingMipLevelOfAnIncompleteTextureReadsZeroInADraw
AMissingMipLevelOfAnIncompleteTextureReadsZeroInADispatch
ALevelPastAnImmutableTexturesStorageReadsZero
ALayerPastAnArraysLastSliceReadsZero
ATextureWithNoStorageReadsZero
ALevelPastATextureViewsWindowReadsZero
AStoreThroughAnInvalidUnitIsLoadedThroughNeitherAnotherInvalidUnitNorItself
AStoreThroughAnEmptyUnitIsLoadedThroughNeitherAnotherEmptyUnitNorItself)
mgl_itest_lane("DirectVulkan.${mglItestArm}.ImageUnitWindow." "InvalidImageUnitScenario.${imageWindowCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-imagewindow${mglItestArmLogTag}-${imageWindowCase}.log")
endforeach()
# ---- Codex closeout finding 2 (cf-magma): THE ARM WITHOUT A NULL DESCRIPTOR -----------
#
# The two AStoreThrough... cases above run the null-descriptor arm on this host: lavapipe
# enables VK_EXT_robustness2 nullDescriptor, so an invalid or empty storage unit binds a
# null descriptor. A device without it binds a placeholder PRIVATE to the unit (per shape)
# instead; MGITEST_MAGMA_FORCE_PRIVATE_IMAGE_PLACEHOLDER=1 puts the server on that arm
# (UniformManager::SetWireInvalidStorageImageArm), and the client reads the same marker to
# expect it: B's load zero (no alias), A's own load of its store = the recorded CONTRACT-P7
# §12 residual, which is also what proves the knob reached the server.
#
# Split+spawn only, the server-side-knob reason above, named in spawn_lane_parity.py's
# MAGMA_SERVER_ENV_KNOB_NO_TCP.
if(mglItestServerEnvKnobArm)
foreach(privateCase AStoreThroughAnInvalidUnitIsLoadedThroughNeitherAnotherInvalidUnitNorItself
AStoreThroughAnEmptyUnitIsLoadedThroughNeitherAnotherEmptyUnitNorItself)
mgl_itest_lane("DirectVulkan.${mglItestArm}.ImageUnitPrivate." "InvalidImageUnitScenario.${privateCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_PRIVATE_IMAGE_PLACEHOLDER=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-imageprivate${mglItestArmLogTag}-${privateCase}.log")
endforeach()
endif()
endforeach()
endif()
# P7 gate 5 (g5-readback): BANDED READBACK ON MAGMA, all three arms, GATED. The flavour is the
# one the device CTS window ran KHR-GL46 under (role-split state, strict errors, run-ahead), so the
# host lane measures the configuration that aborted on the Redmi with
# 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"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) # the ICD pin, see the Fm lane above
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaBandEnvironment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
# Where the >= 16 MiB arena lands on this arm (P11 A2). Split is one process: T0 (the default
# tier) is granted in process and the arena is the store Magma hands its client - adopted.
# Across processes a host has no shareable memory, so the session runs T2 - emulated.
if("${mglItestArm}" STREQUAL "Split")
set(mglItestArenaArm adopted)
else()
set(mglItestArenaArm emulated)
endif()
foreach(bandCase RenderbufferReadOfExactlyTwoMiBIsBandedNotRefused
TwoMiBPlusOneRowIsBanded
AReadLargerThanTheWholeReplySegmentIsBanded
BandedReadsHonourPackAlignmentRowLengthAndSkips
BandedReadIntoAPixelPackBufferKeepsItsPadding
BandedDepthAndDepthStencilReads
GetTexImageLargerThanAReplySlotIsWindowed)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Band." "BandedReadbackScenario.${bandCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/g5-band${mglItestArmLogTag}-${bandCase}.log")
endforeach()
# P9 (W1): the pack-buffer readback on Magma. The server half is backend-neutral - the
# reply form's read, then the applier's buffer-write gate (WriteWireBuffer here) - so the
# same seven cases, same flavour.
foreach(pboCase ReadPixelsIntoAPackBufferMatchesClientMemoryUnderEveryPackState
MapBufferRangeAfterAPackBufferReadSeesThePixels
SwapBytesIntoAPackBufferMatchesClientMemory
GetTexImageIntoAPackBufferMatchesClientMemory
APackBufferReadFeedsAnUploadFromTheSameBuffer
ALaterBufferSubDataWinsOverThePackReadWhereTheyOverlap
APackBufferReadPostsNoReplyAndMarksTheBuffer)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Pbo." "PackBufferReadbackScenario.${pboCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p9-pbo${mglItestArmLogTag}-${pboCase}.log")
endforeach()
# P10 (A): the fence polls on Magma. The server half is backend-neutral (the backend
# table's FenceSync / GetSyncStatus / ClientWaitSync), so the same three cases.
foreach(fenceCase ZeroTimeoutPollLoopsEndInBoundedTime
PollsOfASignaledFenceCrossNothing
AFenceThatFinishesWhileTheClientOnlyPollsIsReported)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Fence." "FencePollScenario.${fenceCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p10-fence${mglItestArmLogTag}-${fenceCase}.log")
endforeach()
# P11 A2 (docs/archive/contracts/CONTRACT-P11.md §2): the >= 16 MiB arena on Magma's three arms, in the
# same flavour, declared per arm (mglItestArenaArm above); DirectVulkan.ArenaArm. is its
# monolith (adopted) half.
foreach(arenaCase IN LISTS MGL_ITEST_ARENA_CASES)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Arena." "LargeArenaAdoptionScenario.${arenaCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena${mglItestArmLogTag}-${arenaCase}.log")
endforeach()
# P11 B2 addendum: DeletedBoundBufferScenario on Magma's three arms, same flavour (the
# Espryt arms come from mgl_itest_register_split_arms). Before the frontend fix Magma did
# not die: the stale handle resolved to no wire buffer and the draw / dispatch declaring
# the point was dropped - which these cases' markers catch.
foreach(deletedCase AStorageBufferDeletedWhileBoundLeavesNoStaleBinding
AUniformBufferDeletedWhileBoundLeavesNoStaleBinding
AnAtomicCounterBufferDeletedWhileBoundLeavesNoStaleBinding
ATransformFeedbackBufferDeletedWhileBoundLeavesNoStaleBinding)
mgl_itest_lane("DirectVulkan.${mglItestArm}.DeletedBound." "DeletedBoundBufferScenario.${deletedCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-deleted-bound${mglItestArmLogTag}-${deletedCase}.log")
endforeach()
# ...and the one-process arena pair (the Espryt half is beside mgl_itest_register_split_arms).
string(REPLACE "\\;" ";" mglItestPairEnv "${mglItestMagmaBandEnvironment}")
list(APPEND mglItestPairEnv "MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena-pair${mglItestArmLogTag}.log")
string(REPLACE "\\;" ";" mglItestPairLabels "${mglItestMagmaLabels}")
set(mglItestPairName "DirectVulkan.${mglItestArm}.Arena.LargeArenaAdoptionScenario.GpuWriteThenTheTierCaseInOneProcess")
add_test(NAME "${mglItestPairName}"
COMMAND MobileGLIntegrationTest
"--gtest_filter=LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack:LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties("${mglItestPairName}" PROPERTIES
LABELS "${mglItestPairLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT}
ENVIRONMENT "${mglItestPairEnv}"
${mglItestArmProperties})
# P11 A1 + B2 (CONTRACT-P11 §1, B2): MOBILEGL_IPC_ADOPT_TIER=0 on every arm, one case set.
# On tcp a stream refuses the tier by name at the client's handshake (A1, the stream case
# reads the line); on spawn the knob ASKS T0, a host has no AHardwareBuffer, and the session
# falls back to T2 by name (B2, the fallback case reads both lines); on split the client is
# the server's process and T0 is granted in process (the tier case reads `adopted`).
# Register only the assertion applicable to each transport; the parity check names these
# transport-specific exceptions explicitly.
if(mglItestArm STREQUAL "Tcp")
set(mglItestAdoptTier0Env "MGITEST_ADOPT_TIER_REFUSAL=T0")
set(mglItestAdoptTier0Case AStreamSessionRefusesTheAdoptTierOnceByName)
elseif(mglItestArm STREQUAL "Split")
set(mglItestAdoptTier0Env "MOBILEGL_IPC_ALLOW_ADOPT_T0=1")
set(mglItestAdoptTier0Case)
else()
set(mglItestAdoptTier0Env "MGITEST_ADOPT_T0_FALLBACK=no AHardwareBuffer")
set(mglItestAdoptTier0Case ASessionThatCannotRunT0FallsBackToT2OnceByName)
endif()
foreach(arenaCase IN LISTS MGL_ITEST_ARENA_CASES ITEMS ${mglItestAdoptTier0Case})
mgl_itest_lane("DirectVulkan.${mglItestArm}.AdoptTier0." "LargeArenaAdoptionScenario.${arenaCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaBandEnvironment}\;MOBILEGL_IPC_ADOPT_TIER=0\;${mglItestAdoptTier0Env}\;MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-adopt-tier0${mglItestArmLogTag}-${arenaCase}.log")
endforeach()
endforeach()
endif()
# P9 W2: THE TEXTURE RE-MINT ON MAGMA, all three arms, GATED. A storage-usage upgrade recreates
# the VkImage and copies the old one into it GPU to GPU (PreserveTextureContentsOnRecreate), so
# no byte comes from the client here either (docs/archive/notes/p9/W2-REMINT.md). The gated band flavour
# (role-split state, strict errors, run-ahead), one entry per case for a private log each.
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaRemintEnvironment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(remintCase AnUploadedMutableTextureKeepsItsTexelsAcrossTheImageBind
AGpuClearAfterTheUploadSurvivesTheImageBind
AnImmutableTexturesGpuClearSurvivesTheImageBind
ANullDefinedTextureWrittenOnlyByTheGpuSurvivesTheImageBind
APendingPartialUploadMergesOverGpuWrittenTexels
AGeneratedMipLevelSurvivesTheImageBind
AGpuClearedMipLevelSurvivesTheImageBind
ALevelDefinedWithoutDataAfterTheLastSyncDoesNotEndTheSession
ALevelRedefinedLargerWithoutDataAfterTheLastSyncDoesNotEndTheSession
APartialUploadIntoALevelDefinedAfterTheLastSyncReachesTheImage
AWidenedFormatsGpuClearSurvivesTheCarrierRemint
AWidenedFormatsUploadSurvivesTheCarrierRemint
AnRgba8SnormUploadSurvivesTheImageBind
AnRgba16SnormImmutableUploadSurvivesTheCarrierRemint)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Remint." "TextureRemintPullScenario.${remintCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaRemintEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p9-remint${mglItestArmLogTag}-${remintCase}.log")
endforeach()
endforeach()
endif()
# P8-B: THE MAGMA CONTROL for GenerateMipmapServerScenario, all three arms, GATED, in the band
# flavour. Magma generates every case on the GPU (GenerateWireMipmap), so these entries say what
# the Espryt arms above must match. One entry per case for a private log each.
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaMipEnvironment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(mipCase PackedFloatMutableMaxLevelBelowTheChain
PackedFloatStorageWithFewerLevelsThanTheChain
PackedFloatMutableBaseLevelOneKeepsTheLevelsOutsideItsWindow
DepthMutableMaxLevelBelowTheChain
DepthStorageWithFewerLevelsThanTheChain
DepthMutableBaseLevelOneKeepsTheLevelsOutsideItsWindow
Rgb16fUploadedBaseGeneratesItsChain
Rgb16fRenderTargetBaseGeneratesFromTheGpuWrittenLevel
Rgb32fUploadedBaseGeneratesItsChain
Rgb32fRenderTargetBaseGeneratesFromTheGpuWrittenLevel)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Mip." "GenerateMipmapServerScenario.${mipCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaMipEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8b-mip${mglItestArmLogTag}-${mipCase}.log")
endforeach()
endforeach()
endif()
# P11 M: DEV'S TWO GPU-CONTENT SCENARIOS ON MAGMA, all three arms, GATED, in the band flavour -
# ImageBindableRemintScenario (Magma is its control backend: a storage upgrade copies image to
# image) and PartialUploadOverGpuContentScenario (8966d5c2's RGB->RGBA region cut and b1d60523's
# upload-after-clear/draw ordering, both of which run in the server's renderer on these arms).
# One entry per scenario and arm; every entry gets a private log through MGL_SPLIT_ARM_TEST_LISTS.
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaGpuContentEnvironment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(mglItestGpuContentScenario ImageBindableRemintScenario PartialUploadOverGpuContentScenario)
mgl_itest_lane("DirectVulkan.${mglItestArm}.GpuContent." "${mglItestGpuContentScenario}.*"
LABELS "${mglItestMagmaLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaGpuContentEnvironment}")
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")
endforeach()
endforeach()
endif()
# Magma's oversized-recording split and descriptor pool growth on the gated arms (band flavour);
# every entry gets a private log through MGL_SPLIT_ARM_TEST_LISTS.
# Oversized. OversizedRecordingSplitScenario at the default draw budget (16384), all arms:
# the unsplit control.
# SplitRecording64. MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER=64 (server-read) makes every
# case split and assert the server's once-per-process log line: one entry per case, no tcp.
# DescPool. DescriptorPoolGrowthScenario, split only: GetDescriptorPoolCensus reads in process
# and answers unavailable when the server is another process (ARCHITECTURE.md §16.1).
if (MOBILEGL_BUILD_DISAGGREGATED)
set(MGL_ITEST_OVERSIZED_CASES
EveryDrawOfTheFrameLandsAcrossCommandBufferSplits
ADepthOccluderDrawnBeforeTheSplitHidesADrawAfterIt
AStencilMarkMadeBeforeTheSplitGatesADrawAfterIt
BlendingAccumulatesOntoPixelsWrittenBeforeTheSplit
AMultisampleTargetKeepsItsSamplesAcrossTheSplit
OcclusionQueriesOpenAcrossTheSplitCountBothSides
APrimitivesGeneratedQueryOpenAcrossTheSplitCountsBothSides
ATransformFeedbackCaptureActiveAcrossTheSplitAppendsEveryDraw)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaM2Environment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
mgl_itest_lane("DirectVulkan.${mglItestArm}.Oversized." "OversizedRecordingSplitScenario.*"
LABELS "${mglItestMagmaLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaM2Environment}")
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")
if(NOT mglItestArm STREQUAL "Tcp")
foreach(mglItestOversizedCase IN LISTS MGL_ITEST_OVERSIZED_CASES)
mgl_itest_lane("DirectVulkan.${mglItestArm}.SplitRecording64." "OversizedRecordingSplitScenario.${mglItestOversizedCase}"
LABELS "${mglItestMagmaLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaM2Environment}\;MOBILEGL_MAGMA_MAX_DRAWS_PER_COMMAND_BUFFER=64")
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")
endforeach()
endif()
if(mglItestArm STREQUAL "Split")
mgl_itest_lane("DirectVulkan.Split.DescPool." "DescriptorPoolGrowthScenario.*"
LABELS "${mglItestMagmaLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaM2Environment}")
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")
endif()
endforeach()
endif()
# Async compilation, the XFB primitive query pair and GL_PRIMITIVES_GENERATED on Magma's gated
# arms (band flavour: role-split state, strict errors, run-ahead); every entry gets a private log
# through MGL_SPLIT_ARM_TEST_LISTS. AsyncOn./AsyncOff./OptimisticShaderStatus. are the monolith
# entries' three AsyncCompileScenario configurations and keep tcp, as the switch is client-side.
# PrimGen. uses the bring-up probe's Auto verdict and leaves out the arming case, which only runs
# with the reroute pinned. PrimGenReroute. pins MOBILEGL_MAGMA_PRIMGEN_QUERY_REROUTE=1, which the
# server reads, so it has no tcp arm (ARCHITECTURE.md §16.1); each case is its own ctest process,
# which the once-per-process arming log line needs.
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(mglItestMagmaP10bEnvironment
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
set(mglItestP10bShapes AsyncOn AsyncOff OptimisticShaderStatus XfbQuery PrimGen)
if(NOT mglItestArm STREQUAL "Tcp")
list(APPEND mglItestP10bShapes PrimGenReroute)
endif()
foreach(mglItestP10bShape IN LISTS mglItestP10bShapes)
set(mglItestP10bKnobs "")
if(mglItestP10bShape STREQUAL "AsyncOn")
set(mglItestP10bFilter
"AsyncCompileScenario.*-AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly")
set(mglItestP10bKnobs "\;MOBILEGL_ASYNC_SHADER_COMPILE=1")
elseif(mglItestP10bShape STREQUAL "AsyncOff")
set(mglItestP10bFilter "AsyncCompileScenario.ExtensionStringMatchesTheConfiguration")
set(mglItestP10bKnobs "\;MOBILEGL_ASYNC_SHADER_COMPILE=0")
elseif(mglItestP10bShape STREQUAL "OptimisticShaderStatus")
set(mglItestP10bFilter "AsyncCompileScenario.IrisShapedTwoPhaseBatchRendersCorrectly")
set(mglItestP10bKnobs
"\;MOBILEGL_ASYNC_SHADER_COMPILE=1\;MOBILEGL_ASYNC_OPTIMISTIC_SHADER_STATUS=1")
elseif(mglItestP10bShape STREQUAL "XfbQuery")
set(mglItestP10bFilter "XfbPrimitiveQueryScenario.*")
elseif(mglItestP10bShape STREQUAL "PrimGen")
set(mglItestP10bFilter
"PrimitivesGeneratedNoXfbScenario.*-PrimitivesGeneratedNoXfbScenario.TheRerouteIsActuallyArmedWhenTheEnvironmentPinsItOn")
else()
set(mglItestP10bFilter "PrimitivesGeneratedNoXfbScenario.*")
set(mglItestP10bKnobs "\;MOBILEGL_MAGMA_PRIMGEN_QUERY_REROUTE=1")
endif()
mgl_itest_lane("DirectVulkan.${mglItestArm}.${mglItestP10bShape}." "${mglItestP10bFilter}"
LABELS "${mglItestMagmaLabels}" TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaP10bEnvironment}${mglItestP10bKnobs}")
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")
endforeach()
endforeach()
endif()
# Magma application buffers: public GL pixels/bytes, with the two role blocks and
# strict field checks forced on. Existing public scenarios supply offset, mutation,
# persistent-map, growth and indirect coverage; no copied backend implementation.
if (MOBILEGL_BUILD_DISAGGREGATED)
set(MGL_ITEST_MAGMA_BUFFER_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_IPC_ADOPT_TIER=2" "MGITEST_PERSISTENT_MAP_ARM=emulated"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) # the ICD pin, see the Fm lane above
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm} buffers)
mgl_itest_join_environment(MGL_ITEST_MAGMA_BUFFER_ENVIRONMENT
${MGL_ITEST_MAGMA_BUFFER_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(bufferCase
F1WireScenario.EnabledVertexBufferDrawKeepsNamedP7Fatal
F1WireScenario.UniformBufferRangeAndRebindPixels
F1WireScenario.ShortUniformBufferRangePadsMissingBytes
F1WireScenario.SubWordUniformBufferRangePadsMissingBytes
F1WireScenario.UnalignedAtomicCounterRangeDeclinesAndTheSessionLives
F1WireScenario.ComputeWrittenVertexAndIndexBuffersDrawAndReadBack
F1WireScenario.ColorBlitToDefaultIgnoresViewportAndPreservesOrientation
F1WireScenario.ColorBlitToDefaultHonorsScissorAndReversedRect
IndexedDrawFamilyScenario.AnElementBufferDrawElementsOffsetSelectsTheRightQuad
IndexedDrawFamilyScenario.DrawElementsBaseVertexMovesTheSameIndicesToTheOtherQuad
IndexedDrawFamilyScenario.DrawRangeElementsDrawsTheRangedRun
IndexedDrawFamilyScenario.DrawElementsInstancedBaseVertexPaintsOneQuadPerInstance
IndexedDrawFamilyScenario.MultiDrawElementsBaseVertexPaintsEverySubDraw
IndexedDrawFamilyScenario.MultiDrawElementsIndirectDrawsFromTheIndirectBuffer
CrossFrameBufferScenario.VertexBufferSubData
CrossFrameBufferScenario.IndexBufferSubData
CrossFrameBufferScenario.VertexOrphanAndReupload
CrossFrameBufferScenario.IndexOrphanAndReupload
PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw
PersistentCoherentMapScenario.AWriteAfterAFrameBoundaryReachesTheNextFramesDraw
StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding
# P11 A2: the >= 16 MiB arena, the flavour's MGITEST_PERSISTENT_MAP_ARM=emulated
# asserted by its tier case (docs/archive/contracts/CONTRACT-P11.md §2).
LargeArenaAdoptionScenario.SubDataAfterAnInFlightDrawReachesTheNextDraw
LargeArenaAdoptionScenario.RespecifiedVertexArenaKeepsVaoBindings
LargeArenaAdoptionScenario.RespecifiedIndexArenaKeepsVaoBinding
LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite
LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack
LargeArenaAdoptionScenario.TheArenaLandsInTheTierItsLaneDeclares)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Buffers." "${bufferCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_BUFFER_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-buffers${mglItestArmLogTag}-${bufferCase}.log")
endforeach()
endforeach()
endif()
# Real Magma run-ahead, including client lead and present-credit backpressure.
# The test fixture requires an actually armed production session; a missing cap
# fails rather than turning this dedicated lane into a skip or a lockstep pass.
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/MagmaRunAheadScenario.cpp)
# P7 package L: THE INPROC ARM ALONE, AND IT IS A PROPERTY OF THE SCENARIO, NOT A GAP.
# MagmaRunAheadScenario::HoldNextBatch arms the hold through
# ServerLoopInstance().SetBeforeRetireHookForTesting (Harness/SplitRuntimePeek.cpp:43-50) -
# a hook on THIS PROCESS's server loop. Under spawn or tcp the server loop is in another
# process, so the hook is installed on a loop that never runs and the wait times out with
# "the clear waited for apply instead of returning while its server batch was held".
# MEASURED, not assumed: registering all three arms made all 14 entries red on both new
# arms, with that sentence and no Fatal in either role log. An entry that cannot pass for a
# reason that is not the subject of its lane is the mirror image of R-16's rule, and this
# file already keeps four DirectGLES lanes (DualBlock, StrictProgramArm, PersistentMapArm,
# StrictArming) on one arm for exactly this reason. The two-process reading of run-ahead
# needs a cross-process hold - a server-side knob, not a function pointer - and that is
# named in this package's report as a debt rather than left as a red nobody can act on.
foreach(credit 1 3)
foreach(mglItestArm IN ITEMS Split)
mgl_itest_magma_arm_shape(${mglItestArm} runahead)
mgl_itest_join_environment(MGL_ITEST_MAGMA_RUNAHEAD_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_IPC_RUN_AHEAD=1" "MOBILEGL_IPC_PRESENT_CREDIT=${credit}"
"MOBILEGL_IPC_SPIN_US=0" "MOBILEGL_IPC_ADOPT_TIER=2"
"MGITEST_MAGMA_RUNAHEAD_LANE=1" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV} # the ICD pin, see the Fm lane
${mglItestArmTransportEnv})
foreach(runAheadCase
QueuedBufferVersionsAndDeletedNameReuseKeepTheirPixels
QueuedProgramRebindsKeepEachUniformSnapshot
QueuedGpuWritesReadBackTheLastDispatchBytes
MixedNativeAndConvertedGpuVertexStreamsKeepTheirReservation
TextureUploadCannotOvertakeThePreviouslyQueuedDraw
GpuClearSurvivesTheFirstStorageImageUpgrade
PresentCreditActuallyParksTheClientAtItsConfiguredLimit)
mgl_itest_lane("DirectVulkan.${mglItestArm}.RunAhead.Credit${credit}." "MagmaRunAheadScenario.${runAheadCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_RUNAHEAD_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-runahead${mglItestArmLogTag}-credit${credit}-${runAheadCase}.log")
endforeach()
endforeach()
endforeach()
endif()
# Magma wire caches: exact view/attachment identity across public GL mutations.
# Split-only source preserves pull discovery; generic split-build registrations
# retain these names but skip outside this dedicated, strict inproc environment.
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/MagmaWireCacheScenario.cpp)
set(MGL_ITEST_MAGMA_CACHES_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_IPC_RUN_AHEAD=1" "MOBILEGL_IPC_PRESENT_CREDIT=3"
"MOBILEGL_IPC_SPIN_US=0" "MOBILEGL_IPC_ADOPT_TIER=2"
"MGITEST_MAGMA_CACHES_LANE=1" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) # the ICD pin, see the Fm lane above
# P7 package L: THE INPROC ARM ALONE, for the run-ahead lane's reason one step more
# explicit - MagmaWireCacheScenario::SetUp asserts `runtime.transportName == "inproc"`
# outright (MagmaWireCacheScenario.cpp:53), because the whole lane reads the SERVER's own
# view and attachment identity out of this process. MEASURED: all 7 entries red on both new
# arms with `Which is: "non-monolith" ... "inproc"` and no Fatal in either role log.
foreach(mglItestArm IN ITEMS Split)
mgl_itest_magma_arm_shape(${mglItestArm} caches)
mgl_itest_join_environment(MGL_ITEST_MAGMA_CACHES_ENVIRONMENT
${MGL_ITEST_MAGMA_CACHES_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(cacheCase
SamplerSwizzleAtoBtoAKeepsEachQueuedPixel
SamplerRespecifyKeepsOldAndNewNativeImages
SamplerViewsDistinguishMipLayerAndViewType
FramebuffersAtoBtoAKeepDistinctAttachments
FramebufferReattachmentDistinguishesMipAndLayer
FramebufferSrgbPolicyAtoBtoAUsesDistinctFormats
FramebufferRespecifyRebindsTheNewNativeImage)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Caches." "MagmaWireCacheScenario.${cacheCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_CACHES_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-caches${mglItestArmLogTag}-${cacheCase}.log")
endforeach()
endforeach()
endif()
# =====================================================================================
# P7 WAVE 2 PACKAGE C: THE `vertex-layout` RETIREMENT (CONTRACT-P7 §3.2)
# =====================================================================================
#
# The source is NOT inside the `if (MOBILEGL_BUILD_DISAGGREGATED)` above, and that is the
# point of the case pair: the claim is "the wire arm now does what the MONOLITH arm does",
# so the monolith DirectVulkan arm has to run the very same two cases. The ambient
# `TEST_PREFIX "DirectVulkan."` discovery near the top of this file picks them up from the
# target sources with no entry of its own; the three transports are added below.
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/MagmaVertexLayoutScenario.cpp)
if (MOBILEGL_BUILD_DISAGGREGATED)
# The flavour block is the buffers lane's minus the persistent-map knobs, which nothing
# here touches. MOBILEGL_BACKEND_TYPE and the ICD pin are the load-bearing pair: without
# them the entries run DirectGLES under a DirectVulkan prefix and skip themselves in
# SetUp, which reads as six green skips and proves nothing (measured, first cut).
set(MGL_ITEST_MAGMA_VERTEXLAYOUT_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
# ALL THREE ARMS, unlike the caches and run-ahead lanes above: nothing here reads this
# process's server loop, so there is no inproc-only property to declare. The scenario's own
# SetUp skips DirectGLES, so a DirectGLES registration would only add three skips.
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm} vertexlayout)
mgl_itest_join_environment(MGL_ITEST_MAGMA_VERTEXLAYOUT_ENVIRONMENT
${MGL_ITEST_MAGMA_VERTEXLAYOUT_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(vertexLayoutCase
AnUnmappableVertexFormatDeclinesTheDrawInsteadOfEndingTheSession
AnUnreadUnmappableArrayDoesNotStopTheRestOfTheDraw)
mgl_itest_lane("DirectVulkan.${mglItestArm}.VertexLayout." "MagmaVertexLayoutScenario.${vertexLayoutCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_VERTEXLAYOUT_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-vertexlayout${mglItestArmLogTag}-${vertexLayoutCase}.log")
endforeach()
endforeach()
endif()
# =====================================================================================
# Magma wire arm: orphaned buffer stores are reclaimed inside a frame
# =====================================================================================
# A replay with one present never reaches a frame boundary, so the server must reclaim orphaned
# VkBuffers inside the frame. The cases read the server's wbuf[] gauges (PipeStats.h) off its
# `MGPipe stats:` line, so the flavour logs stats every swap; MOBILEGL_IPC_WIRE_DEFERRED_MB=8
# keeps the watermark case at 48 MiB of traffic instead of 400. Split and spawn only: both knobs
# are server-process environment (ARCHITECTURE.md §16.1).
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/MagmaWireReclaimScenario.cpp)
set(MGL_ITEST_MAGMA_RECLAIM_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_IPC_WIRE_DEFERRED_MB=8"
"MGITEST_MAGMA_RECLAIM_LANE=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV}) # the ICD pin, see the Fm lane above
foreach(mglItestArm IN ITEMS Split Spawn)
mgl_itest_magma_arm_shape(${mglItestArm} reclaim)
mgl_itest_join_environment(MGL_ITEST_MAGMA_RECLAIM_ENVIRONMENT
${MGL_ITEST_MAGMA_RECLAIM_FLAVOUR_ENV} ${mglItestArmTransportEnv})
# Round 2 (ID-P7-43) adds the fourth: the reclaim's own memo hazard. M3 adds the fifth:
# fixed-store descriptor churn in a long frame. Both use this private-log fixture and two arms.
foreach(reclaimCase
RespecifiesWithNoDrawBetweenKeepTheLiveStoreCountBounded
RespecifyAndDrawEachStoreInOneFrameStaysWithinTheDeferredBudget
ManySmallRespecifyAndDrawRoundsStayUnderTheStoreCountCeiling
ADrawAfterTheEarlyReclaimFollowsTheNewStoreNotTheMemoizedHandle
DescriptorSetsRewindInsideOneLongFrameAfterTheirSubmitRetires)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Reclaim." "MagmaWireReclaimScenario.${reclaimCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_RECLAIM_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-reclaim${mglItestArmLogTag}-${reclaimCase}.log")
endforeach()
endforeach()
endif()
# P5c ct (docs/archive/contracts/CONTRACT-P5C.md §5): the two control records, split-only source for the
# same reason as f1's - the scenario reads the SERVER sink's tallies, which a monolith build
# has no symbol for. One block per case with a LOG PATH OF ITS OWN, the F1 pattern:
# SplitLogPaths.PrivateAndDistinct fails a lane whose cases share one file, and the death
# case's child truncates whatever path it inherits.
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/CtWireScenario.cpp)
# P7 wave 2 package C adds the fifth: the OQ-10 capability read. It belongs in THIS list
# rather than in a buffer lane because the thing it reads is a property of the SESSION -
# what the server published at handshake and the client's mirror received - and this list
# is the one that is replayed on DirectVulkan by tier 2, so one name here gives the bit a
# reading on both backends and all three transports.
foreach(ctCase ApplierResetCrossesAtThePrimedEdgeInSerialOrder
TextureDeathCrossesAndTheRecycledSlotAnswersTheNewObject
FramebufferDeathCrossesAndTheRecycledSlotAnswersTheNewObject
TheServerPublishesTheResidentSubDataCapabilityFromItsOwnTable
TheDirectApplierResetCallOnTheGLThreadIsRoleViolation)
mgl_itest_register_split_arms("CtWireScenario.${ctCase}" "Ct.")
endforeach()
endif()
# P5b named-blit cases, now green on both backends after P5f fm's server framebuffer
# and texture consumers. Keep the backend-specific label: the Magma lane has its own
# strict + dual-block hard gate, while integration-split keeps the Espryt workset.
# The separate spelling also avoids ctest's substring label match (see below).
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(namedBlitBackend DirectGLES DirectVulkan)
# P7 package L: the Magma half gets the three arms; the Espryt half stays inproc-only,
# because its spawn and tcp coverage of this scenario comes from the F1. blocks the
# registration macro already emits, and a second copy would only double the count.
set(namedBlitArms Split)
if (namedBlitBackend STREQUAL "DirectVulkan")
set(namedBlitArms ${MGL_ITEST_MAGMA_ARMS})
endif()
# The ICD pin follows the BACKEND here, not the lane: this block registers both, and
# handing VK_ICD_FILENAMES to the DirectGLES half would pin an ICD for a lane that
# never opens Vulkan (see the Fm lane above for the ruling).
if (namedBlitBackend STREQUAL "DirectVulkan")
set(namedBlitBaseEnv ${MGL_ITEST_VULKAN_ENV})
else()
set(namedBlitBaseEnv ${MGL_ITEST_COMMON_ENV})
endif()
foreach(mglItestArm IN LISTS namedBlitArms)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(MGL_ITEST_NAMED_BLIT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=${namedBlitBackend}"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${namedBlitBaseEnv} ${mglItestArmTransportEnv})
# THE LABEL IS `integration-magma-split` AND NOT `integration-split-magma`, and that is not
# a preference: `ctest -L` takes a REGEX, not an exact name, so `-L integration-split`
# MATCHES a label spelled `integration-split-magma` as a substring. The obvious name would
# have left both entries in the Espryt lane while every listing showed them relabelled - a
# split that looks done and is not. Measured: with that spelling
# `ctest -N -L integration-split` still listed both DirectVulkan entries.
if (namedBlitBackend STREQUAL "DirectVulkan")
set(MGL_ITEST_NAMED_BLIT_LABELS "${mglItestMagmaLabels}")
else()
set(MGL_ITEST_NAMED_BLIT_LABELS "integration-split")
endif()
foreach(namedBlitCase NamedBlitPreservesBindingsAndRestoresNextVerbsPixels NamedBlitDefaultEndpointPixels)
mgl_itest_lane("${namedBlitBackend}.${mglItestArm}.NamedBlit." "F1WireScenario.${namedBlitCase}"
LABELS "${MGL_ITEST_NAMED_BLIT_LABELS}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_NAMED_BLIT_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5b-${namedBlitBackend}${mglItestArmLogTag}-${namedBlitCase}.log")
endforeach()
endforeach()
endforeach()
endif()
# =====================================================================================
# Positive control for the strict-errors run of integration-split
# =====================================================================================
# A drawing split entry that reads its own PipeStats window (TriangleScenario.cpp): `vbs` > 0
# proves the server stamped verb boundaries, so strict was armed, and `rsp` must be 0 or scale
# with `draws`; test.yml's strict split step requires it to run. It has its own log path and a
# RESOURCE_LOCK on it, because the library opens MOBILEGL_LOG_FILE_PATH with "w" and entries
# sharing a path under `ctest -j` truncate each other's logs (Harness/PipeStatsWindow.h).
if (MOBILEGL_BUILD_DISAGGREGATED)
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_STRICT_ARMING_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MGITEST_STRICT_ARMING_LANE=1"
"MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5e-strict-arming-DirectGLES.log"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.Split.StrictArming." "TriangleScenario.TheServerStampedAVerbBoundaryOnThisDrawingFrame"
LABELS "integration-gpu\;integration-split" LOCK p5e-strict-arming-DirectGLES.log ENV "${MGL_ITEST_GLES_SPLIT_STRICT_ARMING_ENVIRONMENT}")
endif()
# =====================================================================================
# Dual-block negative control (integration-dualblock-split)
# =====================================================================================
# DualBlockScenario asserts that the two roles fill distinct PipeInputs blocks. The entry leaves
# MOBILEGL_IPC_ROLE_SPLIT_STATE unpinned, since a ctest ENVIRONMENT property would override it:
# scripts/ci/dualblock_negative_control.sh runs it with the knob on (green) and off (red on its own
# assertion). Label spelling: ARCHITECTURE.md §16.1. MOBILEGL_IPC_RUN_AHEAD=0 makes every verb
# boundary fill, so the assertion is deterministic.
if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE
Scenarios/DualBlockScenario.cpp Harness/DualBlockPeek.cpp)
mgl_itest_join_environment(MGL_ITEST_GLES_DUALBLOCK_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}" "MGITEST_SPLIT_LANE=1"
"MOBILEGL_IPC_RUN_AHEAD=0"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_lane("DirectGLES.Split.DualBlock." "DualBlockScenario.*"
LABELS "integration-dualblock-split" ENV "${MGL_ITEST_GLES_DUALBLOCK_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/split-logs/DirectGLES.Split.DualBlock.TheTwoRolesHaveDistinctBlocks.log")
endif()
# P5f exit: observe every one of three real drawing frames on both backend roles.
if (MOBILEGL_BUILD_DISAGGREGATED)
foreach(backend DirectGLES DirectVulkan)
# P7 package L: the Magma half on all three transports. `integration-p5f-rsp` itself
# stays on the INPROC entries alone - the CI step that reads it require_greens exactly
# the two names it has always had, and `ctest -L` would have matched a suffixed spelling.
set(p5f_rsp_arms Split)
set(p5f_rsp_base_env ${MGL_ITEST_COMMON_ENV})
if (NOT backend STREQUAL "DirectGLES")
set(p5f_rsp_arms ${MGL_ITEST_MAGMA_ARMS})
# The ICD pin follows the backend, as in the NamedBlit block (see the Fm lane).
set(p5f_rsp_base_env ${MGL_ITEST_VULKAN_ENV})
endif()
foreach(mglItestArm IN LISTS p5f_rsp_arms)
mgl_itest_magma_arm_shape(${mglItestArm} p5f-rsp)
if (backend STREQUAL "DirectGLES")
set(p5f_rsp_label "integration-split\\;integration-p5f-rsp")
elseif (mglItestArm STREQUAL "Split")
set(p5f_rsp_label "integration-magma-split\\;integration-p5f-rsp")
else()
set(p5f_rsp_label "${mglItestMagmaLabels}")
endif()
mgl_itest_join_environment(p5f_rsp_environment
"MOBILEGL_BACKEND_TYPE=${backend}" "MGITEST_SPLIT_LANE=1" "MOBILEGL_ITEST_REQUIRE_GPU=1"
"MGITEST_P5F_RSP_LANE=1" "MOBILEGL_PIPE_STATS=1" "MOBILEGL_PIPE_STATS_PERIOD=1"
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5f-rsp-${backend}${mglItestArmLogTag}.log"
${MGL_ITEST_CAPABILITY_ENV} ${p5f_rsp_base_env} ${mglItestArmTransportEnv})
mgl_itest_lane("${backend}.${mglItestArm}.P5fRsp." "F1WireScenario.EachWireFrameHasZeroResidualPulls"
LABELS "${p5f_rsp_label}" LOCK "p5f-rsp-${backend}${mglItestArmLogTag}.log" PROPERTIES ${mglItestArmProperties} ENV "${p5f_rsp_environment}")
endforeach()
endforeach()
endif()
# P8-A
if (MOBILEGL_BUILD_DISAGGREGATED)
include(${CMAKE_CURRENT_SOURCE_DIR}/P8Coverage.cmake)
endif()
# P8-D: the Magma wire arm issues indirect draws natively (docs/archive/notes/p8/D.md).
# Public GL, so the source joins the binary unguarded and the ambient `DirectGLES.` /
# `DirectVulkan.` monolith discovery runs it too. Split arms: DirectGLES through the registration
# macro (which also replays it on Magma's informational tier), Magma's gated tier by hand, all
# three transports - the counter halves read only on the inproc arm (WireIndirectPeek.h) and the
# pixel halves everywhere, so there is no arm-only property to declare.
target_sources(MobileGLIntegrationTest PRIVATE Harness/WireIndirectPeek.cpp Scenarios/WireIndirectDrawScenario.cpp)
if (MOBILEGL_BUILD_DISAGGREGATED)
mgl_itest_register_split_arms("WireIndirectDrawScenario.*" "")
set(MGL_ITEST_MAGMA_INDIRECT_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm} indirect)
mgl_itest_join_environment(MGL_ITEST_MAGMA_INDIRECT_ENVIRONMENT
${MGL_ITEST_MAGMA_INDIRECT_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(indirectCase
EveryIndirectFormIsIssuedNativelyFromItsStore
ComputeWrittenCommandsDrawWithoutAHostWait
ComputeWrittenCountWordIsTheOneTheGpuReads
ShaderWrittenCommandsWithoutAnApplicationBarrierAreOrdered)
mgl_itest_lane("DirectVulkan.${mglItestArm}.Indirect." "WireIndirectDrawScenario.${indirectCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_INDIRECT_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-indirect${mglItestArmLogTag}-${indirectCase}.log")
endforeach()
endforeach()
endif()
# P8-SE (docs/archive/notes/p8/SE.md): Espryt split residuals.
if (MOBILEGL_BUILD_DISAGGREGATED)
# The default framebuffer's depth/stencil format reaches a spawn / tcp client now (the server
# process has no frontend placeholder, and the publish used to return on it before posting),
# so the case A left as `pending-fix` joins the three Espryt arms.
mgl_itest_register_split_arms(
"DepthStencilReadbackAttachmentShapeScenario.DefaultFramebufferDepthStencilFormatIsBlitCompatible" "")
# B2's CPU mip arm x P8-E's driver-written mark: the forced-CPU knob plus E's refused-readback
# knob at the base level's extent (16x16), both read by the SERVER - so inproc and spawn only,
# and their own label as B's `CpuMip.` entries have (the tcp arm's server is the shared
# TcpServer.Start fixture, and the refusal there would reach every 16x16 colour readback).
foreach(mglItestArm IN ITEMS Split Spawn)
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
string(TOLOWER "${mglItestArm}" mglItestArmLower)
foreach(cpuMipCase Rgb16fDrawnBaseWithARefusedReadbackDeclinesByName
Rgb16fUploadedBaseWithARefusedReadbackIsFilteredFromTheStore)
mgl_itest_lane("DirectGLES.${mglItestArm}.CpuMipRefused." "GenerateMipmapServerScenario.${cpuMipCase}"
LABELS "integration-gpu\\;integration-cpu-mip-${mglItestArmLower}" ENV "${MGL_ITEST_GLES_${mglItestArmUpper}_ENVIRONMENT}\;MGITEST_ESPRYT_FORCE_CPU_MIPMAP=1\;MGITEST_ESPRYT_REFUSE_TEXTURE_READBACK_EXTENT=16x16\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8se-cpu-mip-refused-${mglItestArmLower}-${cpuMipCase}.log")
endforeach()
endforeach()
# PipeCatalogueTest's unmigrated-emulation list against the call sites in the tree (a source
# lint, so the unit lane runs it wherever the split build's unit tests run; CI also runs it
# beside split_coverage.py).
add_test(NAME PipeCatalogue.UnmigratedEmulationSitesSelfTest
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/ci/unmigrated_emulation_sites.py" --self-test)
add_test(NAME PipeCatalogue.UnmigratedEmulationSitesMatchTheList
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/ci/unmigrated_emulation_sites.py"
"${CMAKE_SOURCE_DIR}")
set_tests_properties(PipeCatalogue.UnmigratedEmulationSitesSelfTest
PipeCatalogue.UnmigratedEmulationSitesMatchTheList PROPERTIES LABELS unit TIMEOUT 60)
endif()
# P8-SV: Magma split residuals (docs/archive/notes/p8/SV.md) - the 3D native mip blit's layer
# count, the 1D depth mip decline, the depth/stencil mip decline's tally row (asserted inside
# F1WireScenario) and the wire arm's native glDispatchComputeIndirect. Public GL, so the sources join
# the binary unguarded and the ambient `DirectGLES.` / `DirectVulkan.` monolith discovery runs them
# too (the Espryt entries are the control). Split arms: DirectGLES through the registration macro
# (which also replays them on Magma's informational tier), Magma's gated tier by hand:
# `.Mip3D.` and `.IndirectDispatch.` - all three transports; the counter halves read only on the
# inproc arm (WireIndirectPeek.h, WireDeclinePeek.h), the pixel/grid halves everywhere.
# `.ShaderMipDepth1D.` - split + spawn: MGITEST_MAGMA_FORCE_SHADER_MIPMAP is read BY THE SERVER,
# so a tcp copy would run the default arm (its tail is in spawn_lane_parity.py's
# MAGMA_SERVER_ENV_KNOB_NO_TCP).
# `.Mip3DValidation.` - inproc only: the 3D chain under VK_LAYER_KHRONOS_validation, red on any
# vkCmdBlitImage VUID in the output. The server's Vulkan instance must be this process for its
# stdout to be the entry's and for the case to prove the layer loaded (/proc/self/maps), so it
# is in MAGMA_INPROC_ONLY. The lane that runs integration-magma-split in CI installs the layer.
target_sources(MobileGLIntegrationTest PRIVATE Harness/WireDeclinePeek.cpp Scenarios/WireMipShapeScenario.cpp
Scenarios/WireIndirectDispatchScenario.cpp)
if (MOBILEGL_BUILD_DISAGGREGATED)
mgl_itest_register_split_arms("WireMipShapeScenario.*" "")
mgl_itest_register_split_arms("WireIndirectDispatchScenario.*" "")
set(MGL_ITEST_MAGMA_SV_FLAVOUR_ENV
"MOBILEGL_BACKEND_TYPE=DirectVulkan"
"MOBILEGL_IPC_ROLE_SPLIT_STATE=1" "MOBILEGL_IPC_STRICT_ERRORS=1"
"MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(MGL_ITEST_MAGMA_SV_ENVIRONMENT
${MGL_ITEST_MAGMA_SV_FLAVOUR_ENV} ${mglItestArmTransportEnv})
mgl_itest_lane("DirectVulkan.${mglItestArm}.Mip3D." "WireMipShapeScenario.VolumeChainIsGeneratedAlongItsDepth"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_SV_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-sv-mip3d${mglItestArmLogTag}.log")
foreach(dispatchCase
CpuWrittenGroupCountsAreReadByTheGpuAtTheirOffsets
ComputeWrittenGroupCountsDispatchWithoutAHostWait
ShaderWrittenGroupCountsWithoutAnApplicationBarrierAreOrdered)
mgl_itest_lane("DirectVulkan.${mglItestArm}.IndirectDispatch." "WireIndirectDispatchScenario.${dispatchCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_SV_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-sv-dispatch${mglItestArmLogTag}-${dispatchCase}.log")
endforeach()
if (NOT mglItestArm STREQUAL "Tcp")
mgl_itest_lane("DirectVulkan.${mglItestArm}.ShaderMipDepth1D." "WireMipShapeScenario.Depth1DChainWithoutANativeBlitDeclinesByName"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_SV_ENVIRONMENT}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-sv-depth1d${mglItestArmLogTag}.log")
endif()
endforeach()
mgl_itest_magma_arm_shape(Split)
mgl_itest_join_environment(MGL_ITEST_MAGMA_SV_ENVIRONMENT
${MGL_ITEST_MAGMA_SV_FLAVOUR_ENV} ${mglItestArmTransportEnv})
mgl_itest_lane("DirectVulkan.Split.Mip3DValidation." "WireMipShapeScenario.VolumeChainIsGeneratedAlongItsDepth"
LABELS "${mglItestMagmaLabels}" FAIL_REGEX "VUID-vkCmdBlitImage-" ENV "${MGL_ITEST_MAGMA_SV_ENVIRONMENT}\;VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation\;MGITEST_REQUIRE_VK_VALIDATION_LAYER=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-sv-mip3d-validation.log")
endif()
# P14: texture texels uploaded BETWEEN two draws of one frame that sample the texture, with no wait in
# between (Scenarios/TextureUploadBetweenDrawsScenario.cpp): the first draw sees the old texels, the
# second the new, with and without a chain growth (re-mint + preserve) in between. Magma orders its
# upload batch behind the earlier draws with queue order and the batch's first barrier, not a CPU
# drain. Public GL: the ambient `DirectGLES.` / `DirectVulkan.` monolith discovery runs it, the GLES
# split arms through the macro, Magma's three transports by hand, and `.SyncValidation.` entries
# (inproc, where the server's Vulkan instance is this process) under the Khronos
# synchronization validator, red on any SYNC-HAZARD line whatever the device's timing. Submit-time
# validation is OFF by default in the layer, and without it a hazard across two queue submissions -
# the only kind this case can have - is never reported (measured: the src-scope red-once stayed
# green until it was set).
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/TextureUploadBetweenDrawsScenario.cpp)
if (MOBILEGL_BUILD_DISAGGREGATED)
mgl_itest_register_split_arms("TextureUploadBetweenDrawsScenario.*" "")
# One entry PER CASE: every entry needs its own MOBILEGL_LOG_FILE_PATH (SplitLogPaths).
set(MGL_ITEST_UPLOAD_BETWEEN_DRAWS_CASES
EachDrawSeesTheTexelsUploadedBeforeIt
AChainGrowthBetweenTheDrawsPreservesWhatTheFirstOneSampled)
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
mgl_itest_join_environment(MGL_ITEST_MAGMA_UPLOAD_ENVIRONMENT
${MGL_ITEST_MAGMA_SV_FLAVOUR_ENV} ${mglItestArmTransportEnv})
foreach(uploadCase IN LISTS MGL_ITEST_UPLOAD_BETWEEN_DRAWS_CASES)
mgl_itest_lane("DirectVulkan.${mglItestArm}.UploadBetweenDraws." "TextureUploadBetweenDrawsScenario.${uploadCase}"
LABELS "${mglItestMagmaLabels}" PROPERTIES ${mglItestArmProperties} ENV "${MGL_ITEST_MAGMA_UPLOAD_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-upload-between-draws${mglItestArmLogTag}-${uploadCase}.log")
endforeach()
endforeach()
# The synchronization-validation entries: inproc, where the server's Vulkan instance is this
# process (the monolith arm runs the same record-arm upload path and was checked by hand, P14).
mgl_itest_magma_arm_shape(Split)
mgl_itest_join_environment(MGL_ITEST_MAGMA_UPLOAD_ENVIRONMENT
${MGL_ITEST_MAGMA_SV_FLAVOUR_ENV} ${mglItestArmTransportEnv})
set(MGL_ITEST_SYNC_VALIDATION_ENV
"VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation"
"VK_KHRONOS_VALIDATION_VALIDATE_SYNC=true"
"VK_KHRONOS_VALIDATION_SYNCVAL_SUBMIT_TIME_VALIDATION=true"
"VK_KHRONOS_VALIDATION_SYNCVAL_SHADER_ACCESSES_HEURISTIC=true"
"MGITEST_REQUIRE_VK_VALIDATION_LAYER=1")
mgl_itest_join_environment(MGL_ITEST_SYNC_VALIDATION_ENVIRONMENT ${MGL_ITEST_SYNC_VALIDATION_ENV})
foreach(uploadCase IN LISTS MGL_ITEST_UPLOAD_BETWEEN_DRAWS_CASES)
mgl_itest_lane("DirectVulkan.Split.SyncValidation." "TextureUploadBetweenDrawsScenario.${uploadCase}"
LABELS "${mglItestMagmaLabels}" FAIL_REGEX "SYNC-HAZARD" ENV "${MGL_ITEST_MAGMA_UPLOAD_ENVIRONMENT}\;${MGL_ITEST_SYNC_VALIDATION_ENVIRONMENT}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/magma-upload-between-draws-syncval-${uploadCase}.log")
endforeach()
endif()
# =====================================================================================
# Informational Magma census: every split-arm scenario on DirectVulkan
# =====================================================================================
# Replays every mgl_itest_register_split_arms call on DirectVulkan under
# integration-magma-all-{split,spawn,tcp}. No CI step gates on these labels; the gated Magma lanes
# are the hand-laid integration-magma-{split,spawn,tcp} ones (ARCHITECTURE.md §16.1). Replaying
# the recorded calls keeps this set in step with the Espryt split arms. A case red on spawn and
# tcp but green on inproc is a two-process defect; red on all three, a Magma one.
if (MOBILEGL_BUILD_DISAGGREGATED)
list(LENGTH MGL_SPLIT_ARM_CALLS mglItestSplitArmCallCount)
message(STATUS "Integration tests: P7 package L replays ${mglItestSplitArmCallCount} split-arm "
"registration(s) on DirectVulkan under the informational "
"integration-magma-all-{split,spawn,tcp} labels")
foreach(mglItestCall IN LISTS MGL_SPLIT_ARM_CALLS)
# The recorded form is "<filter>|<tail>", with "-" standing for an empty tail. Split by
# regex rather than by list(GET): a filter is allowed to contain anything but `|`, and a
# CMake list cannot represent a trailing empty element unambiguously.
string(REGEX REPLACE "\\|[^|]*$" "" mglItestReplayFilter "${mglItestCall}")
string(REGEX REPLACE "^.*\\|" "" mglItestReplayTail "${mglItestCall}")
if(mglItestReplayTail STREQUAL "-")
set(mglItestReplayTail "")
endif()
mgl_itest_register_split_arms_for_backend(DirectVulkan "${mglItestReplayFilter}"
"${mglItestReplayTail}")
endforeach()
endif()
# =====================================================================================
# Whole integration binary on Magma split (informational, no CI gate)
# =====================================================================================
# The set the monolith `DirectVulkan.` arm runs (all but MGL_ITEST_MONOLITH_MECHANISM_FILTER,
# oracles that must not inherit a transport), minus DualBlockScenario (its own lane). No
# MGITEST_SPLIT_LANE: query-only cases emit no record (ARCHITECTURE.md §16.1). Private log paths
# come through MGL_MAGMA_FULL_TEST_LISTS, not MGL_SPLIT_ARM_TEST_LISTS, whose entries also get
# lane markers that would arm unrelated oracles here; SplitLogPaths.cmake.in walks both.
if (MOBILEGL_BUILD_DISAGGREGATED)
set(MGL_MAGMA_FULL_TEST_LISTS "")
foreach(mglItestArm IN LISTS MGL_ITEST_MAGMA_ARMS)
mgl_itest_magma_arm_shape(${mglItestArm})
string(TOUPPER "${mglItestArm}" mglItestArmUpper)
mgl_itest_join_environment(mglItestMagmaFullEnvironment
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ITEST_REQUIRE_GPU=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV} ${mglItestArmTransportEnv})
mgl_itest_lane("DirectVulkan.${mglItestArm}.Full." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:DualBlockScenario.*"
LABELS "integration-magma-full-${mglItestArmLower}" TEST_LIST "MGL_MAGMA_FULL_TESTS_${mglItestArmUpper}" PROPERTIES ${mglItestArmProperties} ENV "${mglItestMagmaFullEnvironment}")
list(APPEND MGL_MAGMA_FULL_TEST_LISTS "MGL_MAGMA_FULL_TESTS_${mglItestArmUpper}")
endforeach()
message(STATUS "Integration tests: P7 package L tier 3 - the whole integration binary on "
"DirectVulkan x {inproc, spawn, tcp} under the informational "
"integration-magma-full-{split,spawn,tcp} labels (no CI gate)")
endif()
# =====================================================================================
# integration-verify-split: the verify comparator on the split (inproc) arm
# =====================================================================================
# Both backends run the monolith verify lane's cases minus MGL_ITEST_MONOLITH_MECHANISM_FILTER
# (arming entry and negative controls are re-registered below), DualBlockScenario (verify turns
# the dual-block rehearsal off) and, on DirectGLES, four Magma decline cases. No MGITEST_SPLIT_LANE
# (ARCHITECTURE.md §16.1): each entry has a private log (MGL_VERIFY_SPLIT_TEST_LISTS), in which
# test.yml's integration-verify-split job checks the inproc transport and the comparator arming.
if (MOBILEGL_BUILD_DISAGGREGATED AND MOBILEGL_PIPE_VERIFY)
# The transport half, joined onto each backend's own list. One place says what "the verify
# split arm" means, as MGL_ITEST_SPLIT_ARM_TRANSPORT_ENV does for the Magma lanes.
set(MGL_ITEST_VERIFY_SPLIT_ARM_ENV
"MOBILEGL_TRANSPORT=inproc" "MOBILEGL_IPC_SERVER_PATH=${MGL_ITEST_SPLIT_SERVER_PATH}"
"MOBILEGL_PIPE_VERIFY=1" "MOBILEGL_ITEST_REQUIRE_GPU=1")
mgl_itest_join_environment(MGL_ITEST_GLES_VERIFY_SPLIT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectGLES" ${MGL_ITEST_VERIFY_SPLIT_ARM_ENV}
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
mgl_itest_join_environment(MGL_ITEST_VULKAN_VERIFY_SPLIT_ENVIRONMENT
"MOBILEGL_BACKEND_TYPE=DirectVulkan" ${MGL_ITEST_VERIFY_SPLIT_ARM_ENV}
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV})
# The four Magma wire-arm decline cases are left out of the DirectGLES half. They assert
# Magma's declines (depth-stencil mip, two multisample blits, overlapping in-place copy),
# which Espryt does not make, and they skip by backend name in every DirectGLES lane
# (F1WireScenario.cpp, "Magma wire-arm decline"); the subtraction is redundant and kept so the
# lane's counts do not move. The DirectVulkan half runs them.
set(MGL_ITEST_VERIFY_SPLIT_GLES_MAGMA_DECLINE_FILTER
"F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession:F1WireScenario.MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeepsTheSession:F1WireScenario.MultisampleBlitFromASingleSampleSourceDeclinesAndKeepsTheSession:F1WireScenario.CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone")
set(MGL_VERIFY_SPLIT_TEST_LISTS "")
# The ambient set, per backend. The GLES half is registered TWICE for the monolith verify
# lane's reason and with its spelling: the texture-view cases need
# MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1 or DirectGLES withholds GL_ARB_texture_view and they
# green-skip, so they are subtracted from the first registration and re-registered with the
# knob. "Every existing verify case on the split arm" is false without the second block.
mgl_itest_lane("DirectGLES.VerifySplit." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:${MGL_ITEST_GLES_VIEW_FILTER}:DualBlockScenario.*:${MGL_ITEST_VERIFY_SPLIT_GLES_MAGMA_DECLINE_FILTER}"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} TEST_LIST MGL_VERIFY_SPLIT_TESTS_GLES ENV "${MGL_ITEST_GLES_VERIFY_SPLIT_ENVIRONMENT}")
mgl_itest_lane("DirectGLES.VerifySplit." "${MGL_ITEST_GLES_VIEW_FILTER}"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} TEST_LIST MGL_VERIFY_SPLIT_TESTS_GLES_VIEW ENV "${MGL_ITEST_GLES_VERIFY_SPLIT_ENVIRONMENT}\;MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1")
mgl_itest_lane("DirectVulkan.VerifySplit." "-${MGL_ITEST_MONOLITH_MECHANISM_FILTER}:DualBlockScenario.*"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} TEST_LIST MGL_VERIFY_SPLIT_TESTS_VULKAN ENV "${MGL_ITEST_VULKAN_VERIFY_SPLIT_ENVIRONMENT}")
list(APPEND MGL_VERIFY_SPLIT_TEST_LISTS MGL_VERIFY_SPLIT_TESTS_GLES
MGL_VERIFY_SPLIT_TESTS_GLES_VIEW MGL_VERIFY_SPLIT_TESTS_VULKAN)
# The arming assertion on this arm, one entry per backend, with a log path nothing else
# writes to - the monolith VerifyArming. entries' shape and their reason (the case reads the
# whole file, and a path shared with 500 neighbours races them under `-j 4`). This is the
# entry that cannot be green against a library with no comparator compiled in, and unlike the
# monolith pair it also cannot be green against one whose comparator never survived the
# server's bring-up - which is precisely what the relaxed respecify pin unblocked.
#
# MGITEST_PIPE_ARMING_LANE=1 is a harness marker the library never reads; it is what makes
# the case skip in an ambient lane rather than race there.
foreach(mglItestVerifyBackend DirectGLES DirectVulkan)
if (mglItestVerifyBackend STREQUAL "DirectGLES")
set(mglItestVerifyArmEnv "${MGL_ITEST_GLES_VERIFY_SPLIT_ENVIRONMENT}")
else()
set(mglItestVerifyArmEnv "${MGL_ITEST_VULKAN_VERIFY_SPLIT_ENVIRONMENT}")
endif()
mgl_itest_lane("${mglItestVerifyBackend}.VerifySplitArming." "PipeVerifyArmingScenario.Armed"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} LOCK "pipe-verify-split-arming-${mglItestVerifyBackend}.log" ENV "${mglItestVerifyArmEnv}\;MGITEST_PIPE_ARMING_LANE=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-split-arming-${mglItestVerifyBackend}.log")
# THE TWO NEGATIVE CONTROLS ON THIS ARM (CONTRACT-P7 §6), the monolith verify block's
# four entries with the split environment underneath and nothing else changed: each is
# green when its control REPORTS, so each goes red the day the comparator or the poison
# stops reaching the split arm. test.yml's integration-verify-split job requires each of
# them to pass by name, and control A's CI half - the same knob exported against the
# ambient VerifySplit. entries, where ctest has to FAIL - runs there too.
#
# A (G4): the corruption perturbs the snapshot arm at the CLIENT's entry compare, so the
# report lands in the client role's log, which is the half LibraryLog() reads.
mgl_itest_lane("${mglItestVerifyBackend}.VerifySplitCorrupted." "PipeVerifyArmingScenario.CorruptedFieldIsReported"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} LOCK "pipe-verify-split-corrupt-${mglItestVerifyBackend}.log" ENV "${mglItestVerifyArmEnv}\;MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters\;MOBILEGL_PIPE_VERIFY_FATAL=0\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-split-corrupt-${mglItestVerifyBackend}.log")
# A ON THE OTHER ARM (P7 wave 3, V1). Control A above perturbs EntryCompare's snapshot,
# which runs on the CLIENT thread at a verb boundary and reports `where=entry`; the
# compare-at-read hook, which runs on the SERVER's apply thread at every backend read and
# is the only comparator that arm ever runs, had no control at all until this entry. The
# knob names GetPixelStoreParameters because ReadPixels is where the hook does its one
# piece of arm-specific reasoning - the ID-49 neutral pack window, whose oracle it rewrites
# - so a control aimed anywhere else would leave exactly that branch unproven.
#
# FATAL=0 for the corrupt entry's reason and one more: this field IS in kReadback's fill
# mask (FillPoints.def), so the client's entry compare fires first and with FATAL at its
# default the process would die before the server reached the read. The case reads the
# SERVER role's half of its private log and nothing else.
mgl_itest_lane("${mglItestVerifyBackend}.VerifySplitReadCorrupted." "PipeVerifyArmingScenario.CorruptedFieldIsReportedOnTheServerRead"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} LOCK "pipe-verify-split-read-corrupt-${mglItestVerifyBackend}.log" ENV "${mglItestVerifyArmEnv}\;MOBILEGL_PIPE_VERIFY_CORRUPT=GetPixelStoreParameters\;MOBILEGL_PIPE_VERIFY_FATAL=0\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-verify-split-read-corrupt-${mglItestVerifyBackend}.log")
# B (G5): the child brings up its own inproc session from the inherited environment, and
# PoisonOmissionScenario's ReadChildLog reads BOTH role halves of the child's log, because
# under a transport the poisoned read can fire on either side of the seam.
#
# A DIFFERENT PAIR FROM THE MONOLITH ENTRIES', and the monolith pair is not "also fine
# here": GenerateMipmap:GetActiveTextureUnit is inert on this arm - the split backend's
# generate_mipmap resolves its texture from the verb's handle and never reads the active
# unit, so the child exited 0 with that stamp withheld (measured, both backends).
# ReadPixels:GetPixelStoreParameters is read on the server under the server's stamp,
# which is the stamp the knob has to withhold on this arm (PipeInputs.cpp's
# ServerPoisonOmission). PoisonOmissionScenario.cpp's kOmittedPairs carries both.
mgl_itest_lane("${mglItestVerifyBackend}.VerifySplitPoisonOmitted." "PoisonOmissionScenario.OmittedFieldAbortsOnThatVerb"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} LOCK "pipe-poison-omit-split-${mglItestVerifyBackend}.log" ENV "${mglItestVerifyArmEnv}\;MOBILEGL_PIPE_POISON_OMIT=ReadPixels:GetPixelStoreParameters\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/pipe-poison-omit-split-${mglItestVerifyBackend}.log")
# B's AMBIENT HALF. `Verify.PoisonOmissionScenario.WithoutOmissionCompletes` runs the
# scenario with nothing withheld, and the mechanism filter took it out of the ambient set
# above along with the rest of the scenario. Registered back by hand, with no knob, so
# this arm's ambient lane still runs it.
mgl_itest_lane("${mglItestVerifyBackend}.VerifySplit." "PoisonOmissionScenario.WithoutOmissionCompletes"
LABELS "integration-verify-split" TIMEOUT ${MGL_ITEST_VERIFY_TIMEOUT} TEST_LIST "MGL_VERIFY_SPLIT_TESTS_POISON_${mglItestVerifyBackend}" ENV "${mglItestVerifyArmEnv}")
list(APPEND MGL_VERIFY_SPLIT_TEST_LISTS "MGL_VERIFY_SPLIT_TESTS_POISON_${mglItestVerifyBackend}")
endforeach()
# Not on spawn: the comparator compares the applier-written gPipeInputs with the frontend
# GLContext, which only inproc puts in one process. Under spawn the client's entry compare
# fails at the first verb (the applier fills the other process's block), and the server's
# MGPipeVerifyReadHook returns early: the server runs MG_Backend::InitServerRoleForSpawn(),
# never MG_State::Init(), so it has no GLContext. Spawn needs a cross-process compare, not a
# link change.
message(STATUS "Integration tests: P7 wave 3 - the verify comparator on the split (inproc) arm, "
"both backends, under integration-verify-split")
endif()
if (MOBILEGL_BUILD_DISAGGREGATED)
# LAST, so every mgl_itest_register_split_arms call above has already contributed its list
# name. Apply per-entry paths after GoogleTest discovery has populated the TEST_LISTs.
configure_file(Harness/SplitLogPaths.cmake.in SplitLogPaths.cmake @ONLY)
set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES
"${CMAKE_CURRENT_BINARY_DIR}/SplitLogPaths.cmake")
add_test(NAME SplitLogPaths.ResultAccountingControls
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Harness/test_split_log_paths.py)
set_tests_properties(SplitLogPaths.ResultAccountingControls PROPERTIES LABELS "unit")
# The split fixture's transport-claim rule on canned inputs, so the rule itself can go red
# off a GPU: no GL, no library, just the pure function the fixture calls.
add_executable(SplitLaneHarnessTest Harness/SplitLaneHarnessTest.cpp)
target_link_libraries(SplitLaneHarnessTest PRIVATE GTest::gtest_main)
add_test(NAME SplitLaneHarness.TransportClaimRule COMMAND SplitLaneHarnessTest)
set_tests_properties(SplitLaneHarness.TransportClaimRule PROPERTIES LABELS "unit")
endif()