mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-11 15:23:43 +00:00
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>
This commit is contained in:
@@ -1932,10 +1932,12 @@ namespace {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// The backends other than embree that this build and machine can use
|
||||
// The backends other than embree that this build and machine can use (the GPUs')
|
||||
std::vector<UgcRays::eBackend> OtherBackends() {
|
||||
std::vector<UgcRays::eBackend> backends;
|
||||
if (UgcRays::Available(UgcRays::eBackend::HIPRT)) backends.push_back(UgcRays::eBackend::HIPRT);
|
||||
for (const auto backend : { UgcRays::eBackend::HIPRT, UgcRays::eBackend::EMBREE_GPU }) {
|
||||
if (UgcRays::Available(backend)) backends.push_back(backend);
|
||||
}
|
||||
return backends;
|
||||
}
|
||||
}
|
||||
@@ -2087,17 +2089,26 @@ TEST(UgcRender, DenoisedIconsTraceTheOcclusionPerPixel) {
|
||||
}
|
||||
|
||||
TEST(UgcRays, NamesAndFallback) {
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT }) {
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT, UgcRays::eBackend::EMBREE_GPU }) {
|
||||
EXPECT_EQ(UgcRays::Parse(UgcRays::Name(backend)), backend);
|
||||
}
|
||||
EXPECT_EQ(UgcRays::Parse("embree-gpu"), UgcRays::eBackend::EMBREE_GPU);
|
||||
EXPECT_EQ(UgcRays::Parse("builtin"), UgcRays::eBackend::EMBREE); // the backend Embree replaced
|
||||
EXPECT_FALSE(UgcRays::Parse("optix"));
|
||||
EXPECT_TRUE(UgcRays::Available(UgcRays::eBackend::EMBREE));
|
||||
// A backend this machine can't use falls back to embree
|
||||
EXPECT_EQ(UgcRays::Resolve(UgcRays::eBackend::HIPRT), UgcRays::Available(UgcRays::eBackend::HIPRT) ? UgcRays::eBackend::HIPRT : UgcRays::eBackend::EMBREE);
|
||||
// A backend this build or machine can't use falls back to embree, and says why
|
||||
for (const auto backend : { UgcRays::eBackend::HIPRT, UgcRays::eBackend::EMBREE_GPU }) {
|
||||
const bool available = UgcRays::Available(backend);
|
||||
EXPECT_EQ(UgcRays::Resolve(backend), available ? backend : UgcRays::eBackend::EMBREE);
|
||||
EXPECT_EQ(UgcRays::Problem(backend).empty(), available) << UgcRays::Problem(backend);
|
||||
// Made anyway: an Embree scene that works
|
||||
const auto scene = UgcRays::Make(backend, Clutter());
|
||||
EXPECT_NEAR(scene->Closest({ 0.3f, 0.1f, 0.1f }, { 1, 0, 0 }).t, 1.7f, 1e-5f) << UgcRays::Name(backend);
|
||||
}
|
||||
EXPECT_TRUE(UgcRays::Problem(UgcRays::eBackend::EMBREE).empty());
|
||||
EXPECT_EQ(UgcRays::Resolve(UgcRays::eBackend::EMBREE), UgcRays::eBackend::EMBREE);
|
||||
// An empty mesh is hit by nothing
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT }) {
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT, UgcRays::eBackend::EMBREE_GPU }) {
|
||||
const auto scene = UgcRays::Make(backend, UgcModel::Mesh{});
|
||||
EXPECT_EQ(scene->Closest(glm::vec3(0.0f), glm::vec3(0, 1, 0)).triangle, UgcRays::NONE);
|
||||
EXPECT_FALSE(scene->Occluded(glm::vec3(0.0f), glm::vec3(0, 1, 0), 0.0f, 10.0f));
|
||||
@@ -2108,7 +2119,7 @@ TEST(UgcRays, FindsTheExpectedHits) {
|
||||
// The room of the clutter is [-2, 2]^3 (its walls face inwards; triangles 12 on), the box in it [-0.3, 0.3]^3
|
||||
// (triangles 0 to 11): rays whose hits are known
|
||||
const auto mesh = Clutter();
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT }) {
|
||||
for (const auto backend : { UgcRays::eBackend::EMBREE, UgcRays::eBackend::HIPRT, UgcRays::eBackend::EMBREE_GPU }) {
|
||||
const auto scene = UgcRays::Make(backend, mesh);
|
||||
const auto name = std::string(UgcRays::Name(UgcRays::Resolve(backend)));
|
||||
// From the box's +X face out along +X: the room's +X wall (triangles 16 and 17) 1.7 away
|
||||
|
||||
Reference in New Issue
Block a user