# Gears1-Native (Gears of War, 2006) - CMakeLists.txt copied from GearsJ-Native (Shagg's own project), names changed:
# four layers, each its own library; a layer only sees its own and lower layers' headers.
#   layer 0  Xbox 360 GPU core + game-free tools: Gears 2's src/layer0_xenos, shared (GN_LAYER0_HOME below)
#   layer 1  (not yet)          D3D library profile - Gears 3 is XDK 20500 (Gears 2: 7645), to be written
#   layer 2  src/layer2_ue3    Unreal Engine 3 (launch command line)
#   layer 3  src/layer3_gow1   Gears of War (the executable, game patches)
# Third-party pieces (FLIP's stb_image_write, DXC headers/DLLs, Catch2) are used read-only from Gears 2's third_party
# folder (GN_THIRD_PARTY, set by tools/build.ps1), as agreed with the Gears 2 chat.
cmake_minimum_required(VERSION 3.25)
project(gow1 LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY ON)
set(GN_THIRD_PARTY "${CMAKE_SOURCE_DIR}/../Gears2-Native/third_party" CACHE PATH "Gears 2's third_party folder (read-only)")
# The shared renderer (layer 0): built from GN_LAYER0_HOME/src/layer0_xenos and its tests from GN_LAYER0_HOME/tests.
# Since Gears 2's afef446 ("One shared renderer", 2026-10-22) both games build ONE copy, Gears 2's, read-only from
# here; renderer changes go to the Gears 2 chat as patches (patches/to-gears2). This folder's own old copy of src/layer0_xenos
# was deleted in job 114 (Shagg 21:55). Override with -DGN_LAYER0_HOME=<folder>.
if(NOT DEFINED GN_LAYER0_HOME)
    set(GN_LAYER0_HOME "${CMAKE_SOURCE_DIR}/../Gears2-Native")
endif()
set(GN_LAYER0_DIR "${GN_LAYER0_HOME}/src/layer0_xenos")
set(GN_LAYER0_TESTS "${GN_LAYER0_HOME}/tests")
message(STATUS "Gears1-Native: layer 0 (renderer) from ${GN_LAYER0_DIR}")

# SDK boilerplate copied from the template's generated/rexglue.cmake (generated/ is never committed).
include(cmake/rexglue.cmake)

# Layer 0: Xbox 360 GPU core and game-free developer tooling (the shared copy in Gears2-Native).
# PR #117: input/ holds the button-prompt monitor the shared texture cache and in-game menu use. GitHub #214: Gears 1's
# button font and HUD sheets are in the shared prompt catalog; the glyphs come from the package's shared assets\glyphs.
file(GLOB GN_LAYER0_SOURCES CONFIGURE_DEPENDS ${GN_LAYER0_DIR}/*.cpp ${GN_LAYER0_DIR}/renderer/*.cpp ${GN_LAYER0_DIR}/input/*.cpp)
# The vendor-SDK wrappers are added below only when their SDK is there (job 052).
list(FILTER GN_LAYER0_SOURCES EXCLUDE REGEX "(dlss|fsr|xess)_upscaler\\.cpp$")
add_library(gn_layer0_xenos STATIC ${GN_LAYER0_SOURCES})
target_include_directories(gn_layer0_xenos PUBLIC ${GN_LAYER0_DIR}/include)
include(${GN_LAYER0_HOME}/cmake/spirv_opt.cmake)  # 0.6.1: SPIRV-Tools' optimiser for the Vulkan shader path
gn_add_spirv_opt(gn_layer0_xenos ${GN_THIRD_PARTY})
target_include_directories(gn_layer0_xenos PRIVATE ${GN_THIRD_PARTY}/flip/src/cpp/tool)
target_link_libraries(gn_layer0_xenos PUBLIC rex::runtime)
set(GN_DEPS_THIRD_PARTY ${GN_THIRD_PARTY})
include(${GN_LAYER0_HOME}/cmake/gn_deps.cmake)   # GN_DXC_DIR, GN_AGILITY_DIR, GN_XESS_DIR (0.4.9)
target_include_directories(gn_layer0_xenos PRIVATE ${GN_DXC_DIR}/inc)
# Vulkan (Gears 2 notes/68, its CMakeLists.txt; job 018: without these headers vulkan_backend.cpp builds its null stub
# and --gow2_renderer_api=vulkan silently stays on D3D12). Header-only; the loader is opened at run time. Newest
# Khronos Vulkan-Headers release in our third_party, else Gears 2's, else the SDK source tree's copy.
file(GLOB GN_VK_HEADER_DIRS LIST_DIRECTORIES true ${GN_THIRD_PARTY}/vulkan-headers-v1.4.* ${GN_LAYER0_HOME}/third_party/vulkan-headers-v1.4.*)
list(SORT GN_VK_HEADER_DIRS COMPARE NATURAL ORDER DESCENDING)
set(GN_VULKAN_HEADERS "")
foreach(gn_d ${GN_VK_HEADER_DIRS} ${GN_LAYER0_HOME}/third_party/rexglue-sdk/thirdparty/vulkan-headers)
    if(NOT GN_VULKAN_HEADERS AND EXISTS ${gn_d}/include/vulkan/vulkan_core.h)
        set(GN_VULKAN_HEADERS ${gn_d}/include)
    endif()
endforeach()
if(GN_VULKAN_HEADERS)
    message(STATUS "Gears1-Native: Vulkan headers ${GN_VULKAN_HEADERS}")
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_VULKAN_HEADERS})
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_HAVE_VULKAN_HEADERS=1)
else()
    message(WARNING "Gears1-Native: no Vulkan headers found - the Vulkan backend is a stub")
endif()
if(TARGET rex::renderdoc)  # Gears 2 job 375: the SDK's Vulkan headers include renderdoc_app.h
    target_link_libraries(gn_layer0_xenos PRIVATE rex::renderdoc)
endif()
if(WIN32)
    target_link_libraries(gn_layer0_xenos PRIVATE dbghelp d3d12 dxgi dxguid ole32)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
    target_compile_options(gn_layer0_xenos PRIVATE -ffp-contract=off)
elseif(MSVC)
    target_compile_options(gn_layer0_xenos PRIVATE /fp:precise)
endif()
rexglue_apply_target_settings(gn_layer0_xenos)

# Layer 2: Unreal Engine 3.
add_library(gn_layer2_ue3 STATIC src/layer2_ue3/launch_command_line.cpp src/layer2_ue3/config_bundle.cpp
            src/layer2_ue3/config_install.cpp
            src/layer2_ue3/objects.cpp src/layer2_ue3/script_input.cpp)   # developer mode's scripted controller
target_include_directories(gn_layer2_ue3 PUBLIC src/layer2_ue3/include)
target_link_libraries(gn_layer2_ue3 PUBLIC gn_layer0_xenos)
rexglue_apply_target_settings(gn_layer2_ue3)
# notes/78 chapter recipes: Gears 2's package shader reader (patch 0068, game-free UE3 code that already reads Gears 3's
# package layout) and its LZX decoder, built from GN_LAYER0_HOME like layer 0. Only those files get Gears 2's layer 2
# include folder, so this game's own ue3/ headers are not shadowed.
target_sources(gn_layer2_ue3 PRIVATE ${GN_LAYER0_HOME}/src/layer2_ue3/shader_prebuild.cpp
    ${GN_LAYER0_HOME}/src/layer2_ue3/prebuild_lzx.cpp
    ${GN_LAYER0_HOME}/src/layer2_ue3/package_exports.cpp)  # GitHub #176 (as #145 / #153)
set_source_files_properties(${GN_LAYER0_HOME}/src/layer2_ue3/shader_prebuild.cpp
    ${GN_LAYER0_HOME}/src/layer2_ue3/prebuild_lzx.cpp ${GN_LAYER0_HOME}/src/layer2_ue3/package_exports.cpp PROPERTIES
    INCLUDE_DIRECTORIES ${GN_LAYER0_HOME}/src/layer2_ue3/include)

# Layer 3: Gears of War (the executable).
set(GOW1_SOURCES
    src/layer3_gow1/main.cpp
    src/layer3_gow1/game_patches.cpp
    src/layer3_gow1/mouse_look.cpp
    src/layer3_gow1/unlocks.cpp
)
if(WIN32)
    add_executable(gow1 WIN32 ${GOW1_SOURCES})
else()
    add_executable(gow1 ${GOW1_SOURCES})
endif()
target_include_directories(gow1 PRIVATE src/layer3_gow1)
target_link_libraries(gow1 PRIVATE gn_layer2_ue3)
# 0.2.0 (GitHub #9): DirectX 12 Agility SDK - D3D12Core.dll in <exe>\D3D12\, chosen by agility.cpp's exports, so
# Shader Model 6.6 works on Windows 10's older Direct3D 12 runtime too.
if(WIN32 AND EXISTS ${GN_AGILITY_DIR}/build/native/bin/x64/D3D12Core.dll)
    target_sources(gow1 PRIVATE ${GN_LAYER0_DIR}/agility/agility.cpp)
    target_compile_definitions(gow1 PRIVATE GN_AGILITY_SDK_VERSION=${GN_AGILITY_SDK_VERSION})
    add_custom_command(TARGET gow1 POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:gow1>/D3D12
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_AGILITY_DIR}/build/native/bin/x64/D3D12Core.dll $<TARGET_FILE_DIR:gow1>/D3D12
        COMMENT "Copying the Agility SDK's D3D12Core.dll to D3D12\\ next to gow1.exe")
else()
    message(WARNING "gow1: no Agility SDK in third_party/agility-sdk - Windows 10 can't run the D3D12 renderer")
endif()
# GPU_PLUGINS xenos: the SDK's Xenos GPU emulation, the reference our renderer is compared with (developer builds).
rexglue_setup_target(gow1 GPU_PLUGINS xenos)
add_custom_command(TARGET gow1 POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        ${GN_DXC_DIR}/bin/x64/dxcompiler.dll ${GN_DXC_DIR}/bin/x64/dxil.dll $<TARGET_FILE_DIR:gow1>
    COMMENT "Copying dxcompiler.dll + dxil.dll next to gow1.exe")

# Upscalers (job 052, from Gears 2 notes/64 and notes/65): DLSS, FSR and XeSS are built in when their SDKs are in
# Gears 2's third_party folder (read-only, never committed). GN_WITH_DLSS / GN_WITH_FSR / GN_WITH_XESS switch one off.
set(GN_DLSS_DIR ${GN_THIRD_PARTY}/dlss-sdk)
set(GN_FFX_DIR ${GN_THIRD_PARTY}/fidelityfx-sdk/Kits/FidelityFX CACHE PATH "AMD FSR SDK Kits/FidelityFX folder (fsr049: -DGN_FFX_DIR=... builds against another copy)")
set(GN_DLSS_FOUND OFF)
if(WIN32 AND EXISTS ${GN_DLSS_DIR}/include/nvsdk_ngx.h AND EXISTS ${GN_DLSS_DIR}/lib/Windows_x86_64/x64/nvsdk_ngx_d.lib)
    set(GN_DLSS_FOUND ON)
endif()
set(GN_FFX_FOUND OFF)
if(WIN32 AND EXISTS ${GN_FFX_DIR}/api/include/ffx_api.h AND EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_dx12.dll)
    set(GN_FFX_FOUND ON)
endif()
set(GN_XESS_FOUND OFF)
if(WIN32 AND EXISTS ${GN_XESS_DIR}/inc/xess/xess_d3d12.h AND EXISTS ${GN_XESS_DIR}/bin/libxess.dll)
    set(GN_XESS_FOUND ON)
endif()
option(GN_WITH_DLSS "Build NVIDIA DLSS in (needs Gears 2's third_party/dlss-sdk)" ${GN_DLSS_FOUND})
option(GN_WITH_FSR "Build AMD FSR in (needs Gears 2's third_party/fidelityfx-sdk)" ${GN_FFX_FOUND})
option(GN_WITH_XESS "Build Intel XeSS in (needs Gears 2's third_party/xess-sdk)" ${GN_XESS_FOUND})
message(STATUS "Gears1-Native: upscalers - DLSS ${GN_WITH_DLSS} (SDK found: ${GN_DLSS_FOUND}), FSR ${GN_WITH_FSR} (SDK found: ${GN_FFX_FOUND}), XeSS ${GN_WITH_XESS} (SDK found: ${GN_XESS_FOUND})")
if(GN_WITH_DLSS AND GN_DLSS_FOUND)
    target_sources(gn_layer0_xenos PRIVATE ${GN_LAYER0_DIR}/renderer/dlss_upscaler.cpp)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_HAVE_DLSS)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_DLSS_DIR}/include)
    target_link_libraries(gn_layer0_xenos PRIVATE ${GN_DLSS_DIR}/lib/Windows_x86_64/x64/nvsdk_ngx_d.lib)
    add_custom_command(TARGET gow1 POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_DLSS_DIR}/lib/Windows_x86_64/rel/nvngx_dlss.dll $<TARGET_FILE_DIR:gow1>
        COMMENT "Copying nvngx_dlss.dll next to gow1.exe")
endif()
if(GN_WITH_FSR AND GN_FFX_FOUND)
    target_sources(gn_layer0_xenos PRIVATE ${GN_LAYER0_DIR}/renderer/fsr_upscaler.cpp)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_HAVE_FSR)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_FFX_DIR}/api/include ${GN_FFX_DIR}/upscalers/include
        ${GN_FFX_DIR}/framegeneration/include)  # Gears 2 patch 0146: frame generation (job 040 failed without it)
    # ffx_api_loader.h is written for MSBuild (it checks _WINDOWS) - frame_generation.cpp uses it too (as Gears 2's CMake).
    set_source_files_properties(${GN_LAYER0_DIR}/renderer/fsr_upscaler.cpp ${GN_LAYER0_DIR}/renderer/frame_generation.cpp
        ${GN_LAYER0_DIR}/renderer/frame_generation_vk.cpp
        PROPERTIES COMPILE_DEFINITIONS _WINDOWS)
    # Notes/83: AMD FSR frame generation on Vulkan - AMD's Vulkan loader and frame-generation DLLs, when the SDK has them.
    if(EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_vk.dll AND EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_framegeneration_vk.dll)
        add_custom_command(TARGET gow1 POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_vk.dll
                ${GN_FFX_DIR}/signedbin/amd_fidelityfx_framegeneration_vk.dll $<TARGET_FILE_DIR:gow1>
            COMMENT "Copying the FidelityFX Vulkan loader and frame-generation DLLs next to gow1.exe")
    endif()
    # fsr049: AMD FSR SDK 2.4's Vulkan upscaler (FSR 4.1 / 3.1 on the Vulkan renderer).
    if(EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_vk.dll AND EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_upscaler_vk.dll)
        add_custom_command(TARGET gow1 POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_vk.dll
                ${GN_FFX_DIR}/signedbin/amd_fidelityfx_upscaler_vk.dll $<TARGET_FILE_DIR:gow1>
            COMMENT "Copying the FidelityFX Vulkan loader and upscaler DLLs next to gow1.exe")
    endif()
    add_custom_command(TARGET gow1 POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_FFX_DIR}/signedbin/amd_fidelityfx_loader_dx12.dll
            ${GN_FFX_DIR}/signedbin/amd_fidelityfx_upscaler_dx12.dll $<TARGET_FILE_DIR:gow1>
        COMMENT "Copying the FidelityFX loader + upscaler DLLs next to gow1.exe")
    # Gears 2 patches 0146/0147 (FSR frame generation, layer 0 frame_generation.cpp): its DLL too, when the SDK has it.
    if(EXISTS ${GN_FFX_DIR}/signedbin/amd_fidelityfx_framegeneration_dx12.dll)
        add_custom_command(TARGET gow1 POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_FFX_DIR}/signedbin/amd_fidelityfx_framegeneration_dx12.dll $<TARGET_FILE_DIR:gow1>
            COMMENT "Copying the FidelityFX frame generation DLL next to gow1.exe")
    endif()
endif()
if(GN_WITH_XESS AND GN_XESS_FOUND)
    target_sources(gn_layer0_xenos PRIVATE ${GN_LAYER0_DIR}/renderer/xess_upscaler.cpp)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_HAVE_XESS)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_XESS_DIR}/inc)
    # No import library: xess_upscaler.cpp loads libxess.dll when XeSS is chosen, so the exe starts without it.
    add_custom_command(TARGET gow1 POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GN_XESS_DIR}/bin/libxess.dll $<TARGET_FILE_DIR:gow1>
        COMMENT "Copying libxess.dll next to gow1.exe")
endif()
# Low-latency modes (Gears 2 patches 0135/0136, job 447b 780257d), the same blocks as Gears 2's CMakeLists pointing at
# its third_party (read-only): NVIDIA Reflex through NVAPI, AMD Anti-Lag 2 (header-only). latency_reflex.cpp comes in
# with the layer 0 glob and needs SDK patch 35 (rex/frame_hooks.h). cpu_check.cpp also comes in; it stays inactive here
# (no GN_CPU_LEVEL, as Gears 2 describes).
set(GN_NVAPI_DIR ${GN_THIRD_PARTY}/nvapi)
set(GN_NVAPI_FOUND OFF)
if(WIN32 AND EXISTS ${GN_NVAPI_DIR}/nvapi.h AND EXISTS ${GN_NVAPI_DIR}/amd64/nvapi64.lib)
    set(GN_NVAPI_FOUND ON)
endif()
# NVIDIA DLSS frame generation through Streamline (renderer/dlss_framegen.cpp, --gow2_frame_generation=dlss,
# experimental): the few public Streamline 2.14.16 headers it needs are vendored in src/layer0_xenos/streamline (MIT);
# there is no library to link - sl.interposer.dll is loaded at run time when the player has put NVIDIA's DLLs next to
# the exe (never copied by the build). OFF builds keep the same file with empty functions.
option(GN_WITH_STREAMLINE "Build NVIDIA DLSS frame generation (Streamline, loaded at run time) in" ${WIN32})
message(STATUS "Gears1-Native: NVIDIA DLSS frame generation (Streamline) ${GN_WITH_STREAMLINE}")
if(GN_WITH_STREAMLINE AND WIN32)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_HAVE_STREAMLINE)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_LAYER0_DIR}/streamline/include)
endif()
option(GN_WITH_REFLEX "Build NVIDIA Reflex in (needs Gears 2's third_party/nvapi)" ${GN_NVAPI_FOUND})
message(STATUS "Gears1-Native: NVIDIA Reflex ${GN_WITH_REFLEX} (NVAPI found: ${GN_NVAPI_FOUND})")
if(GN_WITH_REFLEX AND GN_NVAPI_FOUND)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_WITH_REFLEX)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_NVAPI_DIR})
    target_link_libraries(gn_layer0_xenos PRIVATE ${GN_NVAPI_DIR}/amd64/nvapi64.lib)
endif()
set(GN_AL2_DIR ${GN_THIRD_PARTY}/antilag2-sdk)
set(GN_AL2_FOUND OFF)
if(WIN32 AND EXISTS ${GN_AL2_DIR}/ffx_antilag2_dx12.h)
    set(GN_AL2_FOUND ON)
endif()
option(GN_WITH_ANTILAG2 "Build AMD Anti-Lag 2 in (needs Gears 2's third_party/antilag2-sdk)" ${GN_AL2_FOUND})
message(STATUS "Gears1-Native: AMD Anti-Lag 2 ${GN_WITH_ANTILAG2} (SDK found: ${GN_AL2_FOUND})")
if(GN_WITH_ANTILAG2 AND GN_AL2_FOUND)
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_WITH_ANTILAG2)
    target_include_directories(gn_layer0_xenos PRIVATE ${GN_AL2_DIR})
endif()

# Profile-guided optimisation (Shagg 14:20 "You need to set up PGO"; method from Gears 2 mail 20261025-135500,
# Gears2-Native patches 0159 / 0160 / 0161). The game thread is the frame-rate limit, and PGO lets the compiler lay out
# and inline the hot recompiled code by measured use (Gears 2: +5-7%).
#   GN_PGO=gen / irgen: training build. Execution counters are built in (-fprofile-instr-generate / -fprofile-generate),
#     and layer 0 writes them to LLVM_PROFILE_FILE once, at swap --gow2_pgo_dump_frame (layer 0 patch 0159, GN_PGO_GEN).
#   GN_PGO=use / iruse: optimised build with the merged profile GN_PGO_PROFILE (.profdata).
#   GN_PGO=off: normal build.
#   Not set: uses the stored profile out\pgo\gow1.profdata when it exists (a training job puts it there), else a normal
#     build. The profile comes from game runs, so it stays in out\ and is never committed.
# GN_PGO / GN_PGO_PROFILE come from the environment, like Gears 2, so they never stick in the CMake cache.
set(GN_PGO "" CACHE STRING "Profile-guided optimisation: empty/auto, off, gen, use, irgen or iruse")
set(GN_PGO_PROFILE "" CACHE FILEPATH "Merged .profdata for GN_PGO=use / iruse")
if(DEFINED ENV{GN_PGO})
    set(GN_PGO "$ENV{GN_PGO}")
endif()
if(DEFINED ENV{GN_PGO_PROFILE})
    set(GN_PGO_PROFILE "$ENV{GN_PGO_PROFILE}")
endif()
if(NOT DEFINED ENV{GN_PGO} AND (GN_PGO STREQUAL "" OR GN_PGO STREQUAL "auto"))
    set(gn_pgo_stored "${CMAKE_SOURCE_DIR}/out/pgo/gow1.profdata")
    if(EXISTS "${gn_pgo_stored}")
        set(GN_PGO "use")
        set(GN_PGO_PROFILE "${gn_pgo_stored}")
    else()
        message(STATUS "Gears1-Native: no stored profile ${gn_pgo_stored} - normal build (no PGO)")
    endif()
endif()
# gow1_recomp is the recompiled game code (rexglue_setup_target); there is no layer 1 target in Gears 1.
set(gn_pgo_targets gow1_recomp gow1 gn_layer0_xenos gn_layer2_ue3)
set(gn_pgo_gen_flag -fprofile-instr-generate)
set(gn_pgo_use_flag -fprofile-instr-use)
if(GN_PGO STREQUAL "irgen" OR GN_PGO STREQUAL "iruse")
    set(gn_pgo_gen_flag -fprofile-generate)
    set(gn_pgo_use_flag -fprofile-use)
endif()
if(GN_PGO STREQUAL "gen" OR GN_PGO STREQUAL "irgen")
    foreach(gn_t ${gn_pgo_targets})
        if(TARGET ${gn_t})
            target_compile_options(${gn_t} PRIVATE ${gn_pgo_gen_flag})
        endif()
    endforeach()
    target_compile_definitions(gn_layer0_xenos PRIVATE GN_PGO_GEN=1)
    # clang++ links the profile runtime (and -include:__llvm_profile_runtime) itself. PUBLIC, so every exe that links
    # the instrumented layer 0 (gow1, gn_tests does not link it) gets it.
    target_link_options(gn_layer0_xenos PUBLIC ${gn_pgo_gen_flag})
    target_link_options(gow1 PRIVATE ${gn_pgo_gen_flag})
    message(STATUS "Gears1-Native: PGO training build (execution counters, ${gn_pgo_gen_flag})")
elseif(GN_PGO STREQUAL "use" OR GN_PGO STREQUAL "iruse")
    if(NOT EXISTS "${GN_PGO_PROFILE}")
        message(FATAL_ERROR "GN_PGO=${GN_PGO}: profile '${GN_PGO_PROFILE}' not found")
    endif()
    # The profile's hash on the command line, so sccache never reuses an object built with an older profile at the
    # same path, and a new profile rebuilds everything it touches.
    file(MD5 "${GN_PGO_PROFILE}" gn_pgo_md5)
    foreach(gn_t ${gn_pgo_targets})
        if(TARGET ${gn_t})
            target_compile_definitions(${gn_t} PRIVATE GN_PGO_PROFILE_MD5=${gn_pgo_md5})
            target_compile_options(${gn_t} PRIVATE "${gn_pgo_use_flag}=${GN_PGO_PROFILE}" -Wno-profile-instr-unprofiled
                -Wno-profile-instr-out-of-date -Wno-profile-instr-missing -Wno-backend-plugin)
        endif()
    endforeach()
    message(STATUS "Gears1-Native: PGO optimised build (${gn_pgo_use_flag}, profile ${GN_PGO_PROFILE}, md5 ${gn_pgo_md5})")
else()
    message(STATUS "Gears1-Native: PGO off")
endif()

# Unit tests (game-free, Gears 2's layer 0 tests).
option(GN_BUILD_TESTS "Build the unit tests" ON)
if(GN_BUILD_TESTS)
    add_subdirectory(${GN_THIRD_PARTY}/catch2 catch2 EXCLUDE_FROM_ALL)
    add_executable(gn_tests
        ${GN_LAYER0_TESTS}/layer0_formats_test.cpp
        ${GN_LAYER0_TESTS}/layer0_renderer_core_test.cpp
        ${GN_LAYER0_TESTS}/layer0_pipeline_state_test.cpp
        ${GN_LAYER0_TESTS}/layer0_transfer_words_test.cpp
        ${GN_LAYER0_TESTS}/layer0_queries_test.cpp
        ${GN_LAYER0_TESTS}/layer0_colour_formats_test.cpp
        ${GN_LAYER0_TESTS}/layer0_pm4_test.cpp
        ${GN_LAYER0_DIR}/pm4.cpp
        ${GN_LAYER0_TESTS}/layer0_display_gamma_test.cpp
        ${GN_LAYER0_DIR}/display_gamma.cpp)
    target_include_directories(gn_tests PRIVATE ${GN_LAYER0_DIR}/include ${GN_LAYER0_TESTS})
    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
        target_compile_options(gn_tests PRIVATE -ffp-contract=off)
    elseif(MSVC)
        target_compile_options(gn_tests PRIVATE /fp:precise)
    endif()
    target_link_libraries(gn_tests PRIVATE Catch2::Catch2WithMain)
endif()

# (Gears 3 also builds gn_census / gn_hlsl_check from its tools folder; not copied yet.)
