Files
UsdLayerManager/CMakeLists.txt
T
indigo fb1ae9d3a6 Add custom viewport color correction (sRGB/OCIO), bypassing HdxColorCorrectionTask
Hydra's HdxColorCorrectionTask rendered prims black in OCIO mode and could
corrupt the GlfDrawTarget bind stack on failure (skipping Unbind), blacking
out every later frame including sRGB. Replace it with our own GL post-process:
the scene renders linear (RGBA16F) and is corrected by a fullscreen shader --
linear->sRGB encode, or OCIO via the OCIO 2.1 GPU API (GpuShaderDesc plus
uploaded 1D/3D LUT textures). OCIO build failures fall back to sRGB (never
black) and USD diagnostics are routed to the app log.

- core: ViewportColorCorrector + ApplyViewportColorCorrection in UsdSceneRenderer
- utils: OcioConfigParser enumerates displays/views/colorspaces/looks from $OCIO
- ui: gear-menu OCIO controls (ViewportTile) + per-viewport persistence (ViewportPanel)
- Application: point $OCIO at the bundled ACES 1.2 config
- CMake: link/copy OpenColorIO, download ACES 1.2 config; plus hdCycles build
  config (disable OpenVDB/Embree, fix TBB/OpenSubdiv/Imath dirs, exclude CRT DLLs)
- main: pre-flight plugin DLL load check to skip plugins with missing deps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:10:44 +08:00

762 lines
31 KiB
CMake

cmake_minimum_required(VERSION 3.20)
project(UsdLayerManager VERSION 1.0.0 LANGUAGES C CXX)
# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Add cmake modules path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
# Find dependencies
find_package(OpenGL REQUIRED)
find_package(OpenUSD REQUIRED)
find_package(Imgui REQUIRED)
find_package(Glad REQUIRED)
find_package(FFmpeg REQUIRED)
# OpenColorIO — provided by the OpenUSD distribution (USD built with OCIO support)
set(OpenColorIO_DIR
"${OpenUSD_ROOT_DIR}/lib/cmake/OpenColorIO"
CACHE PATH "OpenColorIO CMake config dir" FORCE)
find_package(OpenColorIO CONFIG REQUIRED)
# USD build that includes hdEmbree (built with PXR_ENABLE_EMBREE_PLUGIN=ON).
# Can be a separate install from OpenUSD_ROOT_DIR; leave empty to search there instead.
set(HDEMBREE_USD_ROOT "" CACHE PATH "USD build containing hdEmbree plugin (separate from OpenUSD_ROOT_DIR)")
# Only needed if embree*.dll are not inside HDEMBREE_USD_ROOT/bin.
set(EMBREE_LOCATION "" CACHE PATH "Embree SDK bin/ directory (leave empty if bundled in HDEMBREE_USD_ROOT/bin)")
# ── hdEmbree detection ───────────────────────────────────────────────────────
if(HDEMBREE_USD_ROOT AND EXISTS "${HDEMBREE_USD_ROOT}")
set(_hdembree_root "${HDEMBREE_USD_ROOT}")
else()
set(_hdembree_root "${OpenUSD_ROOT_DIR}")
endif()
# hdEmbree.dll lives at plugin/usd/hdEmbree.dll; the subdir holds only plugin metadata.
set(HDEMBREE_PLUGIN_DIR "${_hdembree_root}/plugin/usd/hdEmbree")
set(HDEMBREE_DLL "${_hdembree_root}/plugin/usd/hdEmbree.dll")
if(EXISTS "${HDEMBREE_PLUGIN_DIR}")
set(OPENUSD_HAS_EMBREE TRUE)
if(EMBREE_LOCATION AND EXISTS "${EMBREE_LOCATION}")
set(_embree_dll_dir "${EMBREE_LOCATION}")
else()
set(_embree_dll_dir "${_hdembree_root}/bin")
endif()
# Skip tbb*.dll — already supplied by the main USD install to avoid conflicts.
file(GLOB EMBREE_DLLS "${_embree_dll_dir}/embree*.dll")
else()
set(OPENUSD_HAS_EMBREE FALSE)
message(STATUS "hdEmbree: NOT found — set HDEMBREE_USD_ROOT to a USD build with PXR_ENABLE_EMBREE_PLUGIN=ON")
endif()
# ── hdArnold detection ───────────────────────────────────────────────────────
# HDARNOLD_ROOT: CMAKE_INSTALL_PREFIX used when building arnold-usd
set(HDARNOLD_ROOT "" CACHE PATH "arnold-usd install directory (the one containing plugin/hdArnold.dll)")
# ARNOLD_LOCATION: Arnold SDK root (MtoA dir) — needed to deploy ai.dll
set(ARNOLD_LOCATION "" CACHE PATH "Arnold SDK root (e.g. C:/Autodesk/mtoa/5.5.0/2026) for ai.dll deployment")
if(HDARNOLD_ROOT AND EXISTS "${HDARNOLD_ROOT}/plugin/hdArnold.dll")
set(OPENUSD_HAS_ARNOLD TRUE)
set(HDARNOLD_DLL "${HDARNOLD_ROOT}/plugin/hdArnold.dll")
set(NDRARNOLD_DLL "${HDARNOLD_ROOT}/plugin/ndrArnold.dll")
set(HDARNOLD_PLUGIN_DIR "${HDARNOLD_ROOT}/plugin/hdArnold")
set(NDRARNOLD_PLUGIN_DIR "${HDARNOLD_ROOT}/plugin/ndrArnold")
if(ARNOLD_LOCATION AND EXISTS "${ARNOLD_LOCATION}/bin/ai.dll")
set(ARNOLD_AI_DLL "${ARNOLD_LOCATION}/bin/ai.dll")
# ai.dll also requires companion runtime DLLs (OIDN denoiser, Adsk licensing, cer)
file(GLOB ARNOLD_RUNTIME_DLLS
"${ARNOLD_LOCATION}/bin/ai*.dll"
"${ARNOLD_LOCATION}/bin/AdClm*.dll"
"${ARNOLD_LOCATION}/bin/AdskLicensing*.dll"
"${ARNOLD_LOCATION}/bin/cer.dll"
)
else()
set(ARNOLD_AI_DLL "")
set(ARNOLD_RUNTIME_DLLS "")
endif()
else()
set(OPENUSD_HAS_ARNOLD FALSE)
message(STATUS "hdArnold: NOT found — set HDARNOLD_ROOT to the arnold-usd install dir and ARNOLD_LOCATION to the MtoA root")
endif()
# ── Cycles hdCycles Hydra render delegate ────────────────────────────────────
# Build Blender Cycles from third_party/cycles using ExternalProject.
# Set WITH_CYCLES=ON to enable; requires MSVC, and uses the precompiled libs
# bundled in third_party/cycles/lib/windows_x64/.
# hdCycles.dll is deployed to usd/ alongside hdEmbree and hdArnold.
option(WITH_CYCLES "Build Cycles Hydra render delegate from third_party/cycles" OFF)
set(CYCLES_BINARY_DIR "${CMAKE_BINARY_DIR}/cycles_build")
set(CYCLES_INSTALL_DIR "${CMAKE_BINARY_DIR}/cycles_install")
set(HDCYCLES_DLL "${CYCLES_INSTALL_DIR}/hydra/hdCycles.dll")
set(HDCYCLES_PLUGIN_DIR "${CYCLES_INSTALL_DIR}/hydra/hdCycles")
# tbb12.dll is required by openvdb/embree4 but lives in the precompiled lib tree,
# not in the install output — deploy it explicitly.
set(CYCLES_TBB12_DLL "${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/tbb/bin/tbb12.dll")
if(WITH_CYCLES)
include(ExternalProject)
# Resolve absolute USD root so we can forward it to the sub-build.
# FindOpenUSD.cmake stores the path in OpenUSD_ROOT_DIR.
if(NOT OpenUSD_ROOT_DIR)
message(FATAL_ERROR "WITH_CYCLES requires OpenUSD_ROOT_DIR to be set (run find_package(OpenUSD) first).")
endif()
ExternalProject_Add(CyclesBuild
SOURCE_DIR "${CMAKE_SOURCE_DIR}/third_party/cycles"
BINARY_DIR "${CYCLES_BINARY_DIR}"
INSTALL_DIR "${CYCLES_INSTALL_DIR}"
CMAKE_ARGS
# Always Release — Cycles' Debug build is slow and rarely needed here.
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
# Point Cycles at our app's USD so hdCycles is ABI-compatible.
-DPXR_ROOT=${OpenUSD_ROOT_DIR}
# Hydra delegate is the only deliverable we need.
-DWITH_CYCLES_HYDRA_RENDER_DELEGATE=ON
-DWITH_CYCLES_USD=ON
# Use bundled precompiled libs from lib/windows_x64/.
-DWITH_LIBS_PRECOMPILED=ON
# Turn off heavy optional features that aren't needed for the delegate.
-DWITH_CYCLES_STANDALONE_GUI=OFF
-DWITH_CYCLES_DEVICE_CUDA=OFF
-DWITH_CYCLES_DEVICE_OPTIX=OFF
-DWITH_CYCLES_DEVICE_HIP=OFF
-DWITH_CYCLES_DEVICE_ONEAPI=OFF
-DWITH_CYCLES_LOGGING=OFF
# USD ships OpenVDB v9; Cycles prebuilt is v13 — ABI mismatch, both named
# openvdb.dll. Disable openvdb in Cycles to avoid the version conflict.
-DWITH_CYCLES_OPENVDB=OFF
# Embree4 links tbb12.dll; USD already loads tbb.dll at process startup.
# oneTBB detects the older tbb.dll and aborts initialization (DLL_INIT_FAILED).
# Disable Embree so Cycles uses its own BVH traversal instead.
-DWITH_CYCLES_EMBREE=OFF
# ---- pxrConfig.cmake dependency overrides ----
# pxrConfig.cmake runs find_dependency() for these before Cycles'
# precompiled lib paths are on CMAKE_PREFIX_PATH, so we must supply
# them explicitly. The Python3 vars also override the hardcoded
# CI build-machine path embedded in pxrConfig.cmake.
-DPython3_EXECUTABLE=$ENV{LOCALAPPDATA}/Programs/Python/Python312/python.exe
-DPython3_LIBRARY=$ENV{LOCALAPPDATA}/Programs/Python/Python312/libs/python312.lib
-DPython3_INCLUDE_DIR=$ENV{LOCALAPPDATA}/Programs/Python/Python312/include
-DTBB_DIR=${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/tbb/lib/cmake/TBB
-DMaterialX_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/MaterialX
# OpenSubdiv and Imath cmake configs are bundled in the USD install.
# We must supply their dirs so pxrTargets.cmake can resolve the imported targets.
-DOpenSubdiv_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/OpenSubdiv
-DImath_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/Imath
# Force Cycles to use its own prebuilt OCIO 2.5, not the OCIO 2.1
# bundled in the new USD include dir. Without this, the new USD's
# include/OpenColorIO/ (OCIO 2.1) shadows Cycles' prebuilt OCIO 2.5
# headers and the colorspace.cpp API calls fail to compile.
-DOpenColorIO_DIR=${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/opencolorio/lib/cmake/OpenColorIO
BUILD_COMMAND
${CMAKE_COMMAND} --build <BINARY_DIR> --config Release
INSTALL_COMMAND
${CMAKE_COMMAND} --install <BINARY_DIR> --config Release
BUILD_BYPRODUCTS
"${HDCYCLES_DLL}"
)
set(OPENUSD_HAS_CYCLES TRUE)
else()
set(OPENUSD_HAS_CYCLES FALSE)
message(STATUS "hdCycles: disabled — set WITH_CYCLES=ON to build from third_party/cycles")
endif()
# Collect source files
file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp")
file(GLOB_RECURSE UI_SOURCES "src/ui/*.cpp")
file(GLOB_RECURSE UTILS_SOURCES "src/utils/*.cpp")
set(MAIN_SOURCE "src/main.cpp")
# Create executable
add_executable(UsdLayerManager
${MAIN_SOURCE}
${CORE_SOURCES}
${UI_SOURCES}
${UTILS_SOURCES}
)
# Include directories
target_include_directories(UsdLayerManager PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/third_party/fontawesome
${CMAKE_SOURCE_DIR}/third_party/nanosvg
)
# Link libraries
target_link_libraries(UsdLayerManager PRIVATE
OpenUSD::OpenUSD
Imgui::Imgui
Glad::Glad
FFmpeg::FFmpeg
OpenColorIO::OpenColorIO
ole32
shell32
)
target_include_directories(UsdLayerManager PRIVATE
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
# Cycles is an ExternalProject; UsdLayerManager must wait for it to finish
# before POST_BUILD copies hdCycles.dll to the output directory.
if(WITH_CYCLES)
add_dependencies(UsdLayerManager CyclesBuild)
endif()
# Platform-specific settings
if(WIN32)
# Windows-specific settings
target_compile_definitions(UsdLayerManager PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
# OpenColorIO DLL — copy from USD bin/ (which already ships OpenColorIO_2_1.dll)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${OpenUSD_ROOT_DIR}/bin/OpenColorIO_2_1.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying OpenColorIO runtime DLL..."
)
# FFmpeg DLLs — copy all .dll files from third_party/ffmpeg/bin
file(GLOB FFMPEG_RUNTIME_DLLS "${CMAKE_SOURCE_DIR}/third_party/ffmpeg/bin/*.dll")
if(FFMPEG_RUNTIME_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${FFMPEG_RUNTIME_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying FFmpeg runtime DLLs..."
)
endif()
# cmake -P script to copy USD bin/ DLLs excluding CRT DLLs (msvcp, vcruntime, etc.).
# The USD distribution bundles older CRT DLLs (14.34) that conflict with Cycles
# binaries built against the newer system CRT (14.42+). The system CRT is
# always present on Windows 10+ — no need to bundle our own copy.
file(WRITE "${CMAKE_BINARY_DIR}/copy_usd_bin_dlls.cmake"
"file(GLOB _all_dlls \"${OpenUSD_ROOT_DIR}/bin/*.dll\")\n"
"set(_dlls)\n"
"foreach(_dll \${_all_dlls})\n"
" get_filename_component(_name \"\${_dll}\" NAME)\n"
" string(TOLOWER \"\${_name}\" _lname)\n"
" if(_lname MATCHES \"^(msvcp|vcruntime|ucrtbase|concrt|api-ms-win-)\")\n"
" continue()\n"
" endif()\n"
" list(APPEND _dlls \"\${_dll}\")\n"
"endforeach()\n"
"if(_dlls)\n"
" file(COPY \${_dlls} DESTINATION \"\${DEST_DIR}\")\n"
"endif()\n"
)
# Copy runtime DLLs to output directory for each configuration
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>"
# OpenUSD lib/ — usd_*.dll, boost_*.dll, Alembic.dll, etc.
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>"
# OpenUSD bin/ — tbb.dll, MaterialX, OpenEXR, OpenImageIO, etc. (CRT DLLs excluded)
COMMAND ${CMAKE_COMMAND}
"-DDEST_DIR=$<TARGET_FILE_DIR:UsdLayerManager>"
-P "${CMAKE_BINARY_DIR}/copy_usd_bin_dlls.cmake"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying runtime DLLs and USD plugins..."
)
if(OPENUSD_HAS_EMBREE)
add_custom_command(TARGET UsdLayerManager POST_BUILD
# plugInfo.json has LibraryPath "../hdEmbree.dll" relative to usd/hdEmbree/,
# which resolves to usd/hdEmbree.dll — so the DLL must live in usd/, not exe root.
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDEMBREE_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdEmbree.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDEMBREE_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdEmbree"
COMMENT "Copying hdEmbree plugin..."
)
if(EMBREE_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${EMBREE_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying Embree runtime DLLs..."
)
endif()
endif()
if(OPENUSD_HAS_ARNOLD)
add_custom_command(TARGET UsdLayerManager POST_BUILD
# DLLs go in usd/ alongside their metadata dirs (LibraryPath "../hdArnold.dll")
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDARNOLD_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdArnold.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${NDRARNOLD_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/ndrArnold.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDARNOLD_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdArnold"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${NDRARNOLD_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/ndrArnold"
COMMENT "Copying hdArnold plugin..."
)
if(ARNOLD_RUNTIME_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ARNOLD_RUNTIME_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying Arnold runtime DLLs (ai, OIDN, licensing)..."
)
endif()
endif()
if(OPENUSD_HAS_CYCLES)
# hdCycles uses the same usd/ layout as hdEmbree:
# usd/hdCycles.dll + usd/hdCycles/resources/plugInfo.json
# The bundled plugInfo.json has Root=".." and LibraryPath="../hdCycles.dll"
# which resolves correctly once the plugin dir lives in usd/.
#
# Runtime deps (embree4, epoxy, imath, openvdb, OIDN, sycl, vulkan, etc.)
# live in CYCLES_INSTALL_DIR root and must be next to the exe. We can't
# file(GLOB) them at configure time (ExternalProject hasn't run yet), so we
# write a small cmake -P script that globs them at build time instead.
# The script runs at build time (after ExternalProject finishes) so it can glob
# the actual install output. CRT/Windows DLLs are excluded — they must come
# from the system or the main exe's toolchain, not from Cycles' bundled copy.
file(WRITE "${CMAKE_BINARY_DIR}/copy_cycles_runtime_dlls.cmake"
"file(GLOB _all_dlls \"${CYCLES_INSTALL_DIR}/*.dll\")\n"
"set(_dlls)\n"
"foreach(_dll \${_all_dlls})\n"
" get_filename_component(_name \"\${_dll}\" NAME)\n"
" string(TOLOWER \"\${_name}\" _lname)\n"
# Exclude CRT DLLs (system must supply these; Cycles bundles an older version).
" if(_lname MATCHES \"^(msvcp|vcruntime|ucrtbase|concrt|api-ms-win-)\")\n"
" continue()\n"
" endif()\n"
# Exclude debug-variant DLLs (not needed in release, just wasted space).
# Pattern: OpenColorIO_d_2_5.dll → contains "_d_"
# openjph.0.25d.dll → ends with "d.dll"
" if(_lname MATCHES \"_d_\" OR _lname MATCHES \"d[.]dll$\")\n"
" continue()\n"
" endif()\n"
" list(APPEND _dlls \"\${_dll}\")\n"
"endforeach()\n"
"if(_dlls)\n"
" file(COPY \${_dlls} DESTINATION \"\${DEST_DIR}\")\n"
"endif()\n"
)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDCYCLES_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdCycles.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDCYCLES_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdCycles"
COMMAND ${CMAKE_COMMAND}
"-DDEST_DIR=$<TARGET_FILE_DIR:UsdLayerManager>"
-P "${CMAKE_BINARY_DIR}/copy_cycles_runtime_dlls.cmake"
# tbb12.dll is required by openvdb/embree4 but is not in the install output
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CYCLES_TBB12_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/tbb12.dll"
COMMENT "Copying hdCycles plugin and runtime DLLs..."
)
endif()
endif()
# Compiler warnings
if(MSVC)
target_compile_options(UsdLayerManager PRIVATE /W4)
else()
target_compile_options(UsdLayerManager PRIVATE -Wall -Wextra -Wpedantic)
endif()
# Install rules
install(TARGETS UsdLayerManager
RUNTIME DESTINATION bin
)
# Install all OpenUSD runtime DLLs (same set that POST_BUILD copies to build/Release).
# Without these the exe cannot start from install/bin.
install(DIRECTORY ${OpenUSD_BIN_DIR}/
DESTINATION bin
FILES_MATCHING PATTERN "*.dll"
)
# Install OpenUSD bin/ DLLs — tbb.dll, tbbmalloc.dll, MaterialX, OpenEXR,
# OpenImageIO, zlib, etc. Exclude CRT DLLs: the system VC++ Redistributable supplies
# these at runtime; bundling an older copy (14.34) causes crashes on machines with
# Cycles or other DLLs compiled against the newer CRT (14.42+).
install(DIRECTORY ${OpenUSD_ROOT_DIR}/bin/
DESTINATION bin
FILES_MATCHING PATTERN "*.dll"
PATTERN "msvcp*" EXCLUDE
PATTERN "vcruntime*" EXCLUDE
PATTERN "ucrtbase*" EXCLUDE
PATTERN "concrt*" EXCLUDE
PATTERN "api-ms-win-*" EXCLUDE
)
install(DIRECTORY ${OpenUSD_ROOT_DIR}/lib/usd
DESTINATION bin
)
install(DIRECTORY ${OpenUSD_ROOT_DIR}/plugin/usd/
DESTINATION bin/usd
)
if(OPENUSD_HAS_EMBREE)
# Install to bin/usd/ so plugInfo.json's LibraryPath "../hdEmbree.dll" resolves correctly.
install(FILES "${HDEMBREE_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDEMBREE_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
if(EMBREE_DLLS)
install(FILES ${EMBREE_DLLS}
DESTINATION bin
OPTIONAL
)
endif()
endif()
if(OPENUSD_HAS_ARNOLD)
install(FILES "${HDARNOLD_DLL}" "${NDRARNOLD_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDARNOLD_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${NDRARNOLD_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
if(ARNOLD_RUNTIME_DLLS)
install(FILES ${ARNOLD_RUNTIME_DLLS}
DESTINATION bin
OPTIONAL
)
endif()
endif()
if(OPENUSD_HAS_CYCLES)
install(FILES "${HDCYCLES_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDCYCLES_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
# Cycles runtime deps (embree4, epoxy, imath, openvdb, OIDN, sycl, etc.).
# No OPTIONAL: install(DIRECTORY) disallows it with FILES_MATCHING, and the
# dir always exists here since it is produced by the CyclesBuild step.
#
# EXCLUDE the MSVC CRT and api-ms-win DLLs that Cycles bundles: they are
# older than what the system ships (v14.34 vs system v14.42+), and placing
# an older MSVCP140.dll/VCRUNTIME140.dll ahead of System32 on the DLL
# search path causes sycl8.dll (built with newer CRT) to fail DllMain with
# ERROR_DLL_INIT_FAILED (1114) when hdCycles.dll is loaded.
# The system VC++ Redistributable always satisfies these deps at runtime.
install(DIRECTORY "${CYCLES_INSTALL_DIR}/"
DESTINATION bin
FILES_MATCHING
PATTERN "*.dll"
PATTERN "MSVCP140*.dll" EXCLUDE
PATTERN "VCRUNTIME140*.dll" EXCLUDE
PATTERN "ucrtbase*.dll" EXCLUDE
PATTERN "api-ms-win-*.dll" EXCLUDE
PATTERN "OpenColorIO_d_*.dll" EXCLUDE # debug OCIO build
PATTERN "openjph.*d.dll" EXCLUDE # debug OpenJPH
)
# tbb12.dll (used by openvdb/embree4) lives in the precompiled lib tree, not install output
install(FILES "${CYCLES_TBB12_DLL}"
DESTINATION bin
OPTIONAL
)
endif()
# Install Python runtime DLLs required by OpenUSD and Cycles.
# python312.dll : linked by Cycles (hdCycles.dll → python312.dll)
# python311.dll : linked by usd_python.dll (the OpenUSD Python bindings were
# built against Python 3.11, regardless of what we compile with)
# Detect python311.dll from common install paths.
foreach(_pyroot
"$ENV{LOCALAPPDATA}/Programs/Python/Python311"
"C:/Python311"
"C:/Program Files/Python311"
)
if(EXISTS "${_pyroot}/python311.dll")
set(PYTHON311_DLL "${_pyroot}/python311.dll")
message(STATUS "Found python311.dll (required by usd_python.dll): ${PYTHON311_DLL}")
break()
endif()
endforeach()
set(_python_runtime_dlls
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
)
if(PYTHON311_DLL)
list(APPEND _python_runtime_dlls "${PYTHON311_DLL}")
endif()
install(FILES ${_python_runtime_dlls}
DESTINATION bin
OPTIONAL # OPTIONAL so the install doesn't fail on machines with a
# system-wide Python; in that case the DLL is on PATH already.
)
# Also copy python311.dll to the build output directory so debug runs work
# without requiring C:\Python311 to be on PATH.
if(PYTHON311_DLL)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PYTHON311_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/python311.dll"
COMMENT "Copying python311.dll (for usd_python.dll)..."
)
endif()
# ---------------------------------------------------------------------------
# OCIO: download ACES 1.2 config at configure time if not present
# ---------------------------------------------------------------------------
set(_ocio_config_dir "${CMAKE_SOURCE_DIR}/resources/OpenColorIO-Configs/aces_1.2")
if(NOT EXISTS "${_ocio_config_dir}/config.ocio")
message(STATUS "Downloading ACES 1.2 OCIO config (~124 MB) ...")
set(_ocio_zip "${CMAKE_BINARY_DIR}/aces_1.2.zip")
file(DOWNLOAD
"https://github.com/colour-science/OpenColorIO-Configs/releases/download/v1.2/OpenColorIO-Config-ACES-1.2.zip"
"${_ocio_zip}"
SHOW_PROGRESS
STATUS _ocio_dl_status
)
list(GET _ocio_dl_status 0 _ocio_dl_code)
if(_ocio_dl_code EQUAL 0)
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xf "${_ocio_zip}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
file(COPY "${CMAKE_BINARY_DIR}/OpenColorIO-Config-ACES-1.2/aces_1.2/"
DESTINATION "${_ocio_config_dir}")
file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/OpenColorIO-Config-ACES-1.2")
file(REMOVE "${_ocio_zip}")
message(STATUS "ACES 1.2 OCIO config installed to ${_ocio_config_dir}")
else()
message(WARNING "ACES 1.2 OCIO download failed (${_ocio_dl_status}) — OCIO will not be bundled.")
endif()
endif()
# Copy resources to build directory
file(COPY ${CMAKE_SOURCE_DIR}/resources
DESTINATION ${CMAKE_BINARY_DIR}
)
# Copy font and SVG icons to build directory
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>/../resources/font"
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>/resources/icons"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/third_party/Inter/Inter.ttc"
"$<TARGET_FILE_DIR:UsdLayerManager>/resources/font/Inter.ttc"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/resources/icons"
"$<TARGET_FILE_DIR:UsdLayerManager>/resources/icons"
COMMENT "Copying fonts and SVG icons to build output"
)
# Copy ACES OCIO config to build output — only on first build (skipped if already present).
if(EXISTS "${_ocio_config_dir}/config.ocio")
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND}
"-DSRC=${_ocio_config_dir}"
"-DDST=$<TARGET_FILE_DIR:UsdLayerManager>/resources/OpenColorIO-Configs/aces_1.2"
-P "${CMAKE_SOURCE_DIR}/cmake/CopyDirIfMissing.cmake"
COMMENT "Copying ACES OCIO config to build output (first time only)"
)
endif()
# Install OpenColorIO DLL (from the OpenUSD distribution's bin/)
install(FILES
"${OpenUSD_ROOT_DIR}/bin/OpenColorIO_2_1.dll"
DESTINATION bin
OPTIONAL
)
# Install resources (icons, fonts placeholder) next to the exe so the
# exe-relative path "resources/..." resolves correctly from install/bin/.
install(DIRECTORY ${CMAKE_SOURCE_DIR}/resources
DESTINATION bin
)
# Install Inter font next to the exe under resources/font/
# ImGuiContext tries "resources/font/Inter.ttf" then "resources/font/Inter.ttc".
install(FILES ${CMAKE_SOURCE_DIR}/third_party/Inter/Inter.ttc
DESTINATION bin/resources/font
)
# Print configuration summary
message(STATUS "")
message(STATUS "=== USD Layer Manager Configuration ===")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "OpenUSD found: ${OpenUSD_FOUND}")
message(STATUS "ImGui found: ${Imgui_FOUND}")
message(STATUS "OpenGL found: ${OPENGL_FOUND}")
message(STATUS "Glad found: ${Glad_FOUND}")
message(STATUS "Build tests: ${BUILD_TESTS}")
message(STATUS "hdEmbree support: ${OPENUSD_HAS_EMBREE}")
if(OPENUSD_HAS_EMBREE AND EMBREE_DLLS)
message(STATUS "Embree DLLs: ${EMBREE_DLLS}")
elseif(OPENUSD_HAS_EMBREE)
message(WARNING "hdEmbree found but no Embree DLLs detected — set EMBREE_LOCATION to the Embree SDK bin/ dir or check HDEMBREE_USD_ROOT/bin")
endif()
message(STATUS "hdArnold support: ${OPENUSD_HAS_ARNOLD}")
if(OPENUSD_HAS_ARNOLD)
message(STATUS "hdArnold DLL: ${HDARNOLD_DLL}")
message(STATUS "Arnold runtime DLLs: ${ARNOLD_RUNTIME_DLLS}")
if(NOT ARNOLD_AI_DLL)
message(WARNING "hdArnold found but ai.dll not located — set ARNOLD_LOCATION to the MtoA root (e.g. C:/Autodesk/mtoa/5.5.0/2026)")
endif()
endif()
message(STATUS "hdCycles support: ${OPENUSD_HAS_CYCLES}")
if(OPENUSD_HAS_CYCLES)
message(STATUS "Cycles source: ${CMAKE_SOURCE_DIR}/third_party/cycles")
message(STATUS "Cycles build: ${CYCLES_BINARY_DIR}")
message(STATUS "Cycles install: ${CYCLES_INSTALL_DIR}")
endif()
message(STATUS "=======================================")
message(STATUS "")
# === Test build option ===
option(BUILD_TESTS "Build test executables" OFF)
if(BUILD_TESTS)
# === Viewport Display Test ===
add_executable(ViewportDisplayTest
tests/viewport_display_test.cpp
${CORE_SOURCES}
${UTILS_SOURCES}
)
target_include_directories(ViewportDisplayTest PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
target_link_libraries(ViewportDisplayTest PRIVATE
OpenUSD::OpenUSD
Glad::Glad
FFmpeg::FFmpeg
)
if(WIN32)
target_compile_definitions(ViewportDisplayTest PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
add_custom_command(TARGET ViewportDisplayTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:ViewportDisplayTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:ViewportDisplayTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMENT "Copying runtime DLLs and USD plugins for test..."
)
endif()
enable_testing()
add_test(NAME ViewportDisplayTest COMMAND ViewportDisplayTest)
add_executable(RendererDiagnosticTest
tests/renderer_diagnostic_test.cpp
${CORE_SOURCES}
${UTILS_SOURCES}
)
target_include_directories(RendererDiagnosticTest PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
target_link_libraries(RendererDiagnosticTest PRIVATE
OpenUSD::OpenUSD
Glad::Glad
FFmpeg::FFmpeg
)
if(WIN32)
target_compile_definitions(RendererDiagnosticTest PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
add_custom_command(TARGET RendererDiagnosticTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMENT "Copying runtime DLLs and USD plugins for diagnostic test..."
)
endif()
endif() # BUILD_TESTS