ADD: planet quadtree stabilized
This commit is contained in:
@@ -7,29 +7,52 @@ Lightweight render graph that builds a per‑frame DAG from pass declarations, c
|
|||||||
- Centralize synchronization and image layout transitions across passes.
|
- Centralize synchronization and image layout transitions across passes.
|
||||||
- Make passes declarative: author declares reads/writes; the graph inserts barriers and begins/ends rendering.
|
- Make passes declarative: author declares reads/writes; the graph inserts barriers and begins/ends rendering.
|
||||||
- Keep existing pass classes (`IRenderPass`) while migrating execution to the graph.
|
- Keep existing pass classes (`IRenderPass`) while migrating execution to the graph.
|
||||||
|
- Provide runtime profiling and debugging capabilities for pass execution.
|
||||||
|
|
||||||
### High‑Level Flow
|
### High‑Level Flow
|
||||||
|
|
||||||
- Engine creates the graph each frame and imports swapchain/G‑Buffer images: `src/core/engine.cpp:303`.
|
- Engine creates the graph each frame and imports swapchain/G‑Buffer images: `src/core/engine.cpp`.
|
||||||
- Each pass registers its work by calling `register_graph(graph, ...)` and declaring resources via a builder.
|
- Each pass registers its work by calling `register_graph(graph, ...)` and declaring resources via a builder.
|
||||||
- The graph appends a present chain (copy HDR `drawImage` → swapchain, then transition to `PRESENT`), optionally inserting ImGui before present.
|
- The graph appends a present chain (copy HDR `drawImage` → swapchain, then transition to `PRESENT`), optionally inserting ImGui before present.
|
||||||
- `compile()` topologically sorts passes by data dependencies (read/write) and computes per‑pass barriers.
|
- `compile()` topologically sorts passes by data dependencies (read/write hazards: RAW/WAW/WAR) and computes per‑pass barriers using `VkDependencyInfo` with `Vk*MemoryBarrier2`.
|
||||||
- `execute(cmd)` emits barriers, begins dynamic rendering if attachments were declared, calls the pass record lambda, and ends rendering.
|
- `execute(cmd)` creates timestamp query pools, emits barriers, begins dynamic rendering if attachments were declared, calls the pass record lambda, ends rendering, and records GPU/CPU timings.
|
||||||
|
- `resolve_timings()` retrieves GPU timestamp results after the fence is signaled, converting them to milliseconds.
|
||||||
|
|
||||||
### Core API
|
### Core API
|
||||||
|
|
||||||
|
**Lifecycle:**
|
||||||
|
- `RenderGraph::init(ctx)` — Initialize with engine context. See `src/render/graph/graph.cpp:28`.
|
||||||
|
- `RenderGraph::clear()` — Clear all passes and reset resources. See `src/render/graph/graph.cpp:34`.
|
||||||
|
- `RenderGraph::shutdown()` — Destroy GPU resources (query pools) before device shutdown. See `src/render/graph/graph.cpp:40`.
|
||||||
|
|
||||||
|
**Pass Registration:**
|
||||||
- `RenderGraph::add_pass(name, RGPassType type, BuildCallback build, RecordCallback record)`
|
- `RenderGraph::add_pass(name, RGPassType type, BuildCallback build, RecordCallback record)`
|
||||||
- Declare image/buffer accesses and attachments inside `build` using `RGPassBuilder`.
|
- Declare image/buffer accesses and attachments inside `build` using `RGPassBuilder`.
|
||||||
- Do your actual rendering/copies in `record` using resolved Vulkan objects from `RGPassResources`.
|
- Do your actual rendering/copies in `record` using resolved Vulkan objects from `RGPassResources`.
|
||||||
- See: `src/render/graph/graph.h:36`, `src/render/graph/graph.cpp:51`.
|
- See: `src/render/graph/graph.h:42`, `src/render/graph/graph.cpp:91`.
|
||||||
|
- Legacy form: `add_pass(name, type, record)` for passes with no resource declarations. See `src/render/graph/graph.cpp:117`.
|
||||||
|
|
||||||
- `RenderGraph::compile()` → builds ordering and per‑pass `Vk*MemoryBarrier2` lists. See `src/render/graph/graph.cpp:83`.
|
**Resource Creation:**
|
||||||
|
- `import_image(desc)` / `import_buffer(desc)` — Import externally owned resources (deduplicated by VkImage/VkBuffer handle).
|
||||||
|
- `create_image(desc)` / `create_buffer(desc)` — Create transient resources (destroyed at end of frame via deletion queue).
|
||||||
|
- `create_depth_image(name, extent, format=D32_SFLOAT)` — Convenience helper for depth-only images with depth attachment + sampled usage. See `src/render/graph/graph.cpp:67`.
|
||||||
|
|
||||||
- `RenderGraph::execute(cmd)` → emits barriers and dynamic rendering begin/end. See `src/render/graph/graph.cpp:592`.
|
**Compilation and Execution:**
|
||||||
|
- `RenderGraph::compile()` — Build topological ordering (Kahn's algorithm) and per‑pass `VkImageMemoryBarrier2` / `VkBufferMemoryBarrier2` lists. Returns false on error. See `src/render/graph/graph.cpp:123`.
|
||||||
|
- `RenderGraph::execute(cmd)` — Creates timestamp query pool, emits barriers via `vkCmdPipelineBarrier2`, begins dynamic rendering if attachments exist, invokes record callbacks, ends rendering, and writes GPU timestamps. See `src/render/graph/graph.cpp:874`.
|
||||||
|
- `RenderGraph::resolve_timings()` — Fetch GPU timestamp results after fence wait and convert to milliseconds. Must be called before next `execute()`. See `src/render/graph/graph.cpp:1314`.
|
||||||
|
|
||||||
- Import helpers for engine images: `import_draw_image()`, `import_depth_image()`, `import_gbuffer_*()`, `import_swapchain_image(index)`. See `src/render/graph/graph.cpp:740`.
|
**Import Helpers:**
|
||||||
|
- `import_draw_image()`, `import_depth_image()`, `import_gbuffer_position()`, `import_gbuffer_normal()`, `import_gbuffer_albedo()`, `import_gbuffer_extra()`, `import_id_buffer()`, `import_swapchain_image(index)` — Convenience wrappers for engine-owned images. See `src/render/graph/graph.cpp:1147–1312`.
|
||||||
|
|
||||||
- Present chain: `add_present_chain(draw, swapchain, appendExtra)` inserts Copy→Present passes and lets you inject extra passes (e.g., ImGui) in between. See `src/render/graph/graph.cpp:705`.
|
**Present Chain:**
|
||||||
|
- `add_present_chain(draw, swapchain, appendExtra)` — Inserts `PresentLetterbox` pass (blit draw→swapchain with letterboxing) and `PreparePresent` pass (layout transition to `PRESENT_SRC_KHR`). Optional `appendExtra` callback injects passes (e.g., ImGui) in between. See `src/render/graph/graph.cpp:1043`.
|
||||||
|
|
||||||
|
**Debug and Profiling:**
|
||||||
|
- `pass_count()`, `pass_name(i)`, `pass_enabled(i)`, `set_pass_enabled(i, enabled)` — Runtime pass enable/disable. See `src/render/graph/graph.h:105–108`.
|
||||||
|
- `debug_get_passes(out)` — Retrieve pass metadata including GPU/CPU timings, resource access counts, attachment info. See `src/render/graph/graph.cpp:1163`.
|
||||||
|
- `debug_get_images(out)` — Retrieve image metadata (imported/transient, format, extent, usage, lifetime). See `src/render/graph/graph.cpp:1186`.
|
||||||
|
- `debug_get_buffers(out)` — Retrieve buffer metadata. See `src/render/graph/graph.cpp:1207`.
|
||||||
|
|
||||||
### Declaring a Pass
|
### Declaring a Pass
|
||||||
|
|
||||||
@@ -65,63 +88,155 @@ void MyPass::register_graph(RenderGraph* graph,
|
|||||||
|
|
||||||
### Builder Reference (`RGPassBuilder`)
|
### Builder Reference (`RGPassBuilder`)
|
||||||
|
|
||||||
- Images
|
Passed to the `BuildCallback` to declare resource accesses and attachments. See `src/render/graph/builder.h:40`.
|
||||||
- `read(RGImageHandle, RGImageUsage)` → sample/read usage for this pass.
|
|
||||||
- `write(RGImageHandle, RGImageUsage)` → write usage (compute/storage/transfer).
|
|
||||||
- `write_color(RGImageHandle, bool clearOnLoad=false, VkClearValue clear={})` → declares a color attachment.
|
|
||||||
- `write_depth(RGImageHandle, bool clearOnLoad=false, VkClearValue clear={})` → declares a depth attachment.
|
|
||||||
|
|
||||||
- Buffers
|
**Image Access:**
|
||||||
- `read_buffer(RGBufferHandle, RGBufferUsage)` / `write_buffer(RGBufferHandle, RGBufferUsage)`.
|
- `read(RGImageHandle, RGImageUsage)` — Declare sampled/read usage (e.g., `SampledFragment`, `TransferSrc`). See `src/render/graph/builder.cpp:20`.
|
||||||
- Convenience import: `read_buffer(VkBuffer, RGBufferUsage, size, name)` and `write_buffer(VkBuffer, ...)` dedup by raw handle.
|
- `write(RGImageHandle, RGImageUsage)` — Declare write usage (e.g., `ComputeWrite`, `TransferDst`). See `src/render/graph/builder.cpp:25`.
|
||||||
|
- `write_color(RGImageHandle, bool clearOnLoad=false, VkClearValue clear={})` — Declare color attachment with optional clear. Sets usage to `ColorAttachment` and `store=true` by default. See `src/render/graph/builder.cpp:30`.
|
||||||
|
- `write_depth(RGImageHandle, bool clearOnLoad=false, VkClearValue clear={})` — Declare depth attachment with optional clear. See `src/render/graph/builder.cpp:40`.
|
||||||
|
|
||||||
See `src/render/graph/builder.h:39` and impl in `src/render/graph/builder.cpp:20`.
|
**Buffer Access:**
|
||||||
|
- `read_buffer(RGBufferHandle, RGBufferUsage)` — Declare buffer read (e.g., `VertexRead`, `IndexRead`, `UniformRead`, `StorageRead`). See `src/render/graph/builder.cpp:50`.
|
||||||
|
- `write_buffer(RGBufferHandle, RGBufferUsage)` — Declare buffer write (e.g., `StorageReadWrite`, `TransferDst`). See `src/render/graph/builder.cpp:55`.
|
||||||
|
- Convenience overloads: `read_buffer(VkBuffer, RGBufferUsage, size, name)` and `write_buffer(VkBuffer, ...)` automatically import and deduplicate by raw `VkBuffer` handle. See `src/render/graph/builder.cpp:60,70`.
|
||||||
|
|
||||||
|
**Resource Resolution (`RGPassResources`):**
|
||||||
|
Used inside the `RecordCallback` to fetch resolved Vulkan objects. See `src/render/graph/builder.h:22`.
|
||||||
|
- `image(RGImageHandle)` → `VkImage`
|
||||||
|
- `image_view(RGImageHandle)` → `VkImageView`
|
||||||
|
- `buffer(RGBufferHandle)` → `VkBuffer`
|
||||||
|
|
||||||
### Resource Model (`RGResourceRegistry`)
|
### Resource Model (`RGResourceRegistry`)
|
||||||
|
|
||||||
- Imported vs transient resources are tracked uniformly with lifetime indices (`firstUse/lastUse`).
|
Manages both imported (externally owned) and transient (graph-owned) resources. See `src/render/graph/resources.h:52`.
|
||||||
- Imports are deduplicated by `VkImage`/`VkBuffer` and keep initial layout/stage/access as the starting state.
|
|
||||||
- Transients are created via `ResourceManager` and auto‑destroyed at end of frame using the frame deletion queue.
|
**Imported Resources:**
|
||||||
- See `src/render/graph/resources.h:11` and `src/render/graph/resources.cpp:1`.
|
- Deduplicated by raw Vulkan handle (`VkImage`/`VkBuffer`) using hash maps (`_imageLookup`/`_bufferLookup`). See `src/render/graph/resources.cpp`.
|
||||||
|
- Initial layout/stage/access preserved from `RGImportedImageDesc`/`RGImportedBufferDesc`.
|
||||||
|
- Ownership remains external; graph does not destroy these resources.
|
||||||
|
|
||||||
|
**Transient Resources:**
|
||||||
|
- Created via `ResourceManager` (`AllocatedImage`/`AllocatedBuffer`) with VMA allocations. See `src/render/graph/resources.cpp`.
|
||||||
|
- Automatically destroyed at end of frame via frame deletion queue.
|
||||||
|
- Usage flags must cover all declared usages (validated during `compile()`).
|
||||||
|
|
||||||
|
**Lifetime Tracking:**
|
||||||
|
- `firstUse` and `lastUse` indices computed during `compile()` (see `src/render/graph/graph.cpp:854–869`).
|
||||||
|
- Used for debug visualization and future aliasing/pooling optimizations.
|
||||||
|
|
||||||
|
**Records (`RGImageRecord`/`RGBufferRecord`):**
|
||||||
|
Unified representation storing `VkImage`/`VkBuffer`, `VkImageView`, format, extent, initial state, and allocation info. See `src/render/graph/resources.h:11,34`.
|
||||||
|
|
||||||
### Synchronization and Layouts
|
### Synchronization and Layouts
|
||||||
|
|
||||||
- For each pass, `compile()` compares previous state with desired usage and, if needed, adds a pre‑pass barrier:
|
**Barrier Generation (see `src/render/graph/graph.cpp:232–851`):**
|
||||||
- Images: `VkImageMemoryBarrier2` with stage/access/layout derived from `RGImageUsage`.
|
|
||||||
- Buffers: `VkBufferMemoryBarrier2` with stage/access derived from `RGBufferUsage`.
|
|
||||||
- Initial state comes from the imported descriptor; if unknown, buffers default to `TOP_OF_PIPE`.
|
|
||||||
- Format/usage checks:
|
|
||||||
- Warns if binding a depth format as color (and vice‑versa).
|
|
||||||
- Warns if a transient resource is used with flags it wasn’t created with.
|
|
||||||
|
|
||||||
Image usage → layout/stage examples (subset):
|
For each enabled pass, `compile()` tracks per-resource state (`ImageState`/`BufferState`) and inserts barriers when hazards are detected:
|
||||||
|
|
||||||
- `SampledFragment` → `SHADER_READ_ONLY_OPTIMAL`, `FRAGMENT_SHADER`.
|
**Image Barriers (`VkImageMemoryBarrier2`):**
|
||||||
- `ColorAttachment` → `COLOR_ATTACHMENT_OPTIMAL`, `COLOR_ATTACHMENT_OUTPUT` (read|write).
|
- Triggered by: layout change, prior write before read/write (RAW/WAW), prior reads before write (WAR).
|
||||||
- `DepthAttachment` → `DEPTH_ATTACHMENT_OPTIMAL`, `EARLY|LATE_FRAGMENT_TESTS`.
|
- Stage/access/layout derived from `RGImageUsage` via `usage_info_image()` (see `src/render/graph/graph.cpp:313–365`).
|
||||||
- `TransferDst` → `TRANSFER_DST_OPTIMAL`, `TRANSFER`.
|
- Aspect determined by usage and format (depth formats get `DEPTH_BIT`, others `COLOR_BIT`).
|
||||||
- `Present` → `PRESENT_SRC_KHR`, `BOTTOM_OF_PIPE`.
|
- Initial state from `RGImportedImageDesc::currentLayout/currentStage/currentAccess`; if unknown (layout ≠ UNDEFINED but stage=NONE), conservatively assumes `ALL_COMMANDS + MEMORY_READ|WRITE`.
|
||||||
|
|
||||||
Buffer usage → stage/access examples:
|
**Buffer Barriers (`VkBufferMemoryBarrier2`):**
|
||||||
|
- Triggered by: prior write before read/write, prior reads before write.
|
||||||
|
- Stage/access derived from `RGBufferUsage` via `usage_info_buffer()` (see `src/render/graph/graph.cpp:367–411`).
|
||||||
|
- Size: exact size for transients, `VK_WHOLE_SIZE` for imports (to avoid validation errors).
|
||||||
|
|
||||||
- `IndexRead` → `INDEX_INPUT`, `INDEX_READ`.
|
**Usage Priority and Conflict Resolution:**
|
||||||
- `VertexRead` → `VERTEX_INPUT`, `VERTEX_ATTRIBUTE_READ`.
|
When a pass declares multiple conflicting usages for the same resource (e.g., both `SampledFragment` and `ColorAttachment`), the graph selects the highest-priority usage for layout determination (see `image_usage_priority()` at `src/render/graph/graph.cpp:499`). Stages and access masks are unioned. Warns if layout mismatch detected.
|
||||||
- `UniformRead` → `ALL_GRAPHICS|COMPUTE`, `UNIFORM_READ`.
|
|
||||||
- `StorageReadWrite` → `COMPUTE|FRAGMENT`, `SHADER_STORAGE_READ|WRITE`.
|
**Image Usage → Layout/Stage/Access Mapping:**
|
||||||
|
See `usage_info_image()` at `src/render/graph/graph.cpp:313`.
|
||||||
|
|
||||||
|
| RGImageUsage | Layout | Stage | Access |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `SampledFragment` | `SHADER_READ_ONLY_OPTIMAL` | `FRAGMENT_SHADER` | `SHADER_SAMPLED_READ` |
|
||||||
|
| `SampledCompute` | `SHADER_READ_ONLY_OPTIMAL` | `COMPUTE_SHADER` | `SHADER_SAMPLED_READ` |
|
||||||
|
| `TransferSrc` | `TRANSFER_SRC_OPTIMAL` | `TRANSFER` | `TRANSFER_READ` |
|
||||||
|
| `TransferDst` | `TRANSFER_DST_OPTIMAL` | `TRANSFER` | `TRANSFER_WRITE` |
|
||||||
|
| `ColorAttachment` | `COLOR_ATTACHMENT_OPTIMAL` | `COLOR_ATTACHMENT_OUTPUT` | `COLOR_ATTACHMENT_READ\|WRITE` |
|
||||||
|
| `DepthAttachment` | `DEPTH_ATTACHMENT_OPTIMAL` | `EARLY_FRAGMENT_TESTS\|LATE_FRAGMENT_TESTS` | `DEPTH_STENCIL_ATTACHMENT_READ\|WRITE` |
|
||||||
|
| `ComputeWrite` | `GENERAL` | `COMPUTE_SHADER` | `SHADER_STORAGE_READ\|WRITE` |
|
||||||
|
| `Present` | `PRESENT_SRC_KHR` | `BOTTOM_OF_PIPE` | `MEMORY_READ` |
|
||||||
|
|
||||||
|
**Buffer Usage → Stage/Access Mapping:**
|
||||||
|
See `usage_info_buffer()` at `src/render/graph/graph.cpp:367`.
|
||||||
|
|
||||||
|
| RGBufferUsage | Stage | Access |
|
||||||
|
|---|---|---|
|
||||||
|
| `TransferSrc` | `TRANSFER` | `TRANSFER_READ` |
|
||||||
|
| `TransferDst` | `TRANSFER` | `TRANSFER_WRITE` |
|
||||||
|
| `VertexRead` | `VERTEX_INPUT` | `VERTEX_ATTRIBUTE_READ` |
|
||||||
|
| `IndexRead` | `INDEX_INPUT` | `INDEX_READ` |
|
||||||
|
| `UniformRead` | `ALL_GRAPHICS\|COMPUTE_SHADER` | `UNIFORM_READ` |
|
||||||
|
| `StorageRead` | `COMPUTE_SHADER\|ALL_GRAPHICS` | `SHADER_STORAGE_READ` |
|
||||||
|
| `StorageReadWrite` | `COMPUTE_SHADER\|ALL_GRAPHICS` | `SHADER_STORAGE_READ\|WRITE` |
|
||||||
|
| `IndirectArgs` | `DRAW_INDIRECT` | `INDIRECT_COMMAND_READ` |
|
||||||
|
|
||||||
|
**Validation Warnings:**
|
||||||
|
- Depth-format image declared as color attachment (or vice versa). See `src/render/graph/graph.cpp:645–657`.
|
||||||
|
- Transient resource used without required usage flags. See `src/render/graph/graph.cpp:659–667` (images), `818–826` (buffers).
|
||||||
|
- Multiple conflicting layouts in single pass. See `src/render/graph/graph.cpp:536–543`.
|
||||||
|
|
||||||
### Built‑In Pass Wiring (Current)
|
### Built‑In Pass Wiring (Current)
|
||||||
|
|
||||||
- Resource uploads (if any) → Background (compute) → Geometry (G‑Buffer) → Lighting (deferred) → SSR → Tonemap+Bloom → FXAA → Transparent → CopyToSwapchain → ImGui → PreparePresent.
|
- Resource uploads (if any) → Background (compute) → Geometry (G‑Buffer) → Lighting (deferred) → SSR → Tonemap+Bloom → FXAA → Transparent → CopyToSwapchain → ImGui → PreparePresent.
|
||||||
- See registrations in `src/core/engine.cpp`.
|
- See registrations in `src/core/engine.cpp`.
|
||||||
|
|
||||||
|
### Topological Sorting and Scheduling
|
||||||
|
|
||||||
|
**Dependency Graph Construction (see `src/render/graph/graph.cpp:127–231`):**
|
||||||
|
- Reads/writes create directed edges: `writer → reader` (RAW), `writer → writer` (WAW), `reader → writer` (WAR).
|
||||||
|
- Disabled passes are skipped during edge construction but remain in the pass list.
|
||||||
|
- Kahn's algorithm produces a linear execution order respecting all dependencies.
|
||||||
|
- If cycle detected (topological sort fails), falls back to insertion order but still computes barriers.
|
||||||
|
|
||||||
|
**Execution Order:**
|
||||||
|
Passes execute in sorted order (or insertion order if cycle). Only enabled passes run; disabled passes are skipped during `execute()`. See `src/render/graph/graph.cpp:895`.
|
||||||
|
|
||||||
|
### Dynamic Rendering Setup
|
||||||
|
|
||||||
|
**Render Area Calculation (see `src/render/graph/graph.cpp:936–1000`):**
|
||||||
|
- Chooses min extent across all color/depth attachments.
|
||||||
|
- Falls back to `EngineContext::drawExtent` if no attachments.
|
||||||
|
- Warns if color attachments have mismatched extents.
|
||||||
|
|
||||||
|
**Attachment Construction:**
|
||||||
|
- Color attachments: `VkRenderingAttachmentInfo` with `clearOnLoad` → `LOAD_OP_CLEAR` / `LOAD_OP_LOAD`, `store` → `STORE_OP_STORE` / `STORE_OP_DONT_CARE`.
|
||||||
|
- Depth attachment: similar logic; `clearValue.depthStencil` used if `clearOnLoad=true`.
|
||||||
|
- Layout forced to `COLOR_ATTACHMENT_OPTIMAL` or `DEPTH_ATTACHMENT_OPTIMAL`.
|
||||||
|
|
||||||
|
See `src/render/graph/graph.cpp:927–1012`.
|
||||||
|
|
||||||
|
### Profiling and Timing
|
||||||
|
|
||||||
|
**GPU Timing (Timestamps):**
|
||||||
|
- Per-frame `VkQueryPool` with 2 queries per pass (begin/end). Created in `execute()`, destroyed in `resolve_timings()` or next `execute()`.
|
||||||
|
- `vkCmdWriteTimestamp2()` at `ALL_COMMANDS_BIT` stage before/after pass recording (see `src/render/graph/graph.cpp:919–923`, `1028–1032`).
|
||||||
|
- `resolve_timings()` fetches results with `VK_QUERY_RESULT_WAIT_BIT`, converts ticks to milliseconds using `timestampPeriod`. See `src/render/graph/graph.cpp:1314–1355`.
|
||||||
|
|
||||||
|
**CPU Timing:**
|
||||||
|
- `std::chrono::high_resolution_clock` measures command recording duration (`cpuStart`/`cpuEnd`). See `src/render/graph/graph.cpp:924`, `1026`.
|
||||||
|
- Stored in `_lastCpuMillis` vector; accessible via `debug_get_passes()`.
|
||||||
|
|
||||||
|
**Debug Structures:**
|
||||||
|
- `RGDebugPassInfo`: name, type, enabled, resource counts, attachment info, `gpuMillis`, `cpuMillis`. See `src/render/graph/graph.h:66`.
|
||||||
|
- `RGDebugImageInfo`: id, name, imported, format, extent, usage, lifetime. See `src/render/graph/graph.h:83`.
|
||||||
|
- `RGDebugBufferInfo`: id, name, imported, size, usage, lifetime. See `src/render/graph/graph.h:94`.
|
||||||
|
|
||||||
### Notes & Limits
|
### Notes & Limits
|
||||||
|
|
||||||
- No aliasing or transient pooling yet; images created via `create_*` are released end‑of‑frame.
|
- **No aliasing or transient pooling**: Transient images/buffers created via `create_*` are released end‑of‑frame via frame deletion queue.
|
||||||
- Graph scheduling uses a topological order by data dependency; it does not parallelize across queues.
|
- **Single-queue execution**: Topological order is linear; no multi-queue parallelization.
|
||||||
- Load/store control for attachments is minimal (`clearOnLoad`, `store` on `RGAttachmentInfo`).
|
- **Minimal load/store control**: Only `clearOnLoad` and `store` flags on `RGAttachmentInfo`; no resolve or stencil control.
|
||||||
- Render area is the min of all declared attachment extents and `EngineContext::drawExtent`.
|
- **No mid-pass barriers**: Conflicting usages within a single pass cannot be synchronized (warns but proceeds with unioned stages/access).
|
||||||
|
- **No automatic resource aliasing**: Future work could reuse transient allocations based on lifetime non-overlap.
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|
||||||
- Each pass is wrapped with a debug label (`RG: <name>`).
|
- **Per-pass debug labels**: `vkdebug::cmd_begin_label(cmd, "RG: <name>")` wraps each pass (see `src/render/graph/graph.cpp:903–906`, `1035–1038`).
|
||||||
- Compile prints warnings for suspicious usages or format mismatches.
|
- **Compile-time validation warnings**: Printed via `fmt::println` for format mismatches, missing usage flags, layout conflicts.
|
||||||
|
- **Runtime introspection**: Use `debug_get_*` APIs to export pass/image/buffer metadata for visualization/debugging tools.
|
||||||
|
|||||||
@@ -22,15 +22,22 @@ auto chairPath = assets->modelPath("models/chair.glb");
|
|||||||
- Paths
|
- Paths
|
||||||
- `std::string shaderPath(std::string_view)`
|
- `std::string shaderPath(std::string_view)`
|
||||||
- `std::string assetPath(std::string_view)` / `modelPath(std::string_view)`
|
- `std::string assetPath(std::string_view)` / `modelPath(std::string_view)`
|
||||||
|
- `const AssetPaths& paths() const` / `void setPaths(const AssetPaths &p)` — get/set asset paths
|
||||||
- glTF
|
- glTF
|
||||||
- `std::optional<std::shared_ptr<LoadedGLTF>> loadGLTF(std::string_view nameOrPath)` — cached by canonical absolute path
|
- `std::optional<std::shared_ptr<LoadedGLTF>> loadGLTF(std::string_view nameOrPath)` — cached by canonical absolute path
|
||||||
|
- `std::optional<std::shared_ptr<LoadedGLTF>> loadGLTF(std::string_view nameOrPath, const GLTFLoadCallbacks *cb)` — with custom callbacks
|
||||||
|
- `size_t prefetchGLTFTextures(std::string_view nameOrPath)` — schedule texture loads ahead of time
|
||||||
|
- `GLTFTexturePrefetchResult prefetchGLTFTexturesWithHandles(std::string_view nameOrPath)` — returns handles for tracking
|
||||||
- Meshes
|
- Meshes
|
||||||
- `std::shared_ptr<MeshAsset> createMesh(const MeshCreateInfo &info)`
|
- `std::shared_ptr<MeshAsset> createMesh(const MeshCreateInfo &info)`
|
||||||
- `std::shared_ptr<MeshAsset> createMesh(const std::string &name, std::span<Vertex> v, std::span<uint32_t> i, std::shared_ptr<GLTFMaterial> material = {})`
|
- `std::shared_ptr<MeshAsset> createMesh(const std::string &name, std::span<Vertex> v, std::span<uint32_t> i, std::shared_ptr<GLTFMaterial> material = {}, bool build_bvh = true)`
|
||||||
- `std::shared_ptr<MeshAsset> getMesh(const std::string &name) const`
|
- `std::shared_ptr<MeshAsset> getMesh(const std::string &name) const`
|
||||||
- `std::shared_ptr<MeshAsset> getPrimitive(std::string_view name) const` (returns existing default primitives if created)
|
- `std::shared_ptr<MeshAsset> getPrimitive(std::string_view name) const` — returns existing default primitives if created
|
||||||
- `bool removeMesh(const std::string &name)`
|
- `bool removeMesh(const std::string &name)`
|
||||||
|
- `bool removeMeshDeferred(const std::string &name, DeletionQueue &dq)` — deferred cleanup via deletion queue
|
||||||
- `void cleanup()` — releases meshes, material buffers, and any images owned by the manager
|
- `void cleanup()` — releases meshes, material buffers, and any images owned by the manager
|
||||||
|
- Materials
|
||||||
|
- `std::shared_ptr<GLTFMaterial> createMaterialFromConstants(const std::string &name, const GLTFMetallic_Roughness::MaterialConstants &constants, MaterialPass pass = MaterialPass::MainColor)` — create PBR material from constants using engine default textures
|
||||||
|
|
||||||
### Mesh Creation Model
|
### Mesh Creation Model
|
||||||
|
|
||||||
@@ -41,15 +48,19 @@ struct AssetManager::MaterialOptions {
|
|||||||
std::string albedoPath; // resolved through AssetManager
|
std::string albedoPath; // resolved through AssetManager
|
||||||
std::string metalRoughPath; // resolved through AssetManager
|
std::string metalRoughPath; // resolved through AssetManager
|
||||||
std::string normalPath; // resolved through AssetManager (tangent-space normal)
|
std::string normalPath; // resolved through AssetManager (tangent-space normal)
|
||||||
|
std::string occlusionPath; // resolved through AssetManager (ambient occlusion)
|
||||||
|
std::string emissivePath; // resolved through AssetManager (emissive/glow)
|
||||||
bool albedoSRGB = true; // VK_FORMAT_R8G8B8A8_SRGB when true
|
bool albedoSRGB = true; // VK_FORMAT_R8G8B8A8_SRGB when true
|
||||||
bool metalRoughSRGB = false; // VK_FORMAT_R8G8B8A8_UNORM when false
|
bool metalRoughSRGB = false; // VK_FORMAT_R8G8B8A8_UNORM when false
|
||||||
bool normalSRGB = false; // normal maps should be UNORM
|
bool normalSRGB = false; // normal maps should be UNORM
|
||||||
|
bool occlusionSRGB = false; // occlusion should be UNORM
|
||||||
|
bool emissiveSRGB = true; // emissive is typically sRGB
|
||||||
GLTFMetallic_Roughness::MaterialConstants constants{}; // extra[0].x as normalScale
|
GLTFMetallic_Roughness::MaterialConstants constants{}; // extra[0].x as normalScale
|
||||||
MaterialPass pass = MaterialPass::MainColor; // or Transparent
|
MaterialPass pass = MaterialPass::MainColor; // or Transparent
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AssetManager::MeshGeometryDesc {
|
struct AssetManager::MeshGeometryDesc {
|
||||||
enum class Type { Provided, Cube, Sphere };
|
enum class Type { Provided, Cube, Sphere, Plane, Capsule };
|
||||||
Type type = Type::Provided;
|
Type type = Type::Provided;
|
||||||
std::span<Vertex> vertices{}; // when Provided
|
std::span<Vertex> vertices{}; // when Provided
|
||||||
std::span<uint32_t> indices{}; // when Provided
|
std::span<uint32_t> indices{}; // when Provided
|
||||||
@@ -65,8 +76,9 @@ struct AssetManager::MeshMaterialDesc {
|
|||||||
|
|
||||||
struct AssetManager::MeshCreateInfo {
|
struct AssetManager::MeshCreateInfo {
|
||||||
std::string name; // cache key; reused if already created
|
std::string name; // cache key; reused if already created
|
||||||
MeshGeometryDesc geometry; // Provided / Cube / Sphere
|
MeshGeometryDesc geometry; // Provided / Cube / Sphere / Plane / Capsule
|
||||||
MeshMaterialDesc material; // Default or Textured
|
MeshMaterialDesc material; // Default or Textured
|
||||||
|
std::optional<BoundsType> boundsType; // optional override for collision/picking bounds
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -113,9 +125,23 @@ si.material.kind = AssetManager::MeshMaterialDesc::Kind::Default;
|
|||||||
auto sphere = ctx->getAssets()->createMesh(si);
|
auto sphere = ctx->getAssets()->createMesh(si);
|
||||||
ctx->scene->addMeshInstance("sphere.instance", sphere,
|
ctx->scene->addMeshInstance("sphere.instance", sphere,
|
||||||
glm::translate(glm::mat4(1.f), glm::vec3(2.f, 0.f, -2.f)));
|
glm::translate(glm::mat4(1.f), glm::vec3(2.f, 0.f, -2.f)));
|
||||||
|
|
||||||
|
// Plane primitive
|
||||||
|
AssetManager::MeshCreateInfo pi{};
|
||||||
|
pi.name = "groundPlane";
|
||||||
|
pi.geometry.type = AssetManager::MeshGeometryDesc::Type::Plane;
|
||||||
|
pi.material.kind = AssetManager::MeshMaterialDesc::Kind::Default;
|
||||||
|
auto plane = ctx->getAssets()->createMesh(pi);
|
||||||
|
|
||||||
|
// Capsule primitive
|
||||||
|
AssetManager::MeshCreateInfo capi{};
|
||||||
|
capi.name = "capsuleA";
|
||||||
|
capi.geometry.type = AssetManager::MeshGeometryDesc::Type::Capsule;
|
||||||
|
capi.material.kind = AssetManager::MeshMaterialDesc::Kind::Default;
|
||||||
|
auto capsule = ctx->getAssets()->createMesh(capi);
|
||||||
```
|
```
|
||||||
|
|
||||||
Textured primitive (albedo + metal-rough + normal):
|
Textured primitive (albedo + metal-rough + normal + occlusion + emissive):
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
AssetManager::MeshCreateInfo ti{};
|
AssetManager::MeshCreateInfo ti{};
|
||||||
@@ -128,6 +154,8 @@ ti.material.kind = AssetManager::MeshMaterialDesc::Kind::Textured;
|
|||||||
ti.material.options.albedoPath = "textures/ground_albedo.png"; // sRGB
|
ti.material.options.albedoPath = "textures/ground_albedo.png"; // sRGB
|
||||||
ti.material.options.metalRoughPath = "textures/ground_mr.png"; // UNORM, G=roughness, B=metallic
|
ti.material.options.metalRoughPath = "textures/ground_mr.png"; // UNORM, G=roughness, B=metallic
|
||||||
ti.material.options.normalPath = "textures/ground_n.png"; // UNORM
|
ti.material.options.normalPath = "textures/ground_n.png"; // UNORM
|
||||||
|
ti.material.options.occlusionPath = "textures/ground_ao.png"; // UNORM (optional)
|
||||||
|
ti.material.options.emissivePath = "textures/ground_emit.png"; // sRGB (optional)
|
||||||
ti.material.options.constants.extra[0].x = 1.0f; // normalScale
|
ti.material.options.constants.extra[0].x = 1.0f; // normalScale
|
||||||
// ti.material.options.pass = MaterialPass::Transparent; // optional
|
// ti.material.options.pass = MaterialPass::Transparent; // optional
|
||||||
|
|
||||||
@@ -136,7 +164,25 @@ glm::mat4 tx = glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f));
|
|||||||
ctx->scene->addMeshInstance("ground.textured", texturedPlane, tx);
|
ctx->scene->addMeshInstance("ground.textured", texturedPlane, tx);
|
||||||
```
|
```
|
||||||
|
|
||||||
Textured cube/sphere via options is analogous — set `geometry.type` to `Cube` or `Sphere` and fill `material.options`.
|
Textured cube/sphere/plane/capsule via options is analogous — set `geometry.type` to `Cube`, `Sphere`, `Plane`, or `Capsule` and fill `material.options`.
|
||||||
|
|
||||||
|
Using custom material from constants:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Create a material with custom PBR values (using engine default textures)
|
||||||
|
GLTFMetallic_Roughness::MaterialConstants constants{};
|
||||||
|
constants.colorFactors = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); // red
|
||||||
|
constants.metal_rough_factors = glm::vec4(0.0f, 0.8f, 0.0f, 0.0f); // non-metallic, rough
|
||||||
|
|
||||||
|
auto redMaterial = ctx->getAssets()->createMaterialFromConstants(
|
||||||
|
"red_rough_material",
|
||||||
|
constants,
|
||||||
|
MaterialPass::MainColor
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use with custom mesh
|
||||||
|
auto mesh = ctx->getAssets()->createMesh("custom_mesh", vertices, indices, redMaterial);
|
||||||
|
```
|
||||||
|
|
||||||
Runtime glTF spawning:
|
Runtime glTF spawning:
|
||||||
|
|
||||||
@@ -154,11 +200,74 @@ ctx->scene->addGLTFInstance("chair01", *chair,
|
|||||||
ctx->scene->removeGLTFInstance("chair01");
|
ctx->scene->removeGLTFInstance("chair01");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Texture Prefetching
|
||||||
|
|
||||||
|
Queue texture loads for a glTF file ahead of time. This parses the glTF, builds TextureCache keys for referenced images (both external URIs and embedded images in buffers), and issues `TextureCache::request()` calls. Actual uploads happen via the normal per-frame pump.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Simple version: returns number of textures scheduled
|
||||||
|
size_t count = ctx->getAssets()->prefetchGLTFTextures("models/heavy_asset.glb");
|
||||||
|
|
||||||
|
// Advanced version: returns handles for tracking progress
|
||||||
|
auto result = ctx->getAssets()->prefetchGLTFTexturesWithHandles("models/heavy_asset.glb");
|
||||||
|
fmt::println("Scheduled {} textures", result.scheduled);
|
||||||
|
// Use result.handles with TextureCache to monitor loading state
|
||||||
|
```
|
||||||
|
|
||||||
|
Texture prefetching is particularly useful when combined with `AsyncAssetLoader` for loading large models in the background.
|
||||||
|
|
||||||
|
### Async Asset Loading
|
||||||
|
|
||||||
|
The `AsyncAssetLoader` class provides asynchronous glTF loading with worker threads for CPU-bound tasks (file I/O, parsing, mesh/BVH building). GPU uploads are still deferred through ResourceManager and the Render Graph.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Access via EngineContext
|
||||||
|
auto *loader = ctx->async_loader;
|
||||||
|
|
||||||
|
// Queue a model to load in the background
|
||||||
|
auto jobID = loader->load_gltf_async(
|
||||||
|
"spaceship_01", // scene instance name
|
||||||
|
"models/spaceship.glb", // model path (resolved via AssetManager)
|
||||||
|
glm::translate(glm::mat4(1.f), glm::vec3(0, 5, -10)), // transform
|
||||||
|
true // preload textures
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check progress in your update loop
|
||||||
|
JobState state;
|
||||||
|
float progress;
|
||||||
|
std::string error;
|
||||||
|
if (loader->get_job_status(jobID, state, progress, &error)) {
|
||||||
|
if (state == JobState::Completed) {
|
||||||
|
fmt::println("Model loaded successfully!");
|
||||||
|
} else if (state == JobState::Failed) {
|
||||||
|
fmt::println("Failed to load: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit completed jobs to the scene (call once per frame)
|
||||||
|
loader->pump_main_thread(*ctx->scene);
|
||||||
|
|
||||||
|
// Alternative: use WorldVec3 for large-world coordinates
|
||||||
|
auto jobID2 = loader->load_gltf_async(
|
||||||
|
"distant_building",
|
||||||
|
"models/building.glb",
|
||||||
|
WorldVec3{1000000.0, 0.0, 500000.0}, // world position
|
||||||
|
glm::quat(1.0f, 0.0f, 0.0f, 0.0f), // rotation
|
||||||
|
glm::vec3(1.0f), // scale
|
||||||
|
false // don't preload textures
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `AsyncAssetLoader` integrates with `TextureCache` to track texture streaming progress. When `preload_textures` is true, the loader will schedule all model textures for loading and track their residency state.
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- Default primitives: The engine creates default Cube/Sphere meshes via `AssetManager` and registers them as dynamic scene instances.
|
- Default primitives: The engine creates default Cube/Sphere/Plane/Capsule meshes via `AssetManager` and registers them as dynamic scene instances.
|
||||||
- Reuse by name: `createMesh("name", ...)` returns the cached mesh if it already exists. Use a unique name or call `removeMesh(name)` to replace.
|
- Reuse by name: `createMesh("name", ...)` returns the cached mesh if it already exists. Use a unique name or call `removeMesh(name)` to replace.
|
||||||
- sRGB/UNORM: Albedo is sRGB by default, metal-rough is UNORM by default. Adjust via `MaterialOptions`.
|
- sRGB/UNORM: Albedo and emissive are sRGB by default, metal-rough/normal/occlusion are UNORM by default. Adjust via `MaterialOptions`.
|
||||||
- Hot reload: Shaders are resolved via `shaderPath()`; pipeline hot reload is handled by the pipeline manager, not the AssetManager.
|
- Hot reload: Shaders are resolved via `shaderPath()`; pipeline hot reload is handled by the pipeline manager, not the AssetManager.
|
||||||
- Normal maps: Supported. If `normalPath` is empty, a flat normal is used.
|
- Normal maps: Supported. If `normalPath` is empty, a flat normal is used.
|
||||||
|
- Occlusion & Emissive: Supported via `occlusionPath` and `emissivePath` in `MaterialOptions`.
|
||||||
- Tangents: Loaded from glTF when present; otherwise generated. Enable MikkTSpace at configure time with `-DENABLE_MIKKTS=ON`.
|
- Tangents: Loaded from glTF when present; otherwise generated. Enable MikkTSpace at configure time with `-DENABLE_MIKKTS=ON`.
|
||||||
|
- BVH building: Enabled by default for meshes (`build_bvh = true`). Required for picking and ray-tracing.
|
||||||
|
- Deferred cleanup: Use `removeMeshDeferred()` when destroying meshes during rendering to avoid destroying resources that are in-flight on the GPU.
|
||||||
|
|||||||
@@ -16,11 +16,54 @@ namespace planet
|
|||||||
PatchKey key{};
|
PatchKey key{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void compute_patch_visibility_terms(const PatchKey &key,
|
||||||
|
const glm::dvec3 &patch_center_dir,
|
||||||
|
double radius_m,
|
||||||
|
double &out_cos_patch_radius,
|
||||||
|
double &out_sin_patch_radius,
|
||||||
|
double &out_bound_radius_m)
|
||||||
|
{
|
||||||
|
glm::dvec3 c = patch_center_dir;
|
||||||
|
const double c_len2 = glm::dot(c, c);
|
||||||
|
if (!(c_len2 > 0.0))
|
||||||
|
{
|
||||||
|
c = glm::dvec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c *= (1.0 / std::sqrt(c_len2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double u0 = 0.0, u1 = 0.0, v0 = 0.0, v1 = 0.0;
|
||||||
|
cubesphere_tile_uv_bounds(key.level, key.x, key.y, u0, u1, v0, v1);
|
||||||
|
|
||||||
|
// Conservative angular radius: max angle from patch center direction to any corner direction.
|
||||||
|
double min_dot = 1.0;
|
||||||
|
min_dot = std::min(min_dot, glm::dot(c, cubesphere_unit_direction(key.face, u0, v0)));
|
||||||
|
min_dot = std::min(min_dot, glm::dot(c, cubesphere_unit_direction(key.face, u1, v0)));
|
||||||
|
min_dot = std::min(min_dot, glm::dot(c, cubesphere_unit_direction(key.face, u0, v1)));
|
||||||
|
min_dot = std::min(min_dot, glm::dot(c, cubesphere_unit_direction(key.face, u1, v1)));
|
||||||
|
|
||||||
|
const double cos_a = glm::clamp(min_dot, -1.0, 1.0);
|
||||||
|
const double sin_a = std::sqrt(glm::max(0.0, 1.0 - cos_a * cos_a));
|
||||||
|
|
||||||
|
// Vertex positions are built as (unit_dir - patch_center_dir) * radius (chord length).
|
||||||
|
const double chord_r = radius_m * std::sqrt(glm::max(0.0, 2.0 - 2.0 * cos_a));
|
||||||
|
|
||||||
|
// Skirts extend inward; add a small safety margin so CPU culling stays conservative.
|
||||||
|
const double skirt_depth = cubesphere_skirt_depth_m(radius_m, key.level);
|
||||||
|
|
||||||
|
out_cos_patch_radius = cos_a;
|
||||||
|
out_sin_patch_radius = sin_a;
|
||||||
|
out_bound_radius_m = glm::max(1.0, chord_r + skirt_depth);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_patch_visible_horizon(const WorldVec3 &body_center_world,
|
bool is_patch_visible_horizon(const WorldVec3 &body_center_world,
|
||||||
double radius_m,
|
double radius_m,
|
||||||
const WorldVec3 &camera_world,
|
const WorldVec3 &camera_world,
|
||||||
const glm::dvec3 &patch_center_dir,
|
const glm::dvec3 &patch_center_dir,
|
||||||
double patch_edge_m)
|
double cos_patch_radius,
|
||||||
|
double sin_patch_radius)
|
||||||
{
|
{
|
||||||
const glm::dvec3 w = camera_world - body_center_world;
|
const glm::dvec3 w = camera_world - body_center_world;
|
||||||
const double d = glm::length(w);
|
const double d = glm::length(w);
|
||||||
@@ -36,15 +79,13 @@ namespace planet
|
|||||||
const double cos_h = glm::clamp(radius_m / d, 0.0, 1.0);
|
const double cos_h = glm::clamp(radius_m / d, 0.0, 1.0);
|
||||||
const double sin_h = std::sqrt(glm::max(0.0, 1.0 - cos_h * cos_h));
|
const double sin_h = std::sqrt(glm::max(0.0, 1.0 - cos_h * cos_h));
|
||||||
|
|
||||||
// Expand horizon by patch angular radius to avoid culling near silhouettes.
|
|
||||||
const double half_diag_m = patch_edge_m * 0.7071067811865476; // sqrt(2)/2
|
|
||||||
const double ang = glm::clamp(half_diag_m / radius_m, 0.0, glm::pi<double>());
|
|
||||||
const double cos_a = std::cos(ang);
|
|
||||||
const double sin_a = std::sin(ang);
|
|
||||||
|
|
||||||
// Visible if theta <= theta_h + ang:
|
// Visible if theta <= theta_h + ang:
|
||||||
// cos(theta) >= cos(theta_h + ang)
|
// cos(theta) >= cos(theta_h + ang)
|
||||||
const double cos_limit = cos_h * cos_a - sin_h * sin_a;
|
const double cos_limit = cos_h * cos_patch_radius - sin_h * sin_patch_radius;
|
||||||
|
if (!std::isfinite(cos_theta) || !std::isfinite(cos_limit))
|
||||||
|
{
|
||||||
|
return true; // fail-safe: avoid catastrophic full culls
|
||||||
|
}
|
||||||
return cos_theta >= cos_limit;
|
return cos_theta >= cos_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +202,22 @@ namespace planet
|
|||||||
const double patch_edge_m = cubesphere_patch_edge_m(radius_m, k.level);
|
const double patch_edge_m = cubesphere_patch_edge_m(radius_m, k.level);
|
||||||
const glm::dvec3 patch_dir = cubesphere_patch_center_direction(k.face, k.level, k.x, k.y);
|
const glm::dvec3 patch_dir = cubesphere_patch_center_direction(k.face, k.level, k.x, k.y);
|
||||||
|
|
||||||
|
double cos_patch_radius = 1.0;
|
||||||
|
double sin_patch_radius = 0.0;
|
||||||
|
double patch_bound_r_m = 1.0;
|
||||||
|
if (_settings.horizon_cull || _settings.frustum_cull)
|
||||||
|
{
|
||||||
|
compute_patch_visibility_terms(k, patch_dir, radius_m, cos_patch_radius, sin_patch_radius, patch_bound_r_m);
|
||||||
|
}
|
||||||
|
|
||||||
if (_settings.horizon_cull)
|
if (_settings.horizon_cull)
|
||||||
{
|
{
|
||||||
if (!is_patch_visible_horizon(body_center_world, radius_m, camera_world, patch_dir, patch_edge_m))
|
if (!is_patch_visible_horizon(body_center_world,
|
||||||
|
radius_m,
|
||||||
|
camera_world,
|
||||||
|
patch_dir,
|
||||||
|
cos_patch_radius,
|
||||||
|
sin_patch_radius))
|
||||||
{
|
{
|
||||||
_stats.nodes_culled++;
|
_stats.nodes_culled++;
|
||||||
continue;
|
continue;
|
||||||
@@ -176,7 +230,7 @@ namespace planet
|
|||||||
if (_settings.frustum_cull)
|
if (_settings.frustum_cull)
|
||||||
{
|
{
|
||||||
const glm::vec3 patch_center_local = world_to_local(patch_center_world, origin_world);
|
const glm::vec3 patch_center_local = world_to_local(patch_center_world, origin_world);
|
||||||
const float bound_r = static_cast<float>(patch_edge_m * 0.7071067811865476);
|
const float bound_r = static_cast<float>(patch_bound_r_m);
|
||||||
if (!is_patch_visible_frustum(patch_center_local, bound_r, scene_data.viewproj))
|
if (!is_patch_visible_frustum(patch_center_local, bound_r, scene_data.viewproj))
|
||||||
{
|
{
|
||||||
_stats.nodes_culled++;
|
_stats.nodes_culled++;
|
||||||
|
|||||||
Reference in New Issue
Block a user