EDIT: Docs and minor bug fixed

This commit is contained in:
2025-10-29 22:51:28 +09:00
parent 97177dade3
commit 0226c0b5b3
26 changed files with 348 additions and 17 deletions

23
docs/ASSETS.md Normal file
View File

@@ -0,0 +1,23 @@
**Assets & Paths**
- Default locations
- `assets/` for models/textures and `shaders/` for GLSL live at the repo root.
- The engine autodetects 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
View 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 SPIRV 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.
- Thirdparty 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
View 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/Dont
- Do use `currentFrame->_frameDescriptors` for descriptor sets that live only for this frame.
- Do push transient resource destruction into `currentFrame->_deletionQueue`.
- Dont 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
View 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: perframe allocations and rendergraph resource view.
- Pipelines: list graphics pipelines and hotreload 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 perframe via the render graph.
- If ray tracing extensions and device support are present, raytraced shadows can be enabled via `_context->shadowSettings.mode` (see `src/core/config.h` and usage in `LightingPass`).

42
docs/RayTracing.md Normal file
View 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.

View File

@@ -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 GBuffer as sampled images and writes to `drawImage`. - Lighting (deferred): Reads GBuffer 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.

View File

@@ -47,3 +47,7 @@ Central allocator and uploader built on VMA. Provides creation helpers, an immed
- For tooling and oneoff setup, use `immediate_submit(lambda)` to avoid perframe queuing. - For tooling and oneoff setup, use `immediate_submit(lambda)` to avoid perframe queuing.
- When creating transient images/buffers used only inside a pass, prefer the Render Graphs `create_*` so destruction is automatic at frame end. - When creating transient images/buffers used only inside a pass, prefer the Render Graphs `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
View 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 SPIRV 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
View File

@@ -0,0 +1,26 @@
**Troubleshooting**
- Shader compiler not found
- Ensure `glslangValidator` (and/or `glslc` on Windows) is on `PATH`.
- Reopen 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 validationonly.
- Black screen or outofdate 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()`.

View File

@@ -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:

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 perframe state uniformly.
// - Drive ImGui + debug UIs and optional raytracing 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>

View File

@@ -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.

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -15,6 +15,8 @@
VkDeviceAddress deviceAddress{0}; VkDeviceAddress deviceAddress{0};
}; };
// 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 { class RayTracingManager {
public: public:
void init(DeviceManager* dev, ResourceManager* res); void init(DeviceManager* dev, ResourceManager* res);
@@ -28,6 +30,10 @@
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)
PFN_vkCreateAccelerationStructureKHR _vkCreateAccelerationStructureKHR{}; PFN_vkCreateAccelerationStructureKHR _vkCreateAccelerationStructureKHR{};

View File

@@ -48,7 +48,12 @@ AllocatedBuffer ResourceManager::create_buffer(size_t allocSize, VkBufferUsageFl
VmaAllocationCreateInfo vmaallocInfo = {}; VmaAllocationCreateInfo vmaallocInfo = {};
vmaallocInfo.usage = memoryUsage; vmaallocInfo.usage = memoryUsage;
// 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; 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);
} }
} }
}); });

View File

@@ -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:

View File

@@ -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;

View File

@@ -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{};

View File

@@ -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{

View File

@@ -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);
{ {

View File

@@ -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);

View File

@@ -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);
} }