EDIT: Docs and minor bug fixed
This commit is contained in:
23
docs/ASSETS.md
Normal file
23
docs/ASSETS.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
**Assets & Paths**
|
||||||
|
|
||||||
|
- Default locations
|
||||||
|
- `assets/` for models/textures and `shaders/` for GLSL live at the repo root.
|
||||||
|
- The engine auto‑detects these folders by walking up from the working directory.
|
||||||
|
|
||||||
|
- Override root via environment
|
||||||
|
- Set `VKG_ASSET_ROOT` to a directory containing `assets/` and/or `shaders/`.
|
||||||
|
- Example: `VKG_ASSET_ROOT=/home/user/vulkan-engine ./bin/vulkan_engine`
|
||||||
|
|
||||||
|
- API
|
||||||
|
- Use `AssetLocator` through `EngineContext::getAssets()` helpers:
|
||||||
|
- `shaderPath("name.spv")` resolves a shader.
|
||||||
|
- `assetPath("relative/or/absolute")` resolves runtime assets.
|
||||||
|
- `modelPath("some.glb")` is an alias for `assetPath`.
|
||||||
|
|
||||||
|
- Sample content
|
||||||
|
- The engine loads `assets/police_office.glb` by default in `VulkanEngine::init()`.
|
||||||
|
- Ensure this file (and any textures it references) exists under your asset root, or adjust the path used by the sample scene.
|
||||||
|
|
||||||
|
- Materials & sRGB
|
||||||
|
- See `docs/asset_manager.md` for mesh/material creation and sRGB/UNORM handling.
|
||||||
|
|
||||||
38
docs/BUILD.md
Normal file
38
docs/BUILD.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
**Build & Environment**
|
||||||
|
|
||||||
|
- Prerequisites
|
||||||
|
- Vulkan SDK installed and `VULKAN_SDK` set.
|
||||||
|
- A C++20 compiler and CMake ≥ 3.8.
|
||||||
|
- GPU drivers with Vulkan 1.2+.
|
||||||
|
|
||||||
|
- Configure
|
||||||
|
- `cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug`
|
||||||
|
- Re-run configure after toolchain or dependency changes.
|
||||||
|
|
||||||
|
- Build (single-config)
|
||||||
|
- `cmake --build build --target vulkan_engine`
|
||||||
|
|
||||||
|
- Build (multi-config, e.g., MSVC)
|
||||||
|
- `cmake --build build --config Release`
|
||||||
|
|
||||||
|
- Run
|
||||||
|
- `./bin/vulkan_engine` (Linux/macOS)
|
||||||
|
- `bin/vulkan_engine.exe` (Windows)
|
||||||
|
|
||||||
|
- Shaders
|
||||||
|
- CMake compiles GLSL via `glslangValidator` to SPIR‑V targeting Vulkan 1.2:
|
||||||
|
- Files under `shaders/*.vert|*.frag|*.comp` are rebuilt on `cmake --build`.
|
||||||
|
- Windows helper: `./compile_shaders.ps1` uses `glslc` with `--target-env=vulkan1.3` and supports additional stages (mesh/task/ray tracing).
|
||||||
|
- Ensure `glslangValidator`/`glslc` is on `PATH`. See `docs/SHADERS.md`.
|
||||||
|
|
||||||
|
- Windows SDK note
|
||||||
|
- `CMakeLists.txt` includes a default SDK path for Windows `1.3.296.0`:
|
||||||
|
- Update the path or set `VULKAN_SDK` accordingly if your version differs.
|
||||||
|
|
||||||
|
- Third‑party deps
|
||||||
|
- Vendored under `third_party/` and brought in via CMake. Do not edit headers directly; update through targets.
|
||||||
|
|
||||||
|
- Validation Layers
|
||||||
|
- Enabled in Debug (`kUseValidationLayers = true` in `src/core/config.h`).
|
||||||
|
- Disable by building Release or toggling the flag during local experimentation.
|
||||||
|
|
||||||
34
docs/FrameResources.md
Normal file
34
docs/FrameResources.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
## Frame Resources: Per-Frame Command, Sync, and Transient Descriptors
|
||||||
|
|
||||||
|
Per-frame struct that owns the command buffer, semaphores/fence, a transient descriptor allocator, and a small deletion queue. Frames are indexed by `FRAME_OVERLAP` (currently 2) and rotated by `_frameNumber` in `VulkanEngine`.
|
||||||
|
|
||||||
|
- File: `src/core/frame_resources.h/.cpp`
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Command recording: `_mainCommandBuffer` allocated from a per-frame `_commandPool` with RESET flag.
|
||||||
|
- Synchronization: `_swapchainSemaphore` (image acquired), `_renderSemaphore` (render finished), `_renderFence` (CPU wait per frame).
|
||||||
|
- Transient descriptors: `_frameDescriptors` is a `DescriptorAllocatorGrowable` cleared every frame via `clear_pools()`.
|
||||||
|
- Lifetime: `_deletionQueue` holds lambdas for transient GPU objects created during the frame (buffers/images) and is flushed at the start of the next frame.
|
||||||
|
|
||||||
|
### Frame Flow (engine side)
|
||||||
|
- Start of frame:
|
||||||
|
- Wait on `_renderFence` (previous GPU work for this frame index), flush `_deletionQueue`, and clear `_frameDescriptors` pools.
|
||||||
|
- Acquire swapchain image signaling `_swapchainSemaphore`.
|
||||||
|
- Reset `_renderFence` and `_mainCommandBuffer`; begin recording.
|
||||||
|
- Publish `currentFrame` pointer and `drawExtent` on `EngineContext`.
|
||||||
|
- Graph build and execute:
|
||||||
|
- Render Graph is cleared and rebuilt; `ResourceManager::register_upload_pass(...)` is added first if there are pending uploads.
|
||||||
|
- Passes record using the published `currentFrame` to allocate transient descriptor sets and to enqueue per-frame cleanups.
|
||||||
|
- Submit and present:
|
||||||
|
- Submit `cmd` with wait on `_swapchainSemaphore` and signal `_renderSemaphore`, fence `_renderFence`.
|
||||||
|
- Present waits on `_renderSemaphore`.
|
||||||
|
|
||||||
|
### Do/Don’t
|
||||||
|
- Do use `currentFrame->_frameDescriptors` for descriptor sets that live only for this frame.
|
||||||
|
- Do push transient resource destruction into `currentFrame->_deletionQueue`.
|
||||||
|
- Don’t stash per-frame descriptor sets across frames — they are reset on `clear_pools()`.
|
||||||
|
|
||||||
|
### Extending
|
||||||
|
- If a pass needs additional short-lived command buffers, allocate them from `_commandPool` and reset per frame.
|
||||||
|
- If you add frames-in-flight, update `FRAME_OVERLAP` and verify fences/semaphores and swapchain image acquisition logic.
|
||||||
|
|
||||||
21
docs/RUNTIME.md
Normal file
21
docs/RUNTIME.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
**Runtime Controls & Debug UI**
|
||||||
|
|
||||||
|
- Camera
|
||||||
|
- Move: `W/A/S/D`
|
||||||
|
- Look: hold Right Mouse Button
|
||||||
|
- Mouse wheel: adjust movement speed
|
||||||
|
- Ctrl + wheel: adjust FOV (30°..110°)
|
||||||
|
|
||||||
|
- Windows (ImGui)
|
||||||
|
- Background: choose compute background effect and `Render Scale`.
|
||||||
|
- Stats: frame/draw/update timings, triangle and draw counts.
|
||||||
|
- GPU/Resources: per‑frame allocations and render‑graph resource view.
|
||||||
|
- Pipelines: list graphics pipelines and hot‑reload changed shaders.
|
||||||
|
- Targets: swapchain/draw extent/format info.
|
||||||
|
- PostFX: tonemap operator (Reinhard/ACES) and exposure.
|
||||||
|
- Scene: counts of opaque/transparent draws.
|
||||||
|
|
||||||
|
- Shadow Modes
|
||||||
|
- Cascaded shadow mapping (CSM) is the default; cascades are created per‑frame via the render graph.
|
||||||
|
- If ray tracing extensions and device support are present, ray‑traced shadows can be enabled via `_context->shadowSettings.mode` (see `src/core/config.h` and usage in `LightingPass`).
|
||||||
|
|
||||||
42
docs/RayTracing.md
Normal file
42
docs/RayTracing.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
## Ray Tracing Manager: BLAS Cache and Per-Frame TLAS
|
||||||
|
|
||||||
|
Optional subsystem that enables hybrid or full ray traced shadows via Ray Query. It builds and caches BLAS per mesh and rebuilds a TLAS from the current `DrawContext` when enabled.
|
||||||
|
|
||||||
|
- Files: `src/core/vk_raytracing.h/.cpp`
|
||||||
|
|
||||||
|
### Device Feature & Extension Enablement
|
||||||
|
- Feature detection happens in `DeviceManager::init_vulkan()` and sets:
|
||||||
|
- `VK_KHR_acceleration_structure`, `VK_KHR_ray_query`, and `VK_KHR_deferred_host_operations` (if supported).
|
||||||
|
- Device features are appended via `DeviceBuilder.add_pNext(...)`.
|
||||||
|
- `DescriptorManager::gpuSceneDataLayout()` adds a TLAS binding at `(set=0, binding=1)` when AS is supported.
|
||||||
|
|
||||||
|
### BLAS Build & Cache
|
||||||
|
- `AccelStructureHandle getOrBuildBLAS(const std::shared_ptr<MeshAsset>& mesh)`:
|
||||||
|
- One GAS per `MeshAsset`, keyed by vertex buffer `VkBuffer`.
|
||||||
|
- Populated with one triangle geometry per `GeoSurface`.
|
||||||
|
- Built with `VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR` and device-local storage + scratch.
|
||||||
|
- Cached in `_blasByVB` for reuse across frames.
|
||||||
|
- Called from `AssetManager::createMesh(...)` and from GLTF loader after mesh upload.
|
||||||
|
|
||||||
|
### TLAS Rebuild Per Frame
|
||||||
|
- `VkAccelerationStructureKHR buildTLASFromDrawContext(const DrawContext& dc)`:
|
||||||
|
- Iterates `dc.OpaqueSurfaces` and creates one instance per render object.
|
||||||
|
- Looks up BLAS by `RenderObject::vertexBuffer`; if missing, instance is skipped.
|
||||||
|
- Uploads instances to a CPU→GPU buffer with device address.
|
||||||
|
- Builds TLAS with `immediate_submit` and stores device address for Ray Query.
|
||||||
|
|
||||||
|
### Renderer Integration
|
||||||
|
- In `VulkanEngine::draw()` before building passes:
|
||||||
|
- If RT mode is enabled (`shadowSettings.mode != 0`) and manager exists, TLAS is rebuilt from the latest draw context.
|
||||||
|
- Lighting pass binds the TLAS at `set=0,binding=1` when available.
|
||||||
|
|
||||||
|
### Modes & UI
|
||||||
|
- Mode 0: Shadow maps only (CSM).
|
||||||
|
- Mode 1: Hybrid — selected cascades assisted by Ray Query (configurable bitmask).
|
||||||
|
- Mode 2: Ray Query only (no shadow maps).
|
||||||
|
|
||||||
|
### Notes & Caveats
|
||||||
|
- BLAS cache key is the vertex buffer handle; if you rebuild meshes in-place, BLAS must be invalidated.
|
||||||
|
- CPU→GPU memory is used for the TLAS instance buffer to simplify updates. On some platforms, you may prefer staging + device-local.
|
||||||
|
- The RT path requires Vulkan 1.2+ with Ray Query and Acceleration Structure features available.
|
||||||
|
|
||||||
@@ -70,6 +70,8 @@ addPass(std::move(myPass));
|
|||||||
- Background (compute): Declares `ComputeWrite(drawImage)` and dispatches a selected effect instance.
|
- Background (compute): Declares `ComputeWrite(drawImage)` and dispatches a selected effect instance.
|
||||||
- Geometry (G-Buffer): Declares 3 color attachments and `DepthAttachment`, plus buffer reads for shared index/vertex buffers.
|
- Geometry (G-Buffer): Declares 3 color attachments and `DepthAttachment`, plus buffer reads for shared index/vertex buffers.
|
||||||
- Lighting (deferred): Reads G‑Buffer as sampled images and writes to `drawImage`.
|
- Lighting (deferred): Reads G‑Buffer as sampled images and writes to `drawImage`.
|
||||||
|
- Shadows: Cascaded shadow maps render to per-frame transient depth images (four cascades). If Ray Query is enabled,
|
||||||
|
the lighting pass additionally samples TLAS to evaluate shadow visibility according to the selected mode.
|
||||||
- Transparent (forward): Writes to `drawImage` with depth test against `depthImage` after lighting.
|
- Transparent (forward): Writes to `drawImage` with depth test against `depthImage` after lighting.
|
||||||
- ImGui: Inserted just before present to draw on the swapchain image.
|
- ImGui: Inserted just before present to draw on the swapchain image.
|
||||||
|
|
||||||
|
|||||||
@@ -47,3 +47,7 @@ Central allocator and uploader built on VMA. Provides creation helpers, an immed
|
|||||||
- For tooling and one‑off setup, use `immediate_submit(lambda)` to avoid per‑frame queuing.
|
- For tooling and one‑off setup, use `immediate_submit(lambda)` to avoid per‑frame queuing.
|
||||||
- When creating transient images/buffers used only inside a pass, prefer the Render Graph’s `create_*` so destruction is automatic at frame end.
|
- When creating transient images/buffers used only inside a pass, prefer the Render Graph’s `create_*` so destruction is automatic at frame end.
|
||||||
|
|
||||||
|
### Known Issue (transition after mip generation)
|
||||||
|
|
||||||
|
- In the Render Graph upload pass, the mipmap path should leave the image in `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` after `vkutil::generate_mipmaps(...)`. If you see an extra transition back to `TRANSFER_DST_OPTIMAL` after mip generation, remove it. The helper already transitions to the final sampled layout.
|
||||||
|
|
||||||
|
|||||||
25
docs/SHADERS.md
Normal file
25
docs/SHADERS.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
**Shaders & Hot Reload**
|
||||||
|
|
||||||
|
- Locations
|
||||||
|
- Sources live under `shaders/` and are compiled to `.spv` next to the sources.
|
||||||
|
|
||||||
|
- Build integration
|
||||||
|
- CMake invokes `glslangValidator -V` for `*.vert`, `*.frag`, `*.comp` targeting Vulkan 1.2.
|
||||||
|
- Windows PowerShell helper `compile_shaders.ps1` uses `glslc` targeting Vulkan 1.3 and supports additional stages:
|
||||||
|
- `.mesh` (`-fshader-stage=mesh`), `.task`, and ray tracing stages (`.rgen`, `.rmiss`, `.rchit`, `.rahit`, `.rint`, `.rcall`).
|
||||||
|
- Keep `glslangValidator`/`glslc` on `PATH` and ensure your Vulkan SDK is installed.
|
||||||
|
|
||||||
|
- Hot reload
|
||||||
|
- `PipelineManager::hotReloadChanged()` watches `.spv` modification times.
|
||||||
|
- Pipelines rebind the next frame when a shader file timestamp changes.
|
||||||
|
- ImGui → Pipelines window provides a manual “Reload Changed” button and lists currently registered pipelines.
|
||||||
|
|
||||||
|
- Conventions
|
||||||
|
- Name SPIR‑V files with full extension, e.g. `fullscreen.vert.spv`, `deferred_lighting.frag.spv`.
|
||||||
|
- Use `EngineContext::getAssets()->shaderPath("<name>.spv")` when registering pipelines.
|
||||||
|
- Use sRGB formats for albedo textures and UNORM for PBR control textures (see `docs/asset_manager.md`).
|
||||||
|
|
||||||
|
- Adding a pipeline (graphics)
|
||||||
|
- Fill `GraphicsPipelineCreateInfo` with shader paths, descriptor set layouts, optional push constants, and a `configure(PipelineBuilder&)` callback to set topology, raster, depth/blend, and attachment formats.
|
||||||
|
- Register with `PipelineManager::createGraphicsPipeline(name, info)`. Retrieve via `getGraphics` or `getMaterialPipeline`.
|
||||||
|
|
||||||
26
docs/TROUBLESHOOTING.md
Normal file
26
docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
**Troubleshooting**
|
||||||
|
|
||||||
|
- Shader compiler not found
|
||||||
|
- Ensure `glslangValidator` (and/or `glslc` on Windows) is on `PATH`.
|
||||||
|
- Re‑open your terminal after installing the Vulkan SDK.
|
||||||
|
|
||||||
|
- Windows SDK version mismatch
|
||||||
|
- `CMakeLists.txt` references `C:/VulkanSDK/1.3.296.0` by default.
|
||||||
|
- Update the path or set `VULKAN_SDK` to your installed version.
|
||||||
|
|
||||||
|
- Validation errors on startup
|
||||||
|
- Update GPU drivers and Vulkan SDK.
|
||||||
|
- Try running a Release build to confirm if the issue is validation‑only.
|
||||||
|
|
||||||
|
- Black screen or out‑of‑date swapchain
|
||||||
|
- Resize the window once to force a swapchain rebuild.
|
||||||
|
- Check the ImGui “Targets” window for swapchain/draw formats and extent.
|
||||||
|
|
||||||
|
- No models rendering
|
||||||
|
- Verify `assets/police_office.glb` exists (see `docs/ASSETS.md`).
|
||||||
|
- Open the “Scene” window to confirm draw counts > 0.
|
||||||
|
|
||||||
|
- Shader changes not visible
|
||||||
|
- Confirm the `.spv` file changed (timestamp) and click “Reload Changed” in the Pipelines window.
|
||||||
|
- Ensure you are editing the correct files referenced by `shaderPath()`.
|
||||||
|
|
||||||
@@ -120,6 +120,9 @@ private:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Small compute manager for one-off pipelines and persistent instances.
|
||||||
|
// It owns a dedicated descriptor allocator and provides helpers to build
|
||||||
|
// pipelines, set bindings, and dispatch work (immediate or on a provided cmd).
|
||||||
class ComputeManager
|
class ComputeManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -309,6 +309,11 @@ bool AssetManager::removeMesh(const std::string &name)
|
|||||||
{
|
{
|
||||||
auto it = _meshCache.find(name);
|
auto it = _meshCache.find(name);
|
||||||
if (it == _meshCache.end()) return false;
|
if (it == _meshCache.end()) return false;
|
||||||
|
if (_engine && _engine->_rayManager)
|
||||||
|
{
|
||||||
|
// Clean up BLAS cached for this mesh (if ray tracing is enabled)
|
||||||
|
_engine->_rayManager->removeBLASForBuffer(it->second->meshBuffers.vertexBuffer.buffer);
|
||||||
|
}
|
||||||
if (_engine && _engine->_resourceManager)
|
if (_engine && _engine->_resourceManager)
|
||||||
{
|
{
|
||||||
_engine->_resourceManager->destroy_buffer(it->second->meshBuffers.indexBuffer);
|
_engine->_resourceManager->destroy_buffer(it->second->meshBuffers.indexBuffer);
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
class DeviceManager;
|
class DeviceManager;
|
||||||
|
|
||||||
|
// Per-frame state used by the renderer and passes.
|
||||||
|
// Owns a command buffer, sync primitives, a transient descriptor pool, and a
|
||||||
|
// deletion queue for resources that should be destroyed when the frame is done.
|
||||||
struct FrameResources
|
struct FrameResources
|
||||||
{
|
{
|
||||||
VkSemaphore _swapchainSemaphore = VK_NULL_HANDLE;
|
VkSemaphore _swapchainSemaphore = VK_NULL_HANDLE;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include "SDL2/SDL.h"
|
#include "SDL2/SDL.h"
|
||||||
#include "SDL2/SDL_vulkan.h"
|
#include "SDL2/SDL_vulkan.h"
|
||||||
|
|
||||||
|
// Create Vulkan instance/device, enable debug/validation (in Debug), pick a GPU,
|
||||||
|
// and set up VMA with buffer device address. If available, enable Ray Query and
|
||||||
|
// Acceleration Structure extensions + features.
|
||||||
void DeviceManager::init_vulkan(SDL_Window *window)
|
void DeviceManager::init_vulkan(SDL_Window *window)
|
||||||
{
|
{
|
||||||
vkb::InstanceBuilder builder;
|
vkb::InstanceBuilder builder;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Engine bootstrap, frame loop, and render-graph wiring.
|
||||||
|
//
|
||||||
|
// Responsibilities
|
||||||
|
// - Initialize SDL + Vulkan managers (device, resources, descriptors, samplers, pipelines).
|
||||||
|
// - Create swapchain + default images and build the Render Graph each frame.
|
||||||
|
// - Publish an EngineContext so passes and subsystems access per‑frame state uniformly.
|
||||||
|
// - Drive ImGui + debug UIs and optional ray‑tracing TLAS rebuilds.
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - docs/EngineContext.md
|
||||||
|
// - docs/RenderGraph.md
|
||||||
|
// - docs/FrameResources.md
|
||||||
|
// - docs/RayTracing.md
|
||||||
|
//
|
||||||
//> includes
|
//> includes
|
||||||
#include "vk_engine.h"
|
#include "vk_engine.h"
|
||||||
#include <core/vk_images.h>
|
#include <core/vk_images.h>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include "render/rg_graph.h"
|
#include "render/rg_graph.h"
|
||||||
#include "core/vk_raytracing.h"
|
#include "core/vk_raytracing.h"
|
||||||
|
|
||||||
|
// Number of frames-in-flight. Affects per-frame command buffers, fences,
|
||||||
|
// semaphores, and transient descriptor pools in FrameResources.
|
||||||
constexpr unsigned int FRAME_OVERLAP = 2;
|
constexpr unsigned int FRAME_OVERLAP = 2;
|
||||||
|
|
||||||
// Compute push constants and effects are declared in compute/vk_compute.h now.
|
// Compute push constants and effects are declared in compute/vk_compute.h now.
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ struct GraphicsPipelineCreateInfo
|
|||||||
std::function<void(PipelineBuilder &)> configure;
|
std::function<void(PipelineBuilder &)> configure;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Graphics pipeline registry with hot-reload support.
|
||||||
|
// Stores specs keyed by name, builds on demand, and can rebuild when shader
|
||||||
|
// timestamps change. Also forwards a minimal Compute API to ComputeManager.
|
||||||
class PipelineManager
|
class PipelineManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -307,3 +307,21 @@ VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const Dra
|
|||||||
|
|
||||||
return _tlas.handle;
|
return _tlas.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RayTracingManager::removeBLASForBuffer(VkBuffer vertexBuffer)
|
||||||
|
{
|
||||||
|
if (!vertexBuffer) return;
|
||||||
|
VkDevice dv = _device->device();
|
||||||
|
auto it = _blasByVB.find(vertexBuffer);
|
||||||
|
if (it == _blasByVB.end()) return;
|
||||||
|
|
||||||
|
if (it->second.handle)
|
||||||
|
{
|
||||||
|
_vkDestroyAccelerationStructureKHR(dv, it->second.handle, nullptr);
|
||||||
|
}
|
||||||
|
if (it->second.storage.buffer)
|
||||||
|
{
|
||||||
|
_resources->destroy_buffer(it->second.storage);
|
||||||
|
}
|
||||||
|
_blasByVB.erase(it);
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,18 +15,24 @@
|
|||||||
VkDeviceAddress deviceAddress{0};
|
VkDeviceAddress deviceAddress{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class RayTracingManager {
|
// Ray tracing helper that caches BLAS per mesh and rebuilds TLAS per frame
|
||||||
public:
|
// for hybrid/full ray query shadows. See docs/RayTracing.md.
|
||||||
void init(DeviceManager* dev, ResourceManager* res);
|
class RayTracingManager {
|
||||||
void cleanup();
|
public:
|
||||||
|
void init(DeviceManager* dev, ResourceManager* res);
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
// Build (or get) BLAS for a mesh. Safe to call multiple times.
|
// Build (or get) BLAS for a mesh. Safe to call multiple times.
|
||||||
AccelStructureHandle getOrBuildBLAS(const std::shared_ptr<MeshAsset>& mesh);
|
AccelStructureHandle getOrBuildBLAS(const std::shared_ptr<MeshAsset>& mesh);
|
||||||
|
|
||||||
// Rebuild TLAS from current draw context; returns TLAS handle (or null if unavailable)
|
// Rebuild TLAS from current draw context; returns TLAS handle (or null if unavailable)
|
||||||
VkAccelerationStructureKHR buildTLASFromDrawContext(const DrawContext& dc);
|
VkAccelerationStructureKHR buildTLASFromDrawContext(const DrawContext& dc);
|
||||||
VkAccelerationStructureKHR tlas() const { return _tlas.handle; }
|
VkAccelerationStructureKHR tlas() const { return _tlas.handle; }
|
||||||
VkDeviceAddress tlasAddress() const { return _tlas.deviceAddress; }
|
VkDeviceAddress tlasAddress() const { return _tlas.deviceAddress; }
|
||||||
|
|
||||||
|
// Remove and destroy a cached BLAS associated with a vertex buffer.
|
||||||
|
// Safe to call even if no BLAS exists for the buffer.
|
||||||
|
void removeBLASForBuffer(VkBuffer vertexBuffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// function pointers (resolved on init)
|
// function pointers (resolved on init)
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ AllocatedBuffer ResourceManager::create_buffer(size_t allocSize, VkBufferUsageFl
|
|||||||
|
|
||||||
VmaAllocationCreateInfo vmaallocInfo = {};
|
VmaAllocationCreateInfo vmaallocInfo = {};
|
||||||
vmaallocInfo.usage = memoryUsage;
|
vmaallocInfo.usage = memoryUsage;
|
||||||
vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
// Map buffers only when CPU-visible memory is requested
|
||||||
|
if (memoryUsage == VMA_MEMORY_USAGE_CPU_TO_GPU ||
|
||||||
|
memoryUsage == VMA_MEMORY_USAGE_CPU_ONLY)
|
||||||
|
{
|
||||||
|
vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
AllocatedBuffer newBuffer{};
|
AllocatedBuffer newBuffer{};
|
||||||
VK_CHECK(vmaCreateBuffer(_deviceManager->allocator(), &bufferInfo, &vmaallocInfo,
|
VK_CHECK(vmaCreateBuffer(_deviceManager->allocator(), &bufferInfo, &vmaallocInfo,
|
||||||
@@ -129,11 +134,33 @@ AllocatedImage ResourceManager::create_image(VkExtent3D size, VkFormat format, V
|
|||||||
return newImage;
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns byte size per texel for a subset of common formats.
|
||||||
|
static inline size_t bytes_per_texel(VkFormat fmt)
|
||||||
|
{
|
||||||
|
switch (fmt)
|
||||||
|
{
|
||||||
|
case VK_FORMAT_R8_UNORM:
|
||||||
|
case VK_FORMAT_R8_SRGB:
|
||||||
|
return 1;
|
||||||
|
case VK_FORMAT_R8G8_UNORM:
|
||||||
|
case VK_FORMAT_R8G8_SRGB:
|
||||||
|
return 2;
|
||||||
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||||
|
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||||
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||||
|
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return 4; // STB path uploads 4 channels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AllocatedImage ResourceManager::create_image(const void *data, VkExtent3D size, VkFormat format,
|
AllocatedImage ResourceManager::create_image(const void *data, VkExtent3D size, VkFormat format,
|
||||||
VkImageUsageFlags usage,
|
VkImageUsageFlags usage,
|
||||||
bool mipmapped)
|
bool mipmapped)
|
||||||
{
|
{
|
||||||
size_t data_size = size.depth * size.width * size.height * 4;
|
size_t bpp = bytes_per_texel(format);
|
||||||
|
size_t data_size = static_cast<size_t>(size.depth) * size.width * size.height * bpp;
|
||||||
AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||||
|
|
||||||
@@ -480,8 +507,10 @@ void ResourceManager::register_upload_pass(RenderGraph &graph, FrameResources &f
|
|||||||
|
|
||||||
if (upload.generateMips)
|
if (upload.generateMips)
|
||||||
{
|
{
|
||||||
|
// NOTE: generate_mipmaps() transitions the image to
|
||||||
|
// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL at the end.
|
||||||
|
// Do not transition back to TRANSFER here. See docs/ResourceManager.md.
|
||||||
vkutil::generate_mipmaps(cmd, image, VkExtent2D{upload.extent.width, upload.extent.height});
|
vkutil::generate_mipmaps(cmd, image, VkExtent2D{upload.extent.width, upload.extent.height});
|
||||||
vkutil::transition_image(cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ class DeviceManager;
|
|||||||
class RenderGraph;
|
class RenderGraph;
|
||||||
struct FrameResources;
|
struct FrameResources;
|
||||||
|
|
||||||
|
// VMA-backed allocator + upload helper.
|
||||||
|
// Creates buffers/images, offers an immediate-submit path, and supports
|
||||||
|
// deferring uploads into a single Render Graph transfer pass per frame.
|
||||||
class ResourceManager
|
class ResourceManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
#include "vk_swapchain.h"
|
#include "vk_swapchain.h"
|
||||||
|
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
#include "SDL2/SDL_vulkan.h"
|
||||||
|
|
||||||
#include "vk_device.h"
|
#include "vk_device.h"
|
||||||
#include "vk_initializers.h"
|
#include "vk_initializers.h"
|
||||||
#include "vk_resource.h"
|
#include "vk_resource.h"
|
||||||
|
|
||||||
|
// Swapchain + per-frame targets (HDR draw, depth, GBuffer) management.
|
||||||
|
//
|
||||||
|
// Create/resize/destroy logic keeps per-frame images in a local deletion queue
|
||||||
|
// so they are cleaned up with the swapchain. The engine imports those images
|
||||||
|
// into the Render Graph each frame.
|
||||||
void SwapchainManager::init_swapchain()
|
void SwapchainManager::init_swapchain()
|
||||||
{
|
{
|
||||||
create_swapchain(_windowExtent.width, _windowExtent.height);
|
create_swapchain(_windowExtent.width, _windowExtent.height);
|
||||||
@@ -115,12 +121,13 @@ void SwapchainManager::create_swapchain(uint32_t width, uint32_t height)
|
|||||||
|
|
||||||
void SwapchainManager::destroy_swapchain() const
|
void SwapchainManager::destroy_swapchain() const
|
||||||
{
|
{
|
||||||
vkDestroySwapchainKHR(_deviceManager->device(), _swapchain, nullptr);
|
// Destroy image views before the swapchain for stricter driver orderliness.
|
||||||
|
// (Most drivers tolerate either order, but views reference swapchain images.)
|
||||||
for (auto _swapchainImageView: _swapchainImageViews)
|
for (auto view : _swapchainImageViews)
|
||||||
{
|
{
|
||||||
vkDestroyImageView(_deviceManager->device(), _swapchainImageView, nullptr);
|
vkDestroyImageView(_deviceManager->device(), view, nullptr);
|
||||||
}
|
}
|
||||||
|
vkDestroySwapchainKHR(_deviceManager->device(), _swapchain, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
||||||
@@ -133,7 +140,8 @@ void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
|||||||
_deletionQueue.flush();
|
_deletionQueue.flush();
|
||||||
|
|
||||||
int w, h;
|
int w, h;
|
||||||
SDL_GetWindowSize(window, &w, &h);
|
// HiDPI-aware drawable size for correct pixel dimensions
|
||||||
|
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
||||||
_windowExtent.width = w;
|
_windowExtent.width = w;
|
||||||
_windowExtent.height = h;
|
_windowExtent.height = h;
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,15 @@ RGBufferHandle RenderGraph::create_buffer(const RGBufferDesc &desc)
|
|||||||
return _resources.add_transient(desc);
|
return _resources.add_transient(desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render Graph: builds a per-frame DAG from declared image/buffer accesses,
|
||||||
|
// inserts precise barriers and layouts, and records passes using dynamic rendering.
|
||||||
|
//
|
||||||
|
// Key steps:
|
||||||
|
// - add_pass(): store declarations and callbacks (build to declare, record to issue commands)
|
||||||
|
// - compile(): topologically sort by read/write hazards and generate Vk*Barrier2 sequences
|
||||||
|
// - execute(): emit pre-pass barriers, begin dynamic rendering if attachments exist, invoke record()
|
||||||
|
//
|
||||||
|
// See docs/RenderGraph.md for API overview and pass patterns.
|
||||||
void RenderGraph::add_pass(const char *name, RGPassType type, BuildCallback build, RecordCallback record)
|
void RenderGraph::add_pass(const char *name, RGPassType type, BuildCallback build, RecordCallback record)
|
||||||
{
|
{
|
||||||
Pass p{};
|
Pass p{};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include "vk_swapchain.h"
|
#include "vk_swapchain.h"
|
||||||
#include "render/rg_graph.h"
|
#include "render/rg_graph.h"
|
||||||
|
|
||||||
|
// Basic conservative frustum test against RenderObject AABB.
|
||||||
|
// Clip space uses Vulkan Z0 (0..w). Returns true if any part of the box is inside.
|
||||||
bool is_visible(const RenderObject &obj, const glm::mat4 &viewproj)
|
bool is_visible(const RenderObject &obj, const glm::mat4 &viewproj)
|
||||||
{
|
{
|
||||||
const std::array<glm::vec3, 8> corners{
|
const std::array<glm::vec3, 8> corners{
|
||||||
|
|||||||
@@ -185,7 +185,9 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd,
|
|||||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 1, 1,
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 1, 1,
|
||||||
&_gBufferInputDescriptorSet, 0, nullptr);
|
&_gBufferInputDescriptorSet, 0, nullptr);
|
||||||
|
|
||||||
// Allocate and write shadow descriptor set for this frame (set = 2)
|
// Allocate and write shadow descriptor set for this frame (set = 2).
|
||||||
|
// When RT is enabled, TLAS is bound in the global set at (set=0, binding=1)
|
||||||
|
// via DescriptorManager::gpuSceneDataLayout(). See docs/RayTracing.md.
|
||||||
VkDescriptorSet shadowSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
|
VkDescriptorSet shadowSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
|
||||||
deviceManager->device(), _shadowDescriptorLayout);
|
deviceManager->device(), _shadowDescriptorLayout);
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ void TransparentPass::draw_transparent(VkCommandBuffer cmd,
|
|||||||
writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
writer.update_set(deviceManager->device(), globalDescriptor);
|
writer.update_set(deviceManager->device(), globalDescriptor);
|
||||||
|
|
||||||
// Sort transparent back-to-front using camera-space depth
|
// Sort transparent back-to-front using camera-space depth.
|
||||||
|
// We approximate object depth by transforming the mesh bounds origin.
|
||||||
|
// For better results consider using per-object center or per-draw depth range.
|
||||||
std::vector<const RenderObject *> draws;
|
std::vector<const RenderObject *> draws;
|
||||||
draws.reserve(dc.TransparentSurfaces.size());
|
draws.reserve(dc.TransparentSurfaces.size());
|
||||||
for (const auto &r: dc.TransparentSurfaces) draws.push_back(&r);
|
for (const auto &r: dc.TransparentSurfaces) draws.push_back(&r);
|
||||||
|
|||||||
@@ -576,6 +576,10 @@ void LoadedGLTF::clearAll()
|
|||||||
|
|
||||||
for (auto &[k, v]: meshes)
|
for (auto &[k, v]: meshes)
|
||||||
{
|
{
|
||||||
|
if (creator->_rayManager)
|
||||||
|
{
|
||||||
|
creator->_rayManager->removeBLASForBuffer(v->meshBuffers.vertexBuffer.buffer);
|
||||||
|
}
|
||||||
creator->_resourceManager->destroy_buffer(v->meshBuffers.indexBuffer);
|
creator->_resourceManager->destroy_buffer(v->meshBuffers.indexBuffer);
|
||||||
creator->_resourceManager->destroy_buffer(v->meshBuffers.vertexBuffer);
|
creator->_resourceManager->destroy_buffer(v->meshBuffers.vertexBuffer);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user