diff --git a/CLAUDE.md b/CLAUDE.md index 6e2bef9..0b465a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,6 +13,10 @@ nix develop -c cmake --build build/linux cd build/linux && nix develop -c ctest ``` +For day-to-day work `nix develop -c cmake --preset linux-dev` (then build +`build/linux-dev`) builds every module as its own `.so`, so an edit relinks one +library. Benchmarks and captures you compare stay on `build/linux`. + The binary needs the dev shell at runtime too (`libvkd3d`), so prefix runs with `nix develop -c`, including read-only ones like `--validate` and `--dump-schema`. diff --git a/CMakeLists.txt b/CMakeLists.txt index b685465..30ddac4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,12 +45,12 @@ option(RX_MIMALLOC "Route allocations through mimalloc (+memory tracking)" ON) option(RX_INSTALL "Generate install()/export rules for find_package(rx)" ${PROJECT_IS_TOP_LEVEL}) -# The "DLL test": build each rx module as a shared library instead of a static -# archive. Off by default (the static build is the shipping configuration). This -# is a correctness discipline, not a distribution mode: hidden visibility plus -# explicit per-module export annotations (foundation/build_config/export.h) document each -# module's real cross-DSO API and flush out ambient-state bugs. -option(RX_SHARED "Build rx modules as shared libraries (experimental)" OFF) +# The component build: each rx module is a shared library instead of a static +# archive, so an edit relinks one .so. The `linux-dev` preset turns it on for +# day-to-day work; the static build stays the shipping and benchmark +# configuration. Hidden visibility plus explicit per-module export annotations +# (foundation/build_config/export.h) keep each module's cross-DSO API honest. +option(RX_SHARED "Build rx modules as shared libraries (the -dev presets)" OFF) if(RX_SHARED) if(RX_INSTALL) @@ -63,12 +63,8 @@ if(RX_SHARED) # Third-party static archives (equilibrium::base, volk, VMA, Jolt, kinema, NRD, # FSR3) get linked into rx shared objects, so their objects must be PIC. set(CMAKE_POSITION_INDEPENDENT_CODE ON) - message(WARNING - "RX_SHARED is experimental. rx modules build as shared objects with hidden " - "visibility + explicit export annotations. Caveat: equilibrium's base::Option " - "registry (base::InitChain) is a per-DSO instance under hidden visibility, so " - "each option-owning DSO re-applies env overrides at init. " - "Env knobs work; the mechanism differs from the static single-registry build.") + # One base::Option / base::Feature registry per process, not per DSO: base's + # InitChain carries default visibility (base/containers/init_chain.h). else() set(RX_LIB_TYPE STATIC) endif() @@ -190,8 +186,9 @@ if(NOT TARGET volk) # whole table instead is worse: it collides with SDL3's own Vulkan symbols. # The correct model is a fully-private volk copy per DSO that uses it, each # initialized by that DSO. Force hidden visibility so no volk symbol leaks - # out to interpose another DSO or SDL. (render initializes its copy in - # vk_device.cc; the viewer initializes the imgui copy, see debug_ui.cc.) + # out to interpose another DSO or SDL. rx::gpu fills its copy in + # vk_device.cc; every other DSO fills its own the first time it calls + # gpu::GetVulkanHandles (rxe/gpu/rhi/vulkan_interop.h). set_target_properties(volk PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON) diff --git a/CMakePresets.json b/CMakePresets.json index 7ddad44..a34dc0c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -5,20 +5,42 @@ "name": "linux", "binaryDir": "${sourceDir}/build/linux", "generator": "Ninja", - "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "linux-dev", + "inherits": "linux", + "binaryDir": "${sourceDir}/build/linux-dev", + "cacheVariables": { + "RX_SHARED": "ON", + "RX_INSTALL": "OFF" + } }, { "name": "windows", "binaryDir": "${sourceDir}/build/windows", "generator": "Visual Studio 17 2022", - "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" } + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } }, { "name": "macos", "binaryDir": "${sourceDir}/build/macos", "generator": "Ninja", - "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", "RX_FETCH_SDL3": "ON" }, - "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" } + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "RX_FETCH_SDL3": "ON" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } } ] } diff --git a/docs/STRUCTURE.md b/docs/STRUCTURE.md index 8ed3f68..5ca7009 100644 --- a/docs/STRUCTURE.md +++ b/docs/STRUCTURE.md @@ -331,22 +331,29 @@ out of scope. | preset | `RX_SHARED` | use | |---|---|---| -| `linux-dev`, `windows-dev` | ON | day-to-day work: one DSO per module | +| `linux-dev` (`build/linux-dev`) | ON | day-to-day work: one DSO per module | | `linux`, `windows`, `android`, ... | OFF | shipping, benchmarks, install/export | -Benchmarks keep running on the static presets, so the shared build never skews -a performance number. - -Before `RX_SHARED` can be the default for development, anything that must exist -once per process has to live in exactly one DSO: - -- the `base::Option` registry (today one instance per DSO, see the CMake - warning): export it from `foundation` or build equilibrium base shared -- the allocator override (`memory/new_override.cc`) and mimalloc; on Windows, - the shared CRT (`/MD`) everywhere -- log sinks, the feature list and the app identity -- volk: under `RX_SHARED`, volk is built as its own shared library so every - `_vulkan.cc` file sees one loaded function table +Benchmarks and captures keep running on the static presets, so the shared build +never skews a performance number. + +What has to exist once per process, and how the shared build keeps it so: + +- `base::Option` / `base::Feature` registry: equilibrium's `InitChain` and its + item types carry default visibility, so the chain root is one ELF-unique + symbol however many DSOs instantiate it. Before, an option set from a + platform tier's `[options]` was invisible outside the DSO that declared it. +- volk: it cannot be shared (its table uses the Vulkan entry point names, which + collide with SDL3's), so each DSO keeps a private copy. `rx::gpu` fills its + own at device creation; every other DSO fills its copy inside + `gpu::GetVulkanHandles`, which every Vulkan interop caller already calls + first. +- log sinks, the feature table, app identity and the memory tracker live in + `rx_foundation` and are reached through exported functions; the allocator + override is compiled into the executable only (`rx_enable_mimalloc`). +- Windows: PE has no unique symbols, so a `windows-dev` preset first needs + equilibrium's base linked as a DLL and the shared CRT (`/MD`) everywhere. Not + done yet. ## Violations to fix before moving files @@ -413,8 +420,11 @@ and after. `rx::gpu::d3d12`, `rx::gpu::null`); window, events and the imgui renderer into `rx::ui`; `WorldClock` into `rx::app`, `exr_write` into `rx::asset`. The include lint now checks namespaces too. -5. **Development shared build.** Add the single-instance fixes and the `-dev` - presets, then per-module feature flags on the single registry. +5. **Development shared build.** + - 5a (done): the whole tree links as shared objects (missing exports + added), one option registry per process, per-DSO volk filled through + `GetVulkanHandles`, and the `linux-dev` preset. + - 5b: per-module feature flags on the single registry. 6. **Plugins and apps.** Add `RX_PLUGIN`, `PluginRegistry` and `rx_add_app`. `runtime/` becomes `apps/shell/`, the tools move into `apps/`, and `EngineContext` goes away. diff --git a/runtime/main.cc b/runtime/main.cc index a17205e..d31a0cc 100644 --- a/runtime/main.cc +++ b/runtime/main.cc @@ -258,14 +258,6 @@ int main(int argc, char** argv) { const bool verify_shot = !config.shot_path.empty() && !::getenv("RX_UI_SHOT_SEQ"); if (verify_shot) rx::fs::Remove(config.shot_path); -#if defined(RX_SHARED_BUILD) - // The viewer's own base::Option knobs (viewer.cc, camera_input.cc, - // demo_scenes.cc, debug_ui.cc) live on this executable's InitChain, which - // under RX_SHARED is a separate instance from the engine DSOs' chains. Apply - // the executable's env overrides here; each engine DSO applies its own at - // subsystem init. Compiled out in the static build (one shared chain). - base::InitOptionsFromEnv(); -#endif rx::Viewer viewer(config); rx::app::Host host; diff --git a/rxe/anim/anim_graph.h b/rxe/anim/anim_graph.h index fab12bb..ec84f25 100644 --- a/rxe/anim/anim_graph.h +++ b/rxe/anim/anim_graph.h @@ -4,6 +4,7 @@ #include "base/memory/move.h" #include "base/memory/unique_pointer.h" #include "base/strings/string_ref.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/asset/skeleton.h" @@ -22,7 +23,7 @@ struct GraphState; // The graph is authored kinema-side (kinema stays a private detail of // rxe/anim, so it never appears in this header); BuildBipedLocomotionGraph is // the first such archetype factory. -class AnimGraph { +class RX_ANIM_EXPORT AnimGraph { public: AnimGraph(); explicit AnimGraph(base::UniquePointer state); @@ -53,7 +54,7 @@ class AnimGraph { // forward root motion), a 1D walk<->run blend space on a "speed" parameter, and // a two-state machine (idle <-> locomotion) whose transitions are driven by // "speed". The result is shared by every RigPlayer of this rig. -AnimGraph BuildBipedLocomotionGraph(const asset::Skeleton& skeleton); +RX_ANIM_EXPORT AnimGraph BuildBipedLocomotionGraph(const asset::Skeleton& skeleton); } // namespace rx::anim diff --git a/rxe/anim/foot_placement.h b/rxe/anim/foot_placement.h index 965b7ad..08cb539 100644 --- a/rxe/anim/foot_placement.h +++ b/rxe/anim/foot_placement.h @@ -4,6 +4,7 @@ #include "base/functional/function.h" #include "base/memory/unique_pointer.h" +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/anim/anim_graph.h" #include "rxe/anim/pose.h" @@ -23,7 +24,7 @@ using GroundProbe = base::Function OpenDecoder(ByteSpan bytes, base::StringRef extension); +RX_AUDIO_EXPORT base::UniquePointer OpenDecoder(ByteSpan bytes, base::StringRef extension); // Decodes `bytes` completely into an AudioClip. Returns an invalid clip (see // AudioClip::valid) when the format is unsupported or the data is malformed. diff --git a/rxe/audio/audio_system.cc b/rxe/audio/audio_system.cc index c1d587d..0bf41a2 100644 --- a/rxe/audio/audio_system.cc +++ b/rxe/audio/audio_system.cc @@ -35,13 +35,6 @@ base::StringRef ExtensionOf(base::StringRef path) { } // namespace bool AudioSystem::Initialize(asset::Vfs* vfs) { -#if defined(RX_SHARED_BUILD) - // Apply this DSO's env option overrides: under RX_SHARED base::Option's - // registry is per-DSO (hidden-visibility InitChain head), so the app's - // InitOptionsFromEnv() never reaches the audio DSO's options. See the same - // note in Renderer::Initialize. Compiled out in the static build. - base::InitOptionsFromEnv(); -#endif vfs_ = vfs; muted_ = Mute.get(); master_ = rx::Clamp(Volume.get(), 0.0f, 1.0f); diff --git a/rxe/gpu/rhi/device.h b/rxe/gpu/rhi/device.h index 7311f28..9d93912 100644 --- a/rxe/gpu/rhi/device.h +++ b/rxe/gpu/rhi/device.h @@ -9,6 +9,7 @@ #include "base/functional/function.h" #include "base/memory/unique_pointer.h" #include "base/strings/xstring.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/gpu/rhi/bindings.h" #include "rxe/gpu/rhi/command_list.h" @@ -29,7 +30,7 @@ enum class Backend : u8 { // renderer's control flow testable without a GPU or a loader }; -const char* BackendName(Backend backend); +RX_GPU_EXPORT const char* BackendName(Backend backend); struct DeviceDesc { Backend backend = Backend::kAuto; @@ -110,7 +111,7 @@ enum class PresentResult : u8 { // a null-backend device (is_stub() true) comes back when no loader, no // capable GPU or no presentable window is available, and every operation on // it is a safe no-op. -class Device { +class RX_GPU_EXPORT Device { public: static constexpr u32 kMaxFramesInFlight = 2; diff --git a/rxe/gpu/rhi/gpu_profiler.h b/rxe/gpu/rhi/gpu_profiler.h index 719b114..96030c0 100644 --- a/rxe/gpu/rhi/gpu_profiler.h +++ b/rxe/gpu/rhi/gpu_profiler.h @@ -6,6 +6,7 @@ #include #include "base/strings/xstring.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/gpu/rhi/types.h" @@ -23,7 +24,7 @@ class Device; // reports the count, and each slot grows to that size the next time it comes // round (a pool can only be replaced where its last submission has retired), // so the miss lasts at most one cycle through the slots. -class GpuProfiler { +class RX_GPU_EXPORT GpuProfiler { public: struct PassTiming { base::String name; diff --git a/rxe/gpu/rhi/types.h b/rxe/gpu/rhi/types.h index 13f84c4..0b97bc6 100644 --- a/rxe/gpu/rhi/types.h +++ b/rxe/gpu/rhi/types.h @@ -1,6 +1,7 @@ #ifndef RX_RENDER_RHI_TYPES_H_ #define RX_RENDER_RHI_TYPES_H_ +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" // Backend-agnostic value types shared by the whole RHI. Nothing in this header @@ -72,7 +73,7 @@ constexpr bool IsDepthFormat(Format format) { // Bytes per texel (per block for BC formats), for footprint estimates and // tightly packed copies. -u32 FormatTexelBytes(Format format); +RX_GPU_EXPORT u32 FormatTexelBytes(Format format); struct Extent2D { u32 width = 0; diff --git a/rxe/gpu/rhi/vulkan_interop.h b/rxe/gpu/rhi/vulkan_interop.h index 6d020fa..e4b34c0 100644 --- a/rxe/gpu/rhi/vulkan_interop.h +++ b/rxe/gpu/rhi/vulkan_interop.h @@ -19,6 +19,9 @@ namespace rx::gpu { struct VulkanHandles { + // The loader entry point the device was created through; what another DSO + // needs to fill its own volk table. + PFN_vkGetInstanceProcAddr get_instance_proc_addr = nullptr; VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; @@ -27,8 +30,30 @@ struct VulkanHandles { VmaAllocator allocator = nullptr; }; +// Null-filled when `device` is not the Vulkan backend. Callers use +// GetVulkanHandles below instead. +RX_GPU_EXPORT VulkanHandles QueryVulkanHandles(Device& device); + // Null-filled when `device` is not the Vulkan backend. -RX_GPU_EXPORT VulkanHandles GetVulkanHandles(Device& device); +// +// Under RX_SHARED every DSO carries its own private volk table (see the top-level +// CMakeLists for why it cannot be shared), and only the device's own DSO fills +// its copy. Every interop consumer asks for the handles before its first vk* +// call, so this is the one place to fill the calling module's copy: inline and +// hidden, it runs in the caller's DSO against that DSO's table, once per device. +inline VulkanHandles GetVulkanHandles(Device& device) { + VulkanHandles handles = QueryVulkanHandles(device); +#if defined(RX_SHARED_BUILD) + static VkDevice loaded = VK_NULL_HANDLE; + if (handles.device != VK_NULL_HANDLE && handles.device != loaded) { + volkInitializeCustom(handles.get_instance_proc_addr); + volkLoadInstanceOnly(handles.instance); + volkLoadDevice(handles.device); + loaded = handles.device; + } +#endif + return handles; +} // Frames-in-flight the renderer cycles (== the number of distinct frame slots a // SceneHookContext::frame_slot can take). An app sizing per-frame resources for diff --git a/rxe/gpu/vulkan/vk_interop.cc b/rxe/gpu/vulkan/vk_interop.cc index 4509898..ad20699 100644 --- a/rxe/gpu/vulkan/vk_interop.cc +++ b/rxe/gpu/vulkan/vk_interop.cc @@ -37,10 +37,11 @@ void LoadVulkanEntryPoints(u32 api_version) { } // namespace vk -VulkanHandles GetVulkanHandles(Device& device) { +VulkanHandles QueryVulkanHandles(Device& device) { if (device.caps().backend != Backend::kVulkan) return {}; auto& vk_device = static_cast(device); - return {.instance = vk_device.instance(), + return {.get_instance_proc_addr = vkGetInstanceProcAddr, + .instance = vk_device.instance(), .physical_device = vk_device.physical_device(), .device = vk_device.device(), .graphics_queue = vk_device.graphics_queue(), diff --git a/rxe/render/atmosphere/clouds.h b/rxe/render/atmosphere/clouds.h index f281c76..d4dba4e 100644 --- a/rxe/render/atmosphere/clouds.h +++ b/rxe/render/atmosphere/clouds.h @@ -1,6 +1,7 @@ #ifndef RX_RENDER_CLOUDS_H_ #define RX_RENDER_CLOUDS_H_ +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/resources.h" @@ -16,7 +17,7 @@ namespace rx::render { // (depth-aware, so terrain occludes clouds). Procedural density with Beer's-law // self-shadowing and a Henyey-Greenstein phase. A simplified Nubis-style model; // runs every frame, no ray tracing. -class Clouds { +class RX_RENDER_EXPORT Clouds { public: struct Frame { Mat4 inv_view_proj; diff --git a/rxe/render/core/bindless.h b/rxe/render/core/bindless.h index 2cb51cf..d29a076 100644 --- a/rxe/render/core/bindless.h +++ b/rxe/render/core/bindless.h @@ -5,6 +5,7 @@ #include #include "base/memory/unique_pointer.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/gpu/rhi/device.h" @@ -17,7 +18,7 @@ namespace rx::render { // buffer device addresses for the SPIR-V readers (vk::RawBufferLoad) and // bindless ByteAddressBuffer slots for DXIL, which has no BDA (see // shaders/rt_geometry.hlsli). -class BindlessRegistry { +class RX_RENDER_EXPORT BindlessRegistry { public: static constexpr u32 kMaxTextures = 4096; static constexpr u32 kMaxMeshes = 16384; diff --git a/rxe/render/core/render_graph.h b/rxe/render/core/render_graph.h index 291e9d4..f313cb9 100644 --- a/rxe/render/core/render_graph.h +++ b/rxe/render/core/render_graph.h @@ -7,6 +7,7 @@ #include "base/functional/function.h" #include "base/memory/move.h" #include "base/strings/xstring.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/gpu/rhi/command_list.h" #include "rxe/gpu/rhi/resources.h" @@ -47,7 +48,7 @@ struct TransientTextureDesc { // Caches images keyed by their description so a steady frame allocates // nothing. Contents are not preserved across frames: every acquisition // starts in kUndefined and the first barrier discards. -class TransientPool { +class RX_RENDER_EXPORT TransientPool { public: explicit TransientPool(gpu::Device& device) : device_(device) {} ~TransientPool(); @@ -84,7 +85,7 @@ struct PassContext { // command list. Passes declare reads and writes with a usage; Compile // assigns physical images from the transient pool and derives the image // barriers between passes from the declared accesses. -class RenderGraph { +class RX_RENDER_EXPORT RenderGraph { public: struct PassBuilder { void Read(ResourceHandle handle, ResourceUsage usage) { accesses.push_back({handle, usage}); } diff --git a/rxe/render/core/renderer.cc b/rxe/render/core/renderer.cc index 4da3456..d988a2c 100644 --- a/rxe/render/core/renderer.cc +++ b/rxe/render/core/renderer.cc @@ -454,17 +454,6 @@ bool Renderer::InitializeOffscreen(const RendererDesc &desc, u32 width, bool Renderer::InitializeCommon(const RendererDesc &desc, ui::Window *window, u32 width, u32 height) { -#if defined(RX_SHARED_BUILD) - // base::Option self-registers through base::InitChain, whose list head is a - // vague-linkage function-local static. Under RX_SHARED every module is built - // with hidden visibility, so that head is a SEPARATE instance per DSO: the - // app's InitOptionsFromEnv() (host.cc) only walks the app DSO's chain and - // never sees this DSO's render options (RX_ASYNC_COMPUTE, RX_DEBUG_VIEW, - // ...). Re-run it here so the render DSO applies its own env overrides before - // any option is read. Disjoint chains, so no option is applied twice. In the - // static build there is one shared chain and this is compiled out. - base::InitOptionsFromEnv(); -#endif desc_ = desc; settings_.aa_mode = desc.aa_mode; settings_.upscaler = desc.upscaler; diff --git a/rxe/render/geometry/fluid_sim.h b/rxe/render/geometry/fluid_sim.h index d6037e3..ce1447a 100644 --- a/rxe/render/geometry/fluid_sim.h +++ b/rxe/render/geometry/fluid_sim.h @@ -15,6 +15,7 @@ // the domain, lava solving first so water sees B + C + d_lava as its bed. The // solver core produces the fields; a separate surface renderer draws them. +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/device.h" @@ -44,7 +45,7 @@ struct FluidDomainDesc { f32 ambient_temperature = 20.0f; }; -class FluidSim { +class RX_RENDER_EXPORT FluidSim { public: static constexpr u32 kMaxSources = 64; static constexpr u32 kMaxResolution = 1024; @@ -117,7 +118,7 @@ class FluidSim { // Readback seam for rxe/render/geometry/fluid_sim_test.cc: exposes the solver's output images // (which the public API only surfaces as sampled views) so a test can // ReadbackImage them. Not part of the renderer/demo API. -struct FluidSimProbe { +struct RX_RENDER_EXPORT FluidSimProbe { static const gpu::GpuImage& state(const FluidSim& sim); static const gpu::GpuImage& velocity(const FluidSim& sim); }; diff --git a/rxe/render/geometry/hair_groom.h b/rxe/render/geometry/hair_groom.h index d968dc6..d6bde0d 100644 --- a/rxe/render/geometry/hair_groom.h +++ b/rxe/render/geometry/hair_groom.h @@ -3,6 +3,7 @@ #include +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/asset/mesh.h" #include "rxe/asset/texture.h" @@ -84,7 +85,7 @@ bool BuildHairGroom(const asset::Mesh& hair_mesh, const GroomParams& params, Gro // and a ponytail whose strands are pinned mid-strand at a gather point. Used // by the strands demo and the strand-sim regression test. enum class TestGroomStyle { kLoose, kBraid, kPonytail }; -bool BuildTestGroom(TestGroomStyle style, u32 guide_count, u32 seed, GroomData* out); +RX_RENDER_EXPORT bool BuildTestGroom(TestGroomStyle style, u32 guide_count, u32 seed, GroomData* out); } // namespace rx::render diff --git a/rxe/render/gi/denoiser_rr.h b/rxe/render/gi/denoiser_rr.h index 4c370a2..4ae1e89 100644 --- a/rxe/render/gi/denoiser_rr.h +++ b/rxe/render/gi/denoiser_rr.h @@ -11,6 +11,7 @@ // native resolution (denoise-only); the engine's upscalers stay separate. // Vulkan-backend only, compiled under RX_HAS_DLSS. +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/types.h" @@ -25,7 +26,7 @@ class Device; namespace rx::render { -class RrDenoiser { +class RX_RENDER_EXPORT RrDenoiser { public: struct Frame { Mat4 world_to_view; diff --git a/rxe/render/gi/path_tracer.h b/rxe/render/gi/path_tracer.h index 6d3e0ad..1dfc991 100644 --- a/rxe/render/gi/path_tracer.h +++ b/rxe/render/gi/path_tracer.h @@ -1,6 +1,7 @@ #ifndef RX_RENDER_PATH_TRACER_H_ #define RX_RENDER_PATH_TRACER_H_ +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/resources.h" @@ -22,7 +23,7 @@ class RayTracingContext; // (AddGbufferPass), then re-modulate the denoised radiance (AddCompositePass) // so the view stays clean while moving. // Needs ray query; the renderer gates it. -class PathTracer { +class RX_RENDER_EXPORT PathTracer { public: struct Frame { Mat4 inv_view_proj; diff --git a/rxe/render/gi/raytracing.h b/rxe/render/gi/raytracing.h index b4e5a7e..eacf9e6 100644 --- a/rxe/render/gi/raytracing.h +++ b/rxe/render/gi/raytracing.h @@ -6,6 +6,7 @@ #include #include "base/memory/unique_pointer.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "foundation/math/math.h" #include "rxe/render/gi/rt_slot_tracker.h" @@ -46,7 +47,7 @@ enum RayMask : u8 { // BLASes build once per uploaded mesh, the TLAS rebuilds every frame from // the visible instances. TLAS slots rotate so a rebuild never races the frame // still in flight. -class RayTracingContext { +class RX_RENDER_EXPORT RayTracingContext { public: // Four ping-pong slots (not two): the async-TLAS path (RX_RT_ASYNC_TLAS) // builds slot N on the compute queue for the *next* frame while this frame's diff --git a/rxe/render/gi/recon_path_tracer.h b/rxe/render/gi/recon_path_tracer.h index 9c74b36..97cdb64 100644 --- a/rxe/render/gi/recon_path_tracer.h +++ b/rxe/render/gi/recon_path_tracer.h @@ -1,6 +1,7 @@ #ifndef RX_RENDER_RECON_PATH_TRACER_H_ #define RX_RENDER_RECON_PATH_TRACER_H_ +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/resources.h" @@ -19,7 +20,7 @@ class RayTracingContext; // its own temporal accumulation (motion reproject + history rejection + clamp + // moments/variance) and an a-trous wavelet filter, and composites albedo back in. // Fully in-tree and tunable, with debug views. Needs ray query. -class ReconPathTracer { +class RX_RENDER_EXPORT ReconPathTracer { public: struct Frame { Mat4 inv_view_proj; diff --git a/rxe/render/gi/restir_di.h b/rxe/render/gi/restir_di.h index c83b5b1..3b94158 100644 --- a/rxe/render/gi/restir_di.h +++ b/rxe/render/gi/restir_di.h @@ -9,6 +9,7 @@ // both the analytic cluster evaluation and the local shadow atlas for those // lights with per-pixel ray-traced visibility. +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/render/gi/raytracing.h" @@ -16,7 +17,7 @@ namespace rx::render { -class RestirDi { +class RX_RENDER_EXPORT RestirDi { public: bool Initialize(gpu::Device& device); void Destroy(gpu::Device& device); diff --git a/rxe/render/gi/skinned_rt.h b/rxe/render/gi/skinned_rt.h index 85c84c0..637e17e 100644 --- a/rxe/render/gi/skinned_rt.h +++ b/rxe/render/gi/skinned_rt.h @@ -4,6 +4,7 @@ #include #include +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/gpu/rhi/command_list.h" #include "rxe/gpu/rhi/resources.h" @@ -38,7 +39,7 @@ class RayTracingContext; // Morph targets are NOT applied: a morphed actor registers anyway (skinning is // what moves the silhouette) but warns once, rather than quietly ray tracing a // face that does not match the rasterized one. -class SkinnedRayTracing { +class RX_RENDER_EXPORT SkinnedRayTracing { public: bool Initialize(gpu::Device& device); void Destroy(gpu::Device& device); diff --git a/rxe/render/pipeline/material_system.h b/rxe/render/pipeline/material_system.h index 7e0694a..0c0a2c1 100644 --- a/rxe/render/pipeline/material_system.h +++ b/rxe/render/pipeline/material_system.h @@ -6,6 +6,7 @@ #include #include "base/memory/unique_pointer.h" +#include "foundation/build_config/export.h" #include "rxe/asset/material.h" #include "rxe/asset/texture.h" #include "rxe/render/core/bindless.h" @@ -34,7 +35,7 @@ namespace rx::render { // materials get NEW binding sets (live ones may be GPU-pending); the bindless // slot moves to a fresh index and the old image/set/slot retire once every // in-flight frame has drained (BeginFrame flushes the ring). -class MaterialSystem { +class RX_RENDER_EXPORT MaterialSystem { public: // Matches the std140 block in mesh.frag. struct Params { diff --git a/rxe/render/post/antialiasing.h b/rxe/render/post/antialiasing.h index 755b565..2829e9b 100644 --- a/rxe/render/post/antialiasing.h +++ b/rxe/render/post/antialiasing.h @@ -1,6 +1,7 @@ #ifndef RX_RENDER_ANTIALIASING_H_ #define RX_RENDER_ANTIALIASING_H_ +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/resources.h" @@ -27,7 +28,7 @@ enum class AntiAliasingMode : u8 { kMsaa, }; -struct JitterSequence { +struct RX_RENDER_EXPORT JitterSequence { // Halton (2,3) offsets in pixel units, centered around zero. static void Sample(u32 frame_index, u32 sample_count, f32* out_x, f32* out_y); }; diff --git a/rxe/render/post/depth_of_field.h b/rxe/render/post/depth_of_field.h index 516dee7..6a6c59a 100644 --- a/rxe/render/post/depth_of_field.h +++ b/rxe/render/post/depth_of_field.h @@ -5,13 +5,14 @@ // readback), a half-res 48-tap golden-spiral gather, and a full-res // composite. Runs on the AA-resolved color, before motion blur. +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/device.h" namespace rx::render { -class DepthOfFieldPass { +class RX_RENDER_EXPORT DepthOfFieldPass { public: bool Initialize(gpu::Device& device); void Destroy(gpu::Device& device); diff --git a/rxe/render/post/motion_blur.h b/rxe/render/post/motion_blur.h index bffa03f..dd10592 100644 --- a/rxe/render/post/motion_blur.h +++ b/rxe/render/post/motion_blur.h @@ -7,13 +7,14 @@ // resolution after the AA/upscale resolve; uv velocities are resolution // independent). +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/device.h" namespace rx::render { -class MotionBlurPass { +class RX_RENDER_EXPORT MotionBlurPass { public: bool Initialize(gpu::Device& device); void Destroy(gpu::Device& device); diff --git a/rxe/render/post/post.h b/rxe/render/post/post.h index 498ff83..7497930 100644 --- a/rxe/render/post/post.h +++ b/rxe/render/post/post.h @@ -6,6 +6,7 @@ #include "base/memory/unique_pointer.h" #include "base/strings/xstring.h" +#include "foundation/build_config/export.h" #include "rxe/render/core/render_graph.h" #include "rxe/render/core/settings.h" #include "rxe/gpu/rhi/device.h" @@ -16,7 +17,7 @@ namespace rx::render { // applies the srgb transfer function and writes the backbuffer. Any gap // between render and output resolution is absorbed here by the linear // sampler until a real upscaler owns it. -class PostPass { +class RX_RENDER_EXPORT PostPass { public: static base::UniquePointer Create(gpu::Device& device, gpu::Format output_format); ~PostPass(); diff --git a/rxe/render/post/reference_compare.h b/rxe/render/post/reference_compare.h index 05e5c9b..3a83a03 100644 --- a/rxe/render/post/reference_compare.h +++ b/rxe/render/post/reference_compare.h @@ -3,6 +3,7 @@ #include "base/strings/xstring.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/device.h" @@ -19,7 +20,7 @@ namespace rx::render { // // It also accumulates a per-region error metric, which is what lets the lookdev // tool's fitting be a measurement instead of a preference. -class ReferenceCompare { +class RX_RENDER_EXPORT ReferenceCompare { public: enum class Mode : u8 { kOff, diff --git a/rxe/render/post/upscaler.h b/rxe/render/post/upscaler.h index 342441c..e164aeb 100644 --- a/rxe/render/post/upscaler.h +++ b/rxe/render/post/upscaler.h @@ -3,6 +3,7 @@ #include "base/memory/unique_pointer.h" +#include "foundation/build_config/export.h" #include "foundation/build_config/types.h" #include "rxe/render/core/render_graph.h" @@ -61,11 +62,11 @@ class Upscaler { // Returns null if the SDK for the requested kind is not compiled in or the // device does not support it. Caller falls back to TAA. -base::UniquePointer CreateUpscaler(const UpscalerDesc& desc, class gpu::Device& device); +RX_RENDER_EXPORT base::UniquePointer CreateUpscaler(const UpscalerDesc& desc, class gpu::Device& device); // The spelling the --upscaler flag and the settings ini use, for logging which // backend a fallback actually landed on. -const char* UpscalerName(UpscalerKind kind); +RX_RENDER_EXPORT const char* UpscalerName(UpscalerKind kind); } // namespace rx::render diff --git a/rxe/render/screenspace/reflection_trace.h b/rxe/render/screenspace/reflection_trace.h index 7edd819..b1c77a9 100644 --- a/rxe/render/screenspace/reflection_trace.h +++ b/rxe/render/screenspace/reflection_trace.h @@ -7,6 +7,7 @@ // sampled by the forward pass instead of tracing inline, which turns the // mirror-to-IBL crossfade into a real glossy distribution. +#include "foundation/build_config/export.h" #include "foundation/math/math.h" #include "rxe/render/core/render_graph.h" #include "rxe/gpu/rhi/device.h" @@ -15,7 +16,7 @@ namespace rx::render { class RayTracingContext; -class ReflectionTrace { +class RX_RENDER_EXPORT ReflectionTrace { public: struct Frame { Mat4 inv_view_proj; // unjittered diff --git a/rxe/render/texturing/decal_bake.h b/rxe/render/texturing/decal_bake.h index 750bc3a..6c4b5e9 100644 --- a/rxe/render/texturing/decal_bake.h +++ b/rxe/render/texturing/decal_bake.h @@ -51,7 +51,7 @@ struct DecalStamp { RX_RENDER_EXPORT Decal MakeDecalProjector(const Vec3& position, const Vec3& normal, const Vec3& up, f32 width, f32 height, f32 depth); -class DecalBaker { +class RX_RENDER_EXPORT DecalBaker { public: // The push-constant channel that carries the tile to the forward pass is one // byte wide (see Renderer's tint_packed packing), and 0 means "no layer". diff --git a/rxe/ui/ugui/ugui_backend.h b/rxe/ui/ugui/ugui_backend.h index 37af72c..5d91af8 100644 --- a/rxe/ui/ugui/ugui_backend.h +++ b/rxe/ui/ugui/ugui_backend.h @@ -10,6 +10,7 @@ #include #include "base/containers/unordered_map.h" #include "base/containers/vector.h" +#include "foundation/build_config/export.h" namespace rx::ui { @@ -19,7 +20,7 @@ namespace rx::ui { // into a command buffer the engine already opened with vkCmdBeginRendering on // the backbuffer, exactly where the debug ImGui overlay records. Also serves as // ultragui's TextureBackend so Image/SVG textures work in draw-data mode. -class GuiRenderBackend final : public ugui::TextureBackend { +class RX_UI_EXPORT GuiRenderBackend final : public ugui::TextureBackend { public: struct InitInfo { VkInstance instance = VK_NULL_HANDLE; diff --git a/rxe/world/world_format.h b/rxe/world/world_format.h index fb69fbb..0ddc9ac 100644 --- a/rxe/world/world_format.h +++ b/rxe/world/world_format.h @@ -110,7 +110,7 @@ struct WorldCellRecord { // The decoded index. Cells are sorted by id: lookup is a binary search and // iteration order never depends on cook order. -struct WorldIndexData { +struct RX_WORLD_EXPORT WorldIndexData { u32 version = 0; u64 world_id = 0; // Identifies the cook that produced this index; every payload carries the @@ -250,7 +250,7 @@ struct WorldInstanceRecord { f32 scale = 1.0f; }; -struct WorldCellPayload { +struct RX_WORLD_EXPORT WorldCellPayload { u32 version = 0; PayloadKind kind = PayloadKind::kEntities; u64 cell_id = 0; diff --git a/third_party/equilibrium b/third_party/equilibrium index daed7ca..be9368c 160000 --- a/third_party/equilibrium +++ b/third_party/equilibrium @@ -1 +1 @@ -Subproject commit daed7caf59059189801669a13b2f8d63deb4d112 +Subproject commit be9368ceb0958f6dbecf10cfc7000d26acaddec6