mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-25 22:22:48 +00:00
feat(ugc): ray_backend=hiprt traces on the GPU with HIPRT and Orochi (optional build)
HIPRT (MIT) behind the CMake option DLU_HIPRT (off): its headers come from its SDK (HIPRT_ROOT, else ROCm's /opt/rocm) and are copied next to the servers; its library is loaded when first used (hiprtew), as HIP or CUDA are by Orochi (MIT, fetched pinned by hash; CUDA when its toolkit is found). The trace kernels (nearest hit skipping the triangle a ray leaves, any hit) are compiled the first time and kept in cache/hiprt. One GPU context for the process (hiprt_device picks the GPU); the workers take turns on it. When HIPRT, the GPU or a scene's upload fails, Embree is used instead, and the UGC server logs why at start. For a GPU the rays go in batches (UgcRays::Scene gets batch queries; the CPU backends answer them a ray at a time): - hidden faces: with a batch backend the paths are traced side by side, a bounce at a time (the path code split into Start, Scatter and Bounce, the one by one tracing unchanged); the same paths with the same random numbers, so the same triangles are decided (tested with builtin side by side) - the occlusion bake and the denoised icons' traced occlusion always ask in batches (the same rays, the same results) Its symbols are hidden: the servers export theirs (-rdynamic), and HIPRT's library, which has an Orochi of its own, would otherwise call ours. Check: configure with -DDLU_HIPRT=ON on a machine with ROCm (or HIPRT's SDK) and an AMD RDNA or NVIDIA GPU; UgcServer --make-model x.lxfml out hiprt; the UGC tests (hits, hidden faces and occlusion against builtin); a build without it leaves everything as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -284,36 +284,60 @@ namespace UgcRender {
|
||||
return h;
|
||||
}
|
||||
};
|
||||
std::unordered_map<Key, float, KeyHash> known;
|
||||
known.reserve(mesh.positions.size());
|
||||
// Which vertices are worked out (the first at each place and facing) and which take another's
|
||||
std::unordered_map<Key, size_t, KeyHash> first;
|
||||
first.reserve(mesh.positions.size());
|
||||
std::vector<size_t> copyOf(mesh.positions.size(), SIZE_MAX);
|
||||
std::vector<size_t> traced;
|
||||
for (size_t v = 0; v < mesh.positions.size(); v++) {
|
||||
if ((v & 0xFF) == 0) UgcThrottle::Checkpoint();
|
||||
const auto& normal = mesh.normals[v];
|
||||
const Key key{ { static_cast<int32_t>(std::lround(mesh.positions[v].x * 1000.0f)), static_cast<int32_t>(std::lround(mesh.positions[v].y * 1000.0f)),
|
||||
static_cast<int32_t>(std::lround(mesh.positions[v].z * 1000.0f)) }, { static_cast<int32_t>(std::lround(normal.x * 100.0f)),
|
||||
static_cast<int32_t>(std::lround(normal.y * 100.0f)), static_cast<int32_t>(std::lround(normal.z * 100.0f)) } };
|
||||
if (const auto it = known.find(key); it != known.end()) {
|
||||
ao[v] = it->second;
|
||||
if (const auto it = first.find(key); it != first.end()) {
|
||||
copyOf[v] = it->second;
|
||||
continue;
|
||||
}
|
||||
if (glm::dot(normal, normal) < 0.5f) continue;
|
||||
// A frame around the normal
|
||||
const glm::vec3 helper = std::abs(normal.x) < 0.9f ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0);
|
||||
const auto tangent = glm::normalize(glm::cross(helper, normal));
|
||||
const auto bitangent = glm::cross(normal, tangent);
|
||||
// Hammersley points, turned by an amount of the vertex's own (fixed) so neighbours don't band
|
||||
const float turn = static_cast<float>((v * 0x9E3779B9u) >> 8 & 0xFFFFFF) / 16777216.0f;
|
||||
const auto origin = mesh.positions[v] + normal * 1e-3f;
|
||||
uint32_t open = 0;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const float u = (i + 0.5f) / static_cast<float>(count);
|
||||
const float phi = 2.0f * 3.14159265f * std::fmod(RadicalInverse(i) + turn, 1.0f);
|
||||
const float r = std::sqrt(u), z = std::sqrt(std::max(0.0f, 1.0f - u));
|
||||
const auto direction = tangent * (r * std::cos(phi)) + bitangent * (r * std::sin(phi)) + normal * z;
|
||||
if (!scene->Occluded(origin, direction, 1e-4f, distance)) open++;
|
||||
first.emplace(key, v);
|
||||
traced.push_back(v);
|
||||
}
|
||||
// The rays in batches of vertices: small ones between checkpoints on the CPU, big ones for a GPU
|
||||
const size_t perBatch = scene->PrefersBatches() ? std::max<size_t>(1, (1u << 18) / count) : 256;
|
||||
std::vector<UgcRays::Ray> batch;
|
||||
std::vector<uint8_t> occluded;
|
||||
for (size_t start = 0; start < traced.size(); start += perBatch) {
|
||||
UgcThrottle::Checkpoint();
|
||||
const size_t end = std::min(traced.size(), start + perBatch);
|
||||
batch.clear();
|
||||
for (size_t j = start; j < end; j++) {
|
||||
const auto v = traced[j];
|
||||
const auto& normal = mesh.normals[v];
|
||||
// A frame around the normal
|
||||
const glm::vec3 helper = std::abs(normal.x) < 0.9f ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0);
|
||||
const auto tangent = glm::normalize(glm::cross(helper, normal));
|
||||
const auto bitangent = glm::cross(normal, tangent);
|
||||
// Hammersley points, turned by an amount of the vertex's own (fixed) so neighbours don't band
|
||||
const float turn = static_cast<float>((v * 0x9E3779B9u) >> 8 & 0xFFFFFF) / 16777216.0f;
|
||||
const auto origin = mesh.positions[v] + normal * 1e-3f;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const float u = (i + 0.5f) / static_cast<float>(count);
|
||||
const float phi = 2.0f * 3.14159265f * std::fmod(RadicalInverse(i) + turn, 1.0f);
|
||||
const float r = std::sqrt(u), z = std::sqrt(std::max(0.0f, 1.0f - u));
|
||||
const auto direction = tangent * (r * std::cos(phi)) + bitangent * (r * std::sin(phi)) + normal * z;
|
||||
batch.push_back(UgcRays::Ray{ origin, 1e-4f, direction, distance });
|
||||
}
|
||||
}
|
||||
ao[v] = static_cast<float>(open) / static_cast<float>(count);
|
||||
known.emplace(key, ao[v]);
|
||||
occluded.resize(batch.size());
|
||||
scene->Occluded(batch.data(), occluded.data(), batch.size());
|
||||
for (size_t j = start; j < end; j++) {
|
||||
uint32_t open = 0;
|
||||
for (uint32_t i = 0; i < count; i++) open += occluded[(j - start) * count + i] ? 0 : 1;
|
||||
ao[traced[j]] = static_cast<float>(open) / static_cast<float>(count);
|
||||
}
|
||||
}
|
||||
for (size_t v = 0; v < ao.size(); v++) {
|
||||
if (copyOf[v] != SIZE_MAX) ao[v] = ao[copyOf[v]];
|
||||
}
|
||||
return ao;
|
||||
}
|
||||
@@ -517,31 +541,49 @@ namespace UgcRender {
|
||||
if (traced) {
|
||||
const auto scene = UgcRays::Make(options.ao.rays, model.opaque);
|
||||
const auto count = static_cast<uint32_t>(std::clamp(options.denoiseSamples, 1, 256));
|
||||
// In batches of pixels: small ones between checkpoints on the CPU, big ones for a GPU
|
||||
const size_t perBatch = scene->PrefersBatches() ? std::max<size_t>(1, (1u << 18) / count) : 1024;
|
||||
std::vector<size_t> drawn;
|
||||
for (size_t index = 0; index < color.size(); index++) {
|
||||
if ((index & 0x3FF) == 0) UgcThrottle::Checkpoint();
|
||||
if (color[index].a <= 0.0f) continue;
|
||||
const auto& normal = pointNormals[index];
|
||||
const glm::vec3 helper = std::abs(normal.x) < 0.9f ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0);
|
||||
const auto tangent = glm::normalize(glm::cross(helper, normal));
|
||||
const auto bitangent = glm::cross(normal, tangent);
|
||||
uint32_t hash = static_cast<uint32_t>(index) * 0x9E3779B9u;
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85EBCA6Bu;
|
||||
hash ^= hash >> 13;
|
||||
const float turn = static_cast<float>(hash >> 8) / 16777216.0f;
|
||||
const float shift = static_cast<float>((hash * 0xC2B2AE35u) >> 8) / 16777216.0f;
|
||||
const auto origin = points[index] + normal * 1e-3f;
|
||||
uint32_t open = 0;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const float u = std::fmod((i + shift) / static_cast<float>(count), 1.0f);
|
||||
const float phi = 2.0f * 3.14159265f * std::fmod(RadicalInverse(i) + turn, 1.0f);
|
||||
const float r = std::sqrt(u), up = std::sqrt(std::max(0.0f, 1.0f - u));
|
||||
const auto direction = tangent * (r * std::cos(phi)) + bitangent * (r * std::sin(phi)) + normal * up;
|
||||
if (!scene->Occluded(origin, direction, 1e-4f, options.ao.distance)) open++;
|
||||
if (color[index].a > 0.0f) drawn.push_back(index);
|
||||
}
|
||||
std::vector<UgcRays::Ray> batch;
|
||||
std::vector<uint8_t> occluded;
|
||||
for (size_t start = 0; start < drawn.size(); start += perBatch) {
|
||||
UgcThrottle::Checkpoint();
|
||||
const size_t end = std::min(drawn.size(), start + perBatch);
|
||||
batch.clear();
|
||||
for (size_t j = start; j < end; j++) {
|
||||
const auto index = drawn[j];
|
||||
const auto& normal = pointNormals[index];
|
||||
const glm::vec3 helper = std::abs(normal.x) < 0.9f ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0);
|
||||
const auto tangent = glm::normalize(glm::cross(helper, normal));
|
||||
const auto bitangent = glm::cross(normal, tangent);
|
||||
uint32_t hash = static_cast<uint32_t>(index) * 0x9E3779B9u;
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85EBCA6Bu;
|
||||
hash ^= hash >> 13;
|
||||
const float turn = static_cast<float>(hash >> 8) / 16777216.0f;
|
||||
const float shift = static_cast<float>((hash * 0xC2B2AE35u) >> 8) / 16777216.0f;
|
||||
const auto origin = points[index] + normal * 1e-3f;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const float u = std::fmod((i + shift) / static_cast<float>(count), 1.0f);
|
||||
const float phi = 2.0f * 3.14159265f * std::fmod(RadicalInverse(i) + turn, 1.0f);
|
||||
const float r = std::sqrt(u), up = std::sqrt(std::max(0.0f, 1.0f - u));
|
||||
const auto direction = tangent * (r * std::cos(phi)) + bitangent * (r * std::sin(phi)) + normal * up;
|
||||
batch.push_back(UgcRays::Ray{ origin, 1e-4f, direction, options.ao.distance });
|
||||
}
|
||||
}
|
||||
occluded.resize(batch.size());
|
||||
scene->Occluded(batch.data(), occluded.data(), batch.size());
|
||||
for (size_t j = start; j < end; j++) {
|
||||
uint32_t open = 0;
|
||||
for (uint32_t i = 0; i < count; i++) open += occluded[(j - start) * count + i] ? 0 : 1;
|
||||
const auto index = drawn[j];
|
||||
const float occlusion = static_cast<float>(open) / static_cast<float>(count);
|
||||
const float lit = 1.0f - std::clamp(options.bakedAo, 0.0f, 1.0f) * (1.0f - occlusion);
|
||||
color[index] = glm::vec4(glm::vec3(color[index]) * lit, color[index].a);
|
||||
}
|
||||
const float occlusion = static_cast<float>(open) / static_cast<float>(count);
|
||||
const float lit = 1.0f - std::clamp(options.bakedAo, 0.0f, 1.0f) * (1.0f - occlusion);
|
||||
color[index] = glm::vec4(glm::vec3(color[index]) * lit, color[index].a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user