From 0226c0b5b34cbfc00f461704c00ad404ca0909cf Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Wed, 29 Oct 2025 22:51:28 +0900 Subject: [PATCH] EDIT: Docs and minor bug fixed --- docs/ASSETS.md | 23 +++++++++++++ docs/BUILD.md | 38 +++++++++++++++++++++ docs/FrameResources.md | 34 +++++++++++++++++++ docs/RUNTIME.md | 21 ++++++++++++ docs/RayTracing.md | 42 ++++++++++++++++++++++++ docs/RenderPasses.md | 2 ++ docs/ResourceManager.md | 4 +++ docs/SHADERS.md | 25 ++++++++++++++ docs/TROUBLESHOOTING.md | 26 +++++++++++++++ src/compute/vk_compute.h | 3 ++ src/core/asset_manager.cpp | 5 +++ src/core/frame_resources.h | 3 ++ src/core/vk_device.cpp | 3 ++ src/core/vk_engine.cpp | 14 ++++++++ src/core/vk_engine.h | 2 ++ src/core/vk_pipeline_manager.h | 3 ++ src/core/vk_raytracing.cpp | 18 ++++++++++ src/core/vk_raytracing.h | 20 +++++++---- src/core/vk_resource.cpp | 35 ++++++++++++++++++-- src/core/vk_resource.h | 3 ++ src/core/vk_swapchain.cpp | 18 +++++++--- src/render/rg_graph.cpp | 9 +++++ src/render/vk_renderpass_geometry.cpp | 2 ++ src/render/vk_renderpass_lighting.cpp | 4 ++- src/render/vk_renderpass_transparent.cpp | 4 ++- src/scene/vk_loader.cpp | 4 +++ 26 files changed, 348 insertions(+), 17 deletions(-) create mode 100644 docs/ASSETS.md create mode 100644 docs/BUILD.md create mode 100644 docs/FrameResources.md create mode 100644 docs/RUNTIME.md create mode 100644 docs/RayTracing.md create mode 100644 docs/SHADERS.md create mode 100644 docs/TROUBLESHOOTING.md diff --git a/docs/ASSETS.md b/docs/ASSETS.md new file mode 100644 index 0000000..2021352 --- /dev/null +++ b/docs/ASSETS.md @@ -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. + diff --git a/docs/BUILD.md b/docs/BUILD.md new file mode 100644 index 0000000..2e68115 --- /dev/null +++ b/docs/BUILD.md @@ -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. + diff --git a/docs/FrameResources.md b/docs/FrameResources.md new file mode 100644 index 0000000..ea98155 --- /dev/null +++ b/docs/FrameResources.md @@ -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. + diff --git a/docs/RUNTIME.md b/docs/RUNTIME.md new file mode 100644 index 0000000..2b7015f --- /dev/null +++ b/docs/RUNTIME.md @@ -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`). + diff --git a/docs/RayTracing.md b/docs/RayTracing.md new file mode 100644 index 0000000..f55d87c --- /dev/null +++ b/docs/RayTracing.md @@ -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& 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. + diff --git a/docs/RenderPasses.md b/docs/RenderPasses.md index 815efb0..c3fa13a 100644 --- a/docs/RenderPasses.md +++ b/docs/RenderPasses.md @@ -70,6 +70,8 @@ addPass(std::move(myPass)); - 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. - 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. - ImGui: Inserted just before present to draw on the swapchain image. diff --git a/docs/ResourceManager.md b/docs/ResourceManager.md index fef02ec..0c43b95 100644 --- a/docs/ResourceManager.md +++ b/docs/ResourceManager.md @@ -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. - 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. + diff --git a/docs/SHADERS.md b/docs/SHADERS.md new file mode 100644 index 0000000..81629ca --- /dev/null +++ b/docs/SHADERS.md @@ -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(".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`. + diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 0000000..8ce7f89 --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -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()`. + diff --git a/src/compute/vk_compute.h b/src/compute/vk_compute.h index 7af1aac..92afa39 100644 --- a/src/compute/vk_compute.h +++ b/src/compute/vk_compute.h @@ -120,6 +120,9 @@ private: 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 { public: diff --git a/src/core/asset_manager.cpp b/src/core/asset_manager.cpp index f5078e8..428a973 100644 --- a/src/core/asset_manager.cpp +++ b/src/core/asset_manager.cpp @@ -309,6 +309,11 @@ bool AssetManager::removeMesh(const std::string &name) { auto it = _meshCache.find(name); 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) { _engine->_resourceManager->destroy_buffer(it->second->meshBuffers.indexBuffer); diff --git a/src/core/frame_resources.h b/src/core/frame_resources.h index a635e53..d2f3fc3 100644 --- a/src/core/frame_resources.h +++ b/src/core/frame_resources.h @@ -5,6 +5,9 @@ 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 { VkSemaphore _swapchainSemaphore = VK_NULL_HANDLE; diff --git a/src/core/vk_device.cpp b/src/core/vk_device.cpp index d218d96..3ba888d 100644 --- a/src/core/vk_device.cpp +++ b/src/core/vk_device.cpp @@ -3,6 +3,9 @@ #include "SDL2/SDL.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) { vkb::InstanceBuilder builder; diff --git a/src/core/vk_engine.cpp b/src/core/vk_engine.cpp index d23f1dd..852027e 100644 --- a/src/core/vk_engine.cpp +++ b/src/core/vk_engine.cpp @@ -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 #include "vk_engine.h" #include diff --git a/src/core/vk_engine.h b/src/core/vk_engine.h index 3733617..b845c38 100644 --- a/src/core/vk_engine.h +++ b/src/core/vk_engine.h @@ -32,6 +32,8 @@ #include "render/rg_graph.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; // Compute push constants and effects are declared in compute/vk_compute.h now. diff --git a/src/core/vk_pipeline_manager.h b/src/core/vk_pipeline_manager.h index 41a9cdb..cca2dcf 100644 --- a/src/core/vk_pipeline_manager.h +++ b/src/core/vk_pipeline_manager.h @@ -25,6 +25,9 @@ struct GraphicsPipelineCreateInfo std::function 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 { public: diff --git a/src/core/vk_raytracing.cpp b/src/core/vk_raytracing.cpp index a35fab4..0d3e361 100644 --- a/src/core/vk_raytracing.cpp +++ b/src/core/vk_raytracing.cpp @@ -307,3 +307,21 @@ VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const Dra 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); +} diff --git a/src/core/vk_raytracing.h b/src/core/vk_raytracing.h index 2a3976e..53aec49 100644 --- a/src/core/vk_raytracing.h +++ b/src/core/vk_raytracing.h @@ -15,18 +15,24 @@ VkDeviceAddress deviceAddress{0}; }; - class RayTracingManager { - public: - void init(DeviceManager* dev, ResourceManager* res); - void cleanup(); +// Ray tracing helper that caches BLAS per mesh and rebuilds TLAS per frame +// for hybrid/full ray query shadows. See docs/RayTracing.md. +class RayTracingManager { +public: + void init(DeviceManager* dev, ResourceManager* res); + void cleanup(); // Build (or get) BLAS for a mesh. Safe to call multiple times. AccelStructureHandle getOrBuildBLAS(const std::shared_ptr& mesh); // Rebuild TLAS from current draw context; returns TLAS handle (or null if unavailable) - VkAccelerationStructureKHR buildTLASFromDrawContext(const DrawContext& dc); - VkAccelerationStructureKHR tlas() const { return _tlas.handle; } - VkDeviceAddress tlasAddress() const { return _tlas.deviceAddress; } + VkAccelerationStructureKHR buildTLASFromDrawContext(const DrawContext& dc); + VkAccelerationStructureKHR tlas() const { return _tlas.handle; } + 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: // function pointers (resolved on init) diff --git a/src/core/vk_resource.cpp b/src/core/vk_resource.cpp index b5d84da..94f487b 100644 --- a/src/core/vk_resource.cpp +++ b/src/core/vk_resource.cpp @@ -48,7 +48,12 @@ AllocatedBuffer ResourceManager::create_buffer(size_t allocSize, VkBufferUsageFl VmaAllocationCreateInfo vmaallocInfo = {}; 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{}; VK_CHECK(vmaCreateBuffer(_deviceManager->allocator(), &bufferInfo, &vmaallocInfo, @@ -129,11 +134,33 @@ AllocatedImage ResourceManager::create_image(VkExtent3D size, VkFormat format, V 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, VkImageUsageFlags usage, 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.depth) * size.width * size.height * bpp; AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); @@ -480,8 +507,10 @@ void ResourceManager::register_upload_pass(RenderGraph &graph, FrameResources &f 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::transition_image(cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } } }); diff --git a/src/core/vk_resource.h b/src/core/vk_resource.h index 1b93038..feb09b3 100644 --- a/src/core/vk_resource.h +++ b/src/core/vk_resource.h @@ -7,6 +7,9 @@ class DeviceManager; class RenderGraph; 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 { public: diff --git a/src/core/vk_swapchain.cpp b/src/core/vk_swapchain.cpp index b63318b..8bf2ed7 100644 --- a/src/core/vk_swapchain.cpp +++ b/src/core/vk_swapchain.cpp @@ -1,11 +1,17 @@ #include "vk_swapchain.h" #include +#include "SDL2/SDL_vulkan.h" #include "vk_device.h" #include "vk_initializers.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() { 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 { - vkDestroySwapchainKHR(_deviceManager->device(), _swapchain, nullptr); - - for (auto _swapchainImageView: _swapchainImageViews) + // Destroy image views before the swapchain for stricter driver orderliness. + // (Most drivers tolerate either order, but views reference swapchain images.) + 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) @@ -133,7 +140,8 @@ void SwapchainManager::resize_swapchain(struct SDL_Window *window) _deletionQueue.flush(); 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.height = h; diff --git a/src/render/rg_graph.cpp b/src/render/rg_graph.cpp index 061d3d9..88afca2 100644 --- a/src/render/rg_graph.cpp +++ b/src/render/rg_graph.cpp @@ -59,6 +59,15 @@ RGBufferHandle RenderGraph::create_buffer(const RGBufferDesc &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) { Pass p{}; diff --git a/src/render/vk_renderpass_geometry.cpp b/src/render/vk_renderpass_geometry.cpp index b2476a6..10b2247 100644 --- a/src/render/vk_renderpass_geometry.cpp +++ b/src/render/vk_renderpass_geometry.cpp @@ -15,6 +15,8 @@ #include "vk_swapchain.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) { const std::array corners{ diff --git a/src/render/vk_renderpass_lighting.cpp b/src/render/vk_renderpass_lighting.cpp index 2e61bad..2ce7b62 100644 --- a/src/render/vk_renderpass_lighting.cpp +++ b/src/render/vk_renderpass_lighting.cpp @@ -185,7 +185,9 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd, vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 1, 1, &_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( deviceManager->device(), _shadowDescriptorLayout); { diff --git a/src/render/vk_renderpass_transparent.cpp b/src/render/vk_renderpass_transparent.cpp index 3cf7010..94c586a 100644 --- a/src/render/vk_renderpass_transparent.cpp +++ b/src/render/vk_renderpass_transparent.cpp @@ -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.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 draws; draws.reserve(dc.TransparentSurfaces.size()); for (const auto &r: dc.TransparentSurfaces) draws.push_back(&r); diff --git a/src/scene/vk_loader.cpp b/src/scene/vk_loader.cpp index 31e873f..713f6e1 100644 --- a/src/scene/vk_loader.cpp +++ b/src/scene/vk_loader.cpp @@ -576,6 +576,10 @@ void LoadedGLTF::clearAll() 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.vertexBuffer); }