Files
DarkflameServer/dUgcServer/CMakeLists.txt
Aaron Kimbrell db63fd2919 feat(ugc): ray_backend=embree-gpu traces on Intel GPUs with Embree's SYCL (optional build)
The third ray backend, so every machine has a library for it: Embree on x86
CPUs (embree), HIPRT on AMD and NVIDIA GPUs (hiprt), Embree through SYCL on
Intel Arc and Xe GPUs (embree-gpu).

- CMake option DLU_EMBREE_SYCL (off). dUgcServer/EmbreeSycl is a project of
  its own built by a SYCL compiler (DLU_SYCL_CXX; icpx through ONEAPI_ROOT or
  the path, or the open source DPC++'s clang++ through DPCPP_ROOT) as an
  external project: Embree 4.4 with EMBREE_SYCL_SUPPORT, linked statically and
  bound inside (-Bsymbolic, only its C functions exported, so it never meets
  the servers' own Embree), and the GPU kernels (nearest hit skipping a ray's
  triangle, any hit), into libdlu_embree_sycl next to the servers
- UgcRaysEmbreeGpu loads it the first time embree-gpu is asked for; one GPU for
  the process (embree_gpu_device picks it), the workers take turns, the
  occlusion rays in batches as for hiprt
- without the build, the library or a supported Intel GPU it falls back to
  embree and says why (the UGC server's log at start, --make-model on stderr)
- the option names, the settings page, the dashboard's picker, /reprocessproperty

Verified here: the default build and ctest; the SYCL build with the open source
DPC++ 7.1.0 (compiles, links against oneAPI's libsycl.so.9, exports only its C
functions); on this machine (no Intel GPU) it loads, finds no GPU and falls
back to embree. Not verified: tracing on an Intel GPU (none here).

Check: on a machine with an Intel Arc or Xe GPU and oneAPI, configure with
-DDLU_EMBREE_SYCL=ON and run UgcServer --make-model x.lxfml out embree-gpu;
the UGC tests then compare it with Embree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:29:08 -05:00

105 lines
5.4 KiB
CMake

# The UGC server (docs/UgcServer.md), in folders by role:
# Bricks/ the client's brick data: primitives, Materials.xml, LU Toolbox's palette, the CDClient's module data
# Model/ player models built from LXFML, car and rocket module combinations, glitter
# Render/ the software rasterizer, ambient occlusion, hidden-face removal, rays, icon parameters and pose
# Formats/ the files written for the client and the dashboard (NIF, DDS, PNG, sd0)
# Processing/ the job queue and workers, the CPU budget, stored files
# UgcServer.cpp the server: settings, the master link, HTTP routes
# The pure part of the UGC server (brick data, meshes, rendering, file formats, storage), shared with the tests
set(DUGC_SOURCES
"Bricks/UgcBricks.cpp"
"Bricks/UgcPalette.cpp"
"Formats/UgcFormats.cpp"
"Model/UgcGlitter.cpp"
"Model/UgcModel.cpp"
"Model/UgcModular.cpp"
"Processing/UgcJobs.cpp"
"Processing/UgcStorage.cpp"
"Processing/UgcThrottle.cpp"
"Render/UgcHsr.cpp"
"Render/UgcIconParams.cpp"
"Render/UgcIconPose.cpp"
"Render/UgcRays.cpp"
"Render/UgcRender.cpp"
)
add_library(dUgc STATIC ${DUGC_SOURCES})
target_include_directories(dUgc PUBLIC "." "Bricks" "Formats" "Model" "Processing" "Render"
"${PROJECT_SOURCE_DIR}/thirdparty/MD5" "${PROJECT_SOURCE_DIR}/thirdparty/nlohmann")
target_link_libraries(dUgc PUBLIC dCommon glm::glm tinyxml2 MD5 PRIVATE embree)
# Intel Open Image Denoise, when built with DLU_OIDN (thirdparty/CMakeLists.txt): the denoise=oidn setting
if(DLU_OIDN)
target_link_libraries(dUgc PRIVATE OpenImageDenoise)
target_compile_definitions(dUgc PRIVATE DLU_OIDN)
endif()
# Embree on Intel GPUs through SYCL (ray_backend=embree-gpu), when built with DLU_EMBREE_SYCL: libdlu_embree_sycl
# (EmbreeSycl/, Embree built with its SYCL support and the GPU kernels) is its own project built by a SYCL compiler
# (DLU_SYCL_CXX: Intel oneAPI DPC++'s icpx, or the open source DPC++'s clang++; found through ONEAPI_ROOT or
# DPCPP_ROOT), put next to the servers and loaded by the UGC server when asked for, so nothing else needs oneAPI.
option(DLU_EMBREE_SYCL "Build the UGC server's Intel GPU ray tracer (ray_backend=embree-gpu; needs a SYCL compiler)" OFF)
if(DLU_EMBREE_SYCL)
find_program(DLU_SYCL_CXX NAMES icpx clang++ PATHS "$ENV{ONEAPI_ROOT}/compiler/latest/bin" "$ENV{DPCPP_ROOT}/bin" NO_DEFAULT_PATH)
if(NOT DLU_SYCL_CXX)
find_program(DLU_SYCL_CXX NAMES icpx)
endif()
if(NOT DLU_SYCL_CXX)
message(FATAL_ERROR "DLU_EMBREE_SYCL needs a SYCL compiler: set DLU_SYCL_CXX, ONEAPI_ROOT (Intel oneAPI) or DPCPP_ROOT (open source DPC++)")
endif()
get_filename_component(DLU_SYCL_BIN "${DLU_SYCL_CXX}" DIRECTORY)
find_program(DLU_SYCL_CC NAMES icx clang PATHS "${DLU_SYCL_BIN}" NO_DEFAULT_PATH REQUIRED)
include(ExternalProject)
set(DLU_EMBREE_SYCL_LIBRARY "${CMAKE_SHARED_LIBRARY_PREFIX}dlu_embree_sycl${CMAKE_SHARED_LIBRARY_SUFFIX}")
ExternalProject_Add(dlu_embree_sycl
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/EmbreeSycl"
BINARY_DIR "${CMAKE_BINARY_DIR}/embree_sycl"
CMAKE_ARGS -DCMAKE_CXX_COMPILER=${DLU_SYCL_CXX} -DCMAKE_C_COMPILER=${DLU_SYCL_CC} -DCMAKE_BUILD_TYPE=Release
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different "<BINARY_DIR>/${DLU_EMBREE_SYCL_LIBRARY}" "${CMAKE_BINARY_DIR}/"
BUILD_ALWAYS TRUE
USES_TERMINAL_BUILD TRUE)
target_sources(dUgc PRIVATE "Render/UgcRaysEmbreeGpu.cpp")
target_compile_definitions(dUgc PRIVATE DLU_EMBREE_SYCL)
target_link_libraries(dUgc PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(dUgc dlu_embree_sycl)
message(STATUS "Intel GPU ray tracing (embree-gpu) built with ${DLU_SYCL_CXX}")
endif()
# HIPRT and Orochi, when built with DLU_HIPRT (thirdparty/CMakeLists.txt): the ray_backend=hiprt setting
if(DLU_HIPRT)
target_sources(dUgc PRIVATE "Render/UgcRaysHiprt.cpp")
# hiprtew's function pointers have the names of HIPRT's functions: hidden, or HIPRT's library would call them
# instead of its own (the servers export their symbols)
if(NOT MSVC)
set_source_files_properties("Render/UgcRaysHiprt.cpp" PROPERTIES COMPILE_OPTIONS "-fvisibility=hidden")
endif()
target_include_directories(dUgc PRIVATE "${DLU_HIPRT_INCLUDE_DIR}")
target_link_libraries(dUgc PRIVATE dlu_orochi)
target_compile_definitions(dUgc PRIVATE DLU_HIPRT DLU_HIPRT_INCLUDE_DIR="${DLU_HIPRT_INCLUDE_DIR}")
endif()
add_executable(UgcServer "UgcServer.cpp" "Processing/UgcProcessor.cpp" "Bricks/UgcCdClient.cpp")
target_include_directories(UgcServer PRIVATE
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dClient"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
"${PROJECT_SOURCE_DIR}/dDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/dNet"
"${PROJECT_SOURCE_DIR}/dWeb"
"${PROJECT_SOURCE_DIR}/dServer"
"${PROJECT_SOURCE_DIR}/thirdparty"
"${PROJECT_SOURCE_DIR}/thirdparty/nlohmann"
)
target_compile_definitions(UgcServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(UgcServer ${COMMON_LIBRARIES} dWeb dServer dUgc)
# Rendering is far too slow unoptimized (minutes a model instead of seconds), so it is optimized in every build type
if(NOT MSVC)
set_source_files_properties("Render/UgcRender.cpp" "Render/UgcHsr.cpp" "Render/UgcRays.cpp" "Model/UgcModel.cpp" "Bricks/UgcPalette.cpp" "Model/UgcGlitter.cpp" PROPERTIES COMPILE_OPTIONS "-O2")
endif()