Files
DarkflameServer/dUgcServer/Render/UgcRays.h
Aaron Kimbrell 07154c58d2 refactor(ugc): remove the path traced hidden-face method; the renders are the only one
The path tracer that imitated LU Toolbox's Remove Hidden Faces took 17 to 100
times as long as the renders from 42 directions around the model, too heavy to
use. It goes with its settings (hsr_method, hsr_samples, hsr_bounces,
hsr_sample_spacing, hsr_min_points), its side by side tracing for GPUs and its
tests. The renders (UgcRender::VisibleFromAround) remove hidden faces as before
that method existed; their size is hsr_resolution (1024), and the memory
estimate counts their buffers again.

The hidden-face method is no longer a processing option: the dashboard's picker,
/reprocessproperty and --make-model take only the ray backend (the occlusion's)
and denoising. Options stored before (made_options, process_options,
ugc_process_runs, --make-model arguments) that name toolbox or fast still
parse; the word is skipped.

Check: the settings page has no hsr_method or path settings and has
hsr_resolution; /reprocessproperty embree toolbox still works (toolbox
ignored); models made with the defaults keep their hashes (UGC tests).

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

88 lines
3.9 KiB
C++

#pragma once
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <glm/glm.hpp>
#include "UgcModel.h"
/**
* Rays against a mesh's triangles: the nearest hit (never the triangle a ray leaves) and, for the ambient occlusion
* rays, whether anything is hit, by one of several backends (the ray_backend setting, or per job):
* builtin: the UGC server's own bounding volume hierarchies (the ones it always had)
* embree: Intel's Embree 4 on the CPU, on the thread that asks (no threads of its own)
* hiprt: AMD's HIPRT on the GPU (AMD through HIP, NVIDIA through CUDA, loaded when first asked for by Orochi),
* when built with DLU_HIPRT and a GPU is there; else embree. One GPU for the process, used by one thread
* at a time; its time is not CPU time.
* A scene is built and traced on the thread that asks, so its time counts towards that thread's CPU time
* (UgcThrottle). Scenes aren't shared between threads. docs/UgcServer.md ("Processing options") has the details.
*/
namespace UgcRays {
constexpr uint32_t NONE = std::numeric_limits<uint32_t>::max();
constexpr float INF = std::numeric_limits<float>::infinity();
enum class eBackend : uint8_t { BUILTIN = 0, EMBREE, HIPRT };
// The setting's name of a backend (builtin, embree, hiprt)
std::string_view Name(eBackend backend);
// A backend by its name (case sensitive); nullopt for anything else
std::optional<eBackend> Parse(std::string_view name);
// Whether this build and machine can use the backend (builtin and embree always)
bool Available(eBackend backend);
// The backend that is used when `wanted` is asked for: itself, or embree when it isn't available
eBackend Resolve(eBackend wanted);
// Why a backend isn't available (empty when it is)
std::string Problem(eBackend backend);
struct Hit {
float t{ INF };
uint32_t triangle{ NONE };
float u{}, v{}; // weights of the triangle's second and third vertex
};
// A ray of a batch (the layout the GPU kernels read too)
struct Ray {
glm::vec3 origin{};
float minT{}; // Occluded: hits further than this count (Closest: further than 0)
glm::vec3 direction{}; // unit
float maxT{ INF }; // hits nearer than this count
uint32_t skip{ NONE }; // Closest: the triangle never hit (the one the ray leaves)
uint32_t padding[3]{};
};
static_assert(sizeof(Ray) == 48, "the GPU kernels read rays as 48 bytes");
static_assert(sizeof(Hit) == 16, "the GPU kernels write hits as 16 bytes");
class Scene {
public:
virtual ~Scene() = default;
// The nearest triangle along the ray (unit direction) further than 0 and before `maxT`, never `skip` (the
// triangle the ray leaves, as in Cycles); triangle NONE (and t = maxT) when there is none
virtual Hit Closest(const glm::vec3& origin, const glm::vec3& direction, uint32_t skip = NONE, float maxT = INF) const = 0;
// Whether the ray (unit direction) hits a triangle further than `minT` and nearer than `maxT`
virtual bool Occluded(const glm::vec3& origin, const glm::vec3& direction, float minT, float maxT) const = 0;
// Many rays at once, as the single ray queries answer them (a GPU answers a batch at the cost of one ray)
virtual void Closest(const Ray* rays, Hit* hits, size_t count) const;
virtual void Occluded(const Ray* rays, uint8_t* occluded, size_t count) const;
// Whether the backend is only fast with big batches (a GPU): the callers then trace many paths side by side
virtual bool PrefersBatches() const { return false; }
};
/**
* The mesh's triangles as they are now (copied) in the backend Resolve(backend) picks. Throws std::runtime_error
* when the backend fails.
*/
std::unique_ptr<Scene> Make(eBackend backend, const UgcModel::Mesh& mesh);
// Which GPU hiprt uses (hiprt_device: 0 is the first HIP or CUDA device); before it is first used
void SetGpuDevice(int index);
}