Files
Toastconcern--FrameStudio/CMakeLists.txt
T
ToastconcernandClaude Opus 4.8 d22cc04cb3 Frame Home as the real SteamVR environment on a PC (Windows too), not a home app
On the Steam Frame a layer inside SteamVR's compositor draws the home where it would draw Aurora, so the home is the
environment itself: at boot, during game loads, behind the Steam menu, with the controllers and passthrough, and it
steps aside for a game the instant it opens. On a PC this was faked with SteamVR's home-app slot and a flat 360 photo
in the gaps, which launched and handed off like an app and showed a picture, not the live home. This does it properly
on any PC that runs SteamVR, the Frame's way:

- Windows: the compositor is Direct3D 11, so the layer is a proxy d3d11.dll beside the compositor (layer/
  frame_home_layer_d3d11.cpp, built from the .def that forwards every other export to the real d3d11.dll, kept as
  fh_d3d11real.dll). It wraps the compositor's device, finds Aurora's draws by their shader bytecode and draws the
  home over the sky, leaving the floor and stars off; everything else passes straight through. Pure pass-through in
  any process that isn't vrcompositor.
- The home's frames cross from Frame Home (Vulkan) to the compositor (D3D11): the layer owns kSlots shared textures
  and hands Frame Home their NT handles over a named pipe; Frame Home imports them (VK_KHR_external_memory_win32) and
  renders into them. No cross-API lock — a slot is published only once the GPU has finished it and the four rotate,
  as on Linux. The shared State stays Frame Home's.
- The Environments panel runs frame-home --system and keeps it alive (one at a time, a pid file across panel
  restarts), the same systemEnv path as the Frame. The installer places/removes the proxy and marks the layer with a
  root/layer folder, in place of the old home-app registration and background pictures.
- Safe like the Vulkan layer: no home, or one stale over half a second, and the compositor draws Aurora as usual;
  the layer turns itself off after two unhealthy compositor starts, and root/layer/off turns it off by hand.

Linux keeps its Vulkan layer (unchanged). Verified without a headset on the null driver: the proxy loads and wraps
the compositor without crashing, recognises Aurora's shaders, and the frame hand-over completes (Frame Home connects,
imports the textures, renders). The compositor only draws the environment while presenting to a headset, so the final
on-headset image is the one step the null driver can't exercise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-21 19:39:36 -05:00

170 lines
7.8 KiB
CMake

cmake_minimum_required(VERSION 3.16)
project(frame_home LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(TP "${CMAKE_SOURCE_DIR}/third_party")
# The Steam Frame build (SteamOS, arm64) links SteamVR's own OpenVR library and the system's Vulkan. A PC build
# (Windows, or x86-64 Linux) has to run on any PC with SteamVR: it finds SteamVR's OpenVR library at run time
# (src/openvr_loader.cpp) and loads Vulkan the same way (volk), so it depends on neither at build time.
if(WIN32 OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
set(FRAME_HOME_PC_DEFAULT ON)
else()
set(FRAME_HOME_PC_DEFAULT OFF)
endif()
option(FRAME_HOME_PC "Build Frame Home for a PC running SteamVR (OpenVR and Vulkan found at run time)" ${FRAME_HOME_PC_DEFAULT})
if(FRAME_HOME_PC)
add_library(volk STATIC "${TP}/volk/volk.c")
target_include_directories(volk PUBLIC "${TP}/volk" "${TP}/vulkan-headers/include")
if(NOT WIN32)
target_link_libraries(volk PUBLIC dl)
endif()
add_library(openvr-loader STATIC src/openvr_loader.cpp)
target_include_directories(openvr-loader PUBLIC "${TP}/openvr")
target_compile_definitions(openvr-loader PUBLIC OPENVR_BUILD_STATIC)
if(WIN32)
target_link_libraries(openvr-loader PUBLIC shell32 ole32)
else()
target_link_libraries(openvr-loader PUBLIC dl)
endif()
set(FH_VULKAN volk)
set(FH_OPENVR openvr-loader)
else()
# SteamVR ships the OpenVR client library; link against the runtime's own copy.
set(OPENVR_LIB_DIR "/opt/steamvr/bin/linuxarm64" CACHE PATH "Directory containing libopenvr_api.so")
find_library(OPENVR_LIB NAMES openvr_api PATHS "${OPENVR_LIB_DIR}" NO_DEFAULT_PATH REQUIRED)
find_package(Vulkan REQUIRED)
set(FH_VULKAN Vulkan::Vulkan)
set(FH_OPENVR "${OPENVR_LIB}")
endif()
# ── Editor loader stack (Quest Home Editor: ASMH/HSTF/RENDMESH/MATL/RENDTXTR/RENDSHAD) ──
add_library(miniz STATIC "${TP}/miniz/miniz.c" "${TP}/miniz/miniz_tdef.c" "${TP}/miniz/miniz_tinfl.c" "${TP}/miniz/miniz_zip.c")
target_include_directories(miniz PUBLIC "${TP}/miniz")
# APKs are signed and intact; re-checksumming every extracted asset was ~20% of load time.
target_compile_definitions(miniz PRIVATE MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS)
add_library(smolv STATIC "${TP}/smolv/smolv.cpp")
target_include_directories(smolv PUBLIC "${TP}/smolv")
# astcenc: decoder for the homes' ASTC textures (LDR + HDR lightmaps).
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(ASTCENC_CLI OFF CACHE BOOL "" FORCE)
set(ASTCENC_DECOMPRESSOR OFF CACHE BOOL "" FORCE)
set(ASTCENC_UNIVERSAL_BUILD OFF CACHE BOOL "" FORCE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
set(ASTCENC_ISA_NEON ON CACHE BOOL "" FORCE)
set(ASTCENC_LIB astcenc-neon-static)
else()
set(ASTCENC_ISA_SSE41 ON CACHE BOOL "" FORCE)
set(ASTCENC_LIB astcenc-sse4.1-static)
endif()
add_subdirectory("${TP}/astcenc" astcenc EXCLUDE_FROM_ALL)
# LZMA SDK (public domain): the .fenv scene coder (src/fenv_lzma.cpp), with its threaded match finder.
add_library(lzma STATIC "${TP}/lzma/LzmaEnc.c" "${TP}/lzma/LzmaDec.c" "${TP}/lzma/LzFind.c" "${TP}/lzma/LzFindOpt.c"
"${TP}/lzma/LzFindMt.c" "${TP}/lzma/Threads.c" "${TP}/lzma/CpuArch.c")
target_include_directories(lzma PUBLIC "${TP}/lzma")
# The home loader: HSL / Clay / glTF homes, .fenv environments (baked scenes), Blender / Unity models.
add_library(frame-scene STATIC
src/hsl_scene.cpp
src/scene_bake.cpp
src/anim_bake.cpp
src/fenv_lzma.cpp
src/model_import.cpp
"${TP}/ufbx/ufbx.c"
"${TP}/qhe/cook/hzanim_acl.cpp"
"${TP}/stb/stb_image_impl.c")
target_include_directories(frame-scene PUBLIC
src
"${TP}/stb"
"${TP}/qhe"
"${TP}/miniz"
"${TP}/cgltf"
"${TP}/ufbx"
"${TP}/acl/includes"
"${TP}/rtm/includes"
"${TP}/astcenc/Source")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_source_files_properties("${TP}/qhe/cook/hzanim_acl.cpp" PROPERTIES COMPILE_OPTIONS "-flax-vector-conversions")
endif()
target_link_libraries(frame-scene PUBLIC miniz lzma smolv ${ASTCENC_LIB} pthread)
# libdeflate when the system has it (the Frame): APK scene.zips inflate several times faster than with miniz.
find_library(LIBDEFLATE_LIB deflate)
find_path(LIBDEFLATE_INC libdeflate.h)
if(LIBDEFLATE_LIB AND LIBDEFLATE_INC)
target_compile_definitions(frame-scene PUBLIC QHE_HAVE_LIBDEFLATE)
target_include_directories(frame-scene PUBLIC "${LIBDEFLATE_INC}")
target_link_libraries(frame-scene PUBLIC "${LIBDEFLATE_LIB}")
endif()
# Ogg Opus ambience (Clay homes' _BACKGROUND_LOOP.ogg, or a sound added in Frame Studio): Xiph libopus + libogg +
# opusfile (BSD), decode only — as Frame Studio has it, so the headset plays every sound the preview does.
set(OPUS_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE)
set(OPUS_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF CACHE BOOL "" FORCE)
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF CACHE BOOL "" FORCE)
add_subdirectory("${TP}/opus" opus EXCLUDE_FROM_ALL)
add_library(ogg STATIC "${TP}/ogg/src/bitwise.c" "${TP}/ogg/src/framing.c")
target_include_directories(ogg PUBLIC "${TP}/ogg/include")
add_library(opusfile STATIC "${TP}/opusfile/src/info.c" "${TP}/opusfile/src/internal.c"
"${TP}/opusfile/src/opusfile.c" "${TP}/opusfile/src/stream.c")
target_include_directories(opusfile PUBLIC "${TP}/opusfile/include" PRIVATE "${TP}/opusfile/src")
target_compile_definitions(opusfile PRIVATE OP_DISABLE_HTTP)
target_link_libraries(opusfile PUBLIC opus ogg)
add_executable(frame-home
src/main.cpp
src/hsl_renderer.cpp
src/panorama.cpp
"${TP}/stb/stb_vorbis_impl.c"
"${TP}/miniaudio/miniaudio_impl.c")
target_include_directories(frame-home PRIVATE "${TP}/openvr" "${TP}/miniaudio")
target_link_libraries(frame-home PRIVATE frame-scene opusfile ${FH_VULKAN} ${FH_OPENVR})
if(FRAME_HOME_PC)
target_compile_definitions(frame-home PRIVATE FH_USE_VOLK)
endif()
if(WIN32)
target_link_libraries(frame-home PRIVATE psapi ole32 shell32 winmm)
target_link_options(frame-home PRIVATE -static)
else()
target_link_libraries(frame-home PRIVATE dl m)
endif()
if(NOT FRAME_HOME_PC)
set_target_properties(frame-home PROPERTIES BUILD_RPATH "${OPENVR_LIB_DIR}" INSTALL_RPATH "${OPENVR_LIB_DIR}")
endif()
# The Windows compositor layer: a proxy d3d11.dll that wraps vrcompositor's Direct3D 11 device and draws the home in
# Aurora's place (layer/frame_home_layer_d3d11.cpp). The .def forwards every other d3d11 export to the real library.
if(WIN32 AND FRAME_HOME_PC)
add_library(frame-home-d3d11 MODULE layer/frame_home_layer_d3d11.cpp layer/d3d11_proxy.def)
set_target_properties(frame-home-d3d11 PROPERTIES OUTPUT_NAME d3d11 PREFIX "")
target_include_directories(frame-home-d3d11 PRIVATE src)
target_link_libraries(frame-home-d3d11 PRIVATE dxguid uuid)
target_link_options(frame-home-d3d11 PRIVATE -static -static-libgcc -static-libstdc++)
endif()
# fenv-tool: convert homes and models into .fenv environments.
add_executable(fenv-tool src/fenv_tool.cpp)
target_link_libraries(fenv-tool PRIVATE frame-scene)
# frame-switcher: the SteamVR dashboard panel that switches the environment.
add_executable(frame-switcher src/switcher.cpp)
target_include_directories(frame-switcher PRIVATE src "${TP}/stb" "${TP}/qhe" "${TP}/miniz" "${TP}/openvr")
if(FRAME_HOME_PC)
target_include_directories(frame-switcher PRIVATE "${TP}/vulkan-headers/include")
endif()
target_link_libraries(frame-switcher PRIVATE frame-scene ${FH_OPENVR})
if(WIN32)
target_link_libraries(frame-switcher PRIVATE psapi ole32 shell32)
target_link_options(frame-switcher PRIVATE -static)
endif()
if(NOT FRAME_HOME_PC)
set_target_properties(frame-switcher PROPERTIES BUILD_RPATH "${OPENVR_LIB_DIR}" INSTALL_RPATH "${OPENVR_LIB_DIR}")
endif()