mirror of
https://github.com/Toastconcern/FrameStudio.git
synced 2026-10-28 20:03:07 +01:00
Frame Studio - Gaussian splats (PLY, SPZ v2-v4), PLY vertex colours, panorama skies - Copy, cut, paste and duplicate for objects, splats and world items - Walkable-area rebake moved off the UI thread; viewport one frame sooner; refresh-locked pacing (FS_PACE=timer for the old one) - Fixed: bundle thumbnail use-after-free crash, auto-save deleting environments, saves into scratch copies, edits lost on non-ASCII paths, unopenable manifests with inf/nan, bundle save data loss, opening a home marking it changed, gizmo NaN, long paths, many malformed-file crashes - Updater verifies sha256, journals the swap and never leaves the exe missing Frame Home - Seats turn you to face their way - Draw batching (Hand Tutorials up to ~20% less GPU time), cut-out EQUAL pass, late head-pose read before submit - Proper queue-family hand-over to the compositor layer; layer descriptor generations and seqlocked slots; structural SPIR-V check - Fixed: hangs/crashes on damaged .fenv files and shaders, render-pass validation error, sync hazard in the foveation smoothing pass, unverified update downloads, half-done installs Tests: tests_loco/env_share/spv_check wired into CMake (FRAME_TESTS) and CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VsaXGU27gsrXjDoFtyFJP9
233 lines
12 KiB
CMake
233 lines
12 KiB
CMake
# Frame Studio — desktop app for Windows, Linux and macOS (same renderer as Frame Home, in a window with an editor
|
|
# UI). Everything it needs is vendored: Vulkan is loaded at runtime through volk (MoltenVK on macOS); the window is
|
|
# Win32 on Windows and GLFW elsewhere (studio/platform.h has the rest of the system). Presets: CMakePresets.json.
|
|
cmake_minimum_required(VERSION 3.16)
|
|
project(frame_studio LANGUAGES C CXX)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
if(NOT CMAKE_BUILD_TYPE)
|
|
set(CMAKE_BUILD_TYPE Release)
|
|
endif()
|
|
|
|
# The build's identity for updates (studio/updater.h): the commit CI built ($FRAME_BUILD); empty for a local build,
|
|
# which never updates itself.
|
|
# Defined on studio.cpp alone (below, with FRAME_VERSION): on every file it changed every one's command line each
|
|
# commit, and the compiler cache missed on all of them.
|
|
set(FRAME_BUILD "$ENV{FRAME_BUILD}")
|
|
|
|
set(ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
|
set(TP "${ROOT}/third_party")
|
|
include("${ROOT}/cmake/frame_version.cmake") # (FRAME_VERSION: on studio.cpp alone, so a new number rebuilds only it)
|
|
set(FRAME_HOME_PAYLOAD_DIR "$ENV{FRAME_HOME_PAYLOAD_DIR}" CACHE PATH "Built Frame Home payload to bundle with Studio")
|
|
if(NOT FRAME_HOME_PAYLOAD_DIR)
|
|
set(FRAME_HOME_PAYLOAD_DIR "${ROOT}/frame/payload")
|
|
endif()
|
|
# A release must carry it (CI: GitHub sets CI); a developer's build without one still builds, and its Install on Steam
|
|
# Frame says the headset files are missing.
|
|
option(FRAME_HOME_REQUIRE_PAYLOAD "Fail when the Frame Home payload is incomplete" "$ENV{CI}")
|
|
set(FRAME_HOME_PAYLOAD_COMPLETE ON)
|
|
foreach(FRAME_HOME_FILE install.sh bin/frame-home bin/frame-switcher bin/fenv-tool layer/libframe_home_layer.so layer/env_ps.spv)
|
|
if(NOT EXISTS "${FRAME_HOME_PAYLOAD_DIR}/${FRAME_HOME_FILE}")
|
|
set(FRAME_HOME_PAYLOAD_COMPLETE OFF)
|
|
set(FRAME_HOME_MISSING "${FRAME_HOME_FILE}")
|
|
endif()
|
|
endforeach()
|
|
if(NOT FRAME_HOME_PAYLOAD_COMPLETE)
|
|
set(FRAME_HOME_PAYLOAD_HELP "build or download it and set FRAME_HOME_PAYLOAD_DIR (${FRAME_HOME_MISSING} missing)")
|
|
if(FRAME_HOME_REQUIRE_PAYLOAD)
|
|
message(FATAL_ERROR "Frame Home payload is incomplete; ${FRAME_HOME_PAYLOAD_HELP}")
|
|
endif()
|
|
message(WARNING "Frame Home payload is incomplete, so this Studio can't install Frame Home; ${FRAME_HOME_PAYLOAD_HELP}")
|
|
endif()
|
|
|
|
if(WIN32)
|
|
add_compile_definitions(NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS VK_USE_PLATFORM_WIN32_KHR)
|
|
endif()
|
|
if(MSVC)
|
|
add_compile_options(/utf-8 /bigobj /W3 /wd4244 /wd4267 /wd4305)
|
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i[3-6]86|x86|X86|x86_64|AMD64)$")
|
|
# 32-bit x86 with gcc/clang: SSE2 on, as MSVC has it by default (rtm, under ACL, uses it on any x86)
|
|
add_compile_options(-msse2 -mfpmath=sse)
|
|
endif()
|
|
|
|
add_library(volk STATIC "${TP}/volk/volk.c")
|
|
target_include_directories(volk PUBLIC "${TP}/volk" "${TP}/vulkan-headers/include")
|
|
|
|
# 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")
|
|
# zstd 1.5.6 (BSD), decompression only: version 4 .spz splat files (src/splat_import.cpp). Single-threaded, plain C
|
|
# (no amd64 assembly), so it builds the same with every compiler.
|
|
add_library(zstd STATIC "${TP}/zstd/common/debug.c" "${TP}/zstd/common/entropy_common.c"
|
|
"${TP}/zstd/common/error_private.c" "${TP}/zstd/common/fse_decompress.c" "${TP}/zstd/common/xxhash.c"
|
|
"${TP}/zstd/common/zstd_common.c" "${TP}/zstd/decompress/huf_decompress.c" "${TP}/zstd/decompress/zstd_ddict.c"
|
|
"${TP}/zstd/decompress/zstd_decompress.c" "${TP}/zstd/decompress/zstd_decompress_block.c")
|
|
target_include_directories(zstd PUBLIC "${TP}/zstd")
|
|
target_compile_definitions(zstd PRIVATE ZSTD_DISABLE_ASM)
|
|
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")
|
|
|
|
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)
|
|
# astcenc's SIMD: NEON on ARM (Windows on ARM, Apple Silicon, ARM Linux), SSE4.1 on x86-64, none on 32-bit x86
|
|
# (its SSE4.1 code needs 64-bit popcount, which 32-bit MSVC doesn't have).
|
|
# (on macOS the architecture asked for decides: an Apple Silicon Mac builds the Intel version too)
|
|
if(CMAKE_OSX_ARCHITECTURES)
|
|
set(STUDIO_ARCH "${CMAKE_OSX_ARCHITECTURES}")
|
|
else()
|
|
set(STUDIO_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
|
|
endif()
|
|
if(STUDIO_ARCH MATCHES "^(arm64|ARM64|aarch64|AARCH64)$")
|
|
set(ASTCENC_ISA_NEON ON CACHE BOOL "" FORCE)
|
|
set(STUDIO_ASTCENC astcenc-neon-static)
|
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
|
set(ASTCENC_ISA_NONE ON CACHE BOOL "" FORCE)
|
|
set(STUDIO_ASTCENC astcenc-none-static)
|
|
else()
|
|
set(ASTCENC_ISA_SSE41 ON CACHE BOOL "" FORCE)
|
|
set(STUDIO_ASTCENC astcenc-sse4.1-static)
|
|
endif()
|
|
add_subdirectory("${TP}/astcenc" astcenc EXCLUDE_FROM_ALL)
|
|
|
|
# Ogg Opus ambience (Clay homes' _BACKGROUND_LOOP.ogg): Xiph libopus + libogg + opusfile (BSD), decode only.
|
|
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)
|
|
set(OPUS_STATIC_RUNTIME ON CACHE BOOL "" FORCE) # same static C runtime as the app (32-bit MSVC can't link a mix)
|
|
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)
|
|
|
|
# The window: Win32 on Windows; GLFW (X11 on Linux, Cocoa on macOS) elsewhere.
|
|
if(WIN32)
|
|
set(STUDIO_IMGUI_BACKEND "${TP}/imgui/backends/imgui_impl_win32.cpp")
|
|
set(STUDIO_PLATFORM platform_win32.cpp)
|
|
else()
|
|
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
|
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
|
set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
|
|
# (Wayland sessions run it through XWayland; GLFW's own Wayland backend needs wayland-scanner to build)
|
|
set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "" FORCE)
|
|
add_subdirectory("${TP}/glfw" glfw EXCLUDE_FROM_ALL)
|
|
set(STUDIO_IMGUI_BACKEND "${TP}/imgui/backends/imgui_impl_glfw.cpp")
|
|
set(STUDIO_PLATFORM platform_posix.cpp "${TP}/tinyfiledialogs/tinyfiledialogs.c")
|
|
endif()
|
|
|
|
add_library(imgui STATIC
|
|
"${TP}/imgui/imgui.cpp" "${TP}/imgui/imgui_draw.cpp" "${TP}/imgui/imgui_tables.cpp" "${TP}/imgui/imgui_widgets.cpp"
|
|
${STUDIO_IMGUI_BACKEND} "${TP}/imgui/backends/imgui_impl_vulkan.cpp"
|
|
"${TP}/imguizmo/ImGuizmo.cpp")
|
|
target_include_directories(imgui PUBLIC "${TP}/imgui" "${TP}/imguizmo")
|
|
# ImGuizmo reads matrix rows through casts to other types (*(vec_t*)&m[i] and the like): fine for MSVC, but GCC and
|
|
# Clang may optimize assuming no such aliasing, and Clang for x64 then turned gizmo drags into NaN transforms.
|
|
if(NOT MSVC)
|
|
set_source_files_properties("${TP}/imguizmo/ImGuizmo.cpp" PROPERTIES COMPILE_OPTIONS "-fno-strict-aliasing")
|
|
endif()
|
|
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK GLFW_INCLUDE_NONE)
|
|
target_link_libraries(imgui PUBLIC volk)
|
|
if(NOT WIN32)
|
|
target_link_libraries(imgui PUBLIC glfw)
|
|
endif()
|
|
|
|
set_source_files_properties(studio.cpp PROPERTIES COMPILE_DEFINITIONS "FRAME_VERSION=\"${FRAME_VERSION}\";FRAME_BUILD=\"${FRAME_BUILD}\"")
|
|
add_executable(frame-studio WIN32 MACOSX_BUNDLE
|
|
studio.cpp
|
|
${STUDIO_PLATFORM}
|
|
"${ROOT}/src/hsl_scene.cpp"
|
|
"${ROOT}/src/scene_bake.cpp"
|
|
"${ROOT}/src/anim_bake.cpp"
|
|
"${ROOT}/src/fenv_lzma.cpp"
|
|
"${ROOT}/src/model_import.cpp"
|
|
"${ROOT}/src/splat_import.cpp"
|
|
"${ROOT}/src/spv_opt.cpp"
|
|
"${TP}/ufbx/ufbx.c"
|
|
"${ROOT}/src/hsl_renderer.cpp"
|
|
"${ROOT}/src/hsl_renderer_splat.cpp"
|
|
"${TP}/qhe/cook/hzanim_acl.cpp"
|
|
"${TP}/stb/stb_image_impl.c"
|
|
"${TP}/stb/stb_vorbis_impl.c"
|
|
"${TP}/miniaudio/miniaudio_impl.c")
|
|
set_target_properties(frame-studio PROPERTIES OUTPUT_NAME "Frame Studio")
|
|
# ACL's NEON code mixes vector types GCC keeps apart by default (as the Frame's own build: CMakeLists.txt)
|
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64|ARM64|AARCH64)$")
|
|
set_source_files_properties("${TP}/qhe/cook/hzanim_acl.cpp" PROPERTIES COMPILE_OPTIONS "-flax-vector-conversions")
|
|
endif()
|
|
target_compile_definitions(frame-studio PRIVATE FH_USE_VOLK)
|
|
target_include_directories(frame-studio PRIVATE
|
|
"${ROOT}/src"
|
|
"${TP}/cgltf"
|
|
"${TP}/ufbx"
|
|
"${TP}/qhe"
|
|
"${TP}/stb"
|
|
"${TP}/miniaudio"
|
|
"${TP}/inter"
|
|
"${TP}/acl/includes"
|
|
"${TP}/rtm/includes"
|
|
"${TP}/astcenc/Source"
|
|
"${TP}/tinyfiledialogs")
|
|
target_link_libraries(frame-studio PRIVATE imgui volk miniz lzma zstd smolv opusfile ${STUDIO_ASTCENC})
|
|
# The SPIRV-Tools optimizer for src/spv_opt.h (FH_SPV_OPT=1 at run time). Off: on the Frame it made no shader any
|
|
# cheaper (Turnip's compiler already does what it does), and it is a large library to build everywhere.
|
|
option(FRAME_SPV_OPT "Build in the SPIRV-Tools shader optimizer (third_party/spirv-tools)" OFF)
|
|
if(FRAME_SPV_OPT)
|
|
add_subdirectory("${TP}/spirv-tools" spirv-tools EXCLUDE_FROM_ALL)
|
|
target_link_libraries(frame-studio PRIVATE spirv-tools-opt)
|
|
target_compile_definitions(frame-studio PRIVATE FH_HAVE_SPV_OPT)
|
|
endif()
|
|
if(WIN32)
|
|
target_link_libraries(frame-studio PRIVATE dwmapi comdlg32 shell32)
|
|
else()
|
|
find_package(Threads REQUIRED)
|
|
target_link_libraries(frame-studio PRIVATE glfw Threads::Threads ${CMAKE_DL_LIBS} m)
|
|
endif()
|
|
# The app icon (art/icon): a Windows resource, the macOS bundle's .icns, and on Linux a PNG beside the program
|
|
# that the window sets as its icon.
|
|
if(WIN32)
|
|
# The manifest (paths over 260 characters), merged into the program's: from the build folder, since mt.exe can't
|
|
# open it from a network path such as \\wsl.localhost (a source folder in WSL).
|
|
configure_file(studio.manifest "${CMAKE_CURRENT_BINARY_DIR}/studio.manifest" COPYONLY)
|
|
target_sources(frame-studio PRIVATE studio.rc "${CMAKE_CURRENT_BINARY_DIR}/studio.manifest")
|
|
elseif(APPLE)
|
|
set(STUDIO_ICNS "${ROOT}/art/icon/frame-studio.icns")
|
|
target_sources(frame-studio PRIVATE "${STUDIO_ICNS}")
|
|
set_source_files_properties("${STUDIO_ICNS}" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
|
else()
|
|
add_custom_command(TARGET frame-studio POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ROOT}/art/icon/frame-studio-256.png" "$<TARGET_FILE_DIR:frame-studio>/frame-studio.png")
|
|
endif()
|
|
if(APPLE)
|
|
set_target_properties(frame-studio PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Frame Studio" MACOSX_BUNDLE_GUI_IDENTIFIER "com.framestudio.app"
|
|
MACOSX_BUNDLE_ICON_FILE frame-studio.icns)
|
|
endif()
|
|
# Frame Home's headset side (arm64 binaries + install.sh) ships beside the exe, in frame\: Frame > Install on Steam Frame.
|
|
if(FRAME_HOME_PAYLOAD_COMPLETE)
|
|
add_custom_command(TARGET frame-studio POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${FRAME_HOME_PAYLOAD_DIR}" "$<TARGET_FILE_DIR:frame-studio>/frame")
|
|
endif()
|
|
if(MINGW) # MinGW builds (x64, x86 and ARM64): wWinMain entry point, no runtime DLLs to ship
|
|
target_link_options(frame-studio PRIVATE -municode -static)
|
|
endif()
|
|
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) # 32-bit: up to 4 GB of address space on 64-bit Windows instead of 2 (big homes)
|
|
if(MSVC)
|
|
target_link_options(frame-studio PRIVATE /LARGEADDRESSAWARE)
|
|
else()
|
|
target_link_options(frame-studio PRIVATE "LINKER:--large-address-aware")
|
|
endif()
|
|
endif()
|