EDIT: Docs and minor bug fixed
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <core/vk_images.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.
|
||||
|
||||
@@ -25,6 +25,9 @@ struct GraphicsPipelineCreateInfo
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<MeshAsset>& 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)
|
||||
|
||||
@@ -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_t>(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#include "vk_swapchain.h"
|
||||
|
||||
#include <SDL_video.h>
|
||||
#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;
|
||||
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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<glm::vec3, 8> corners{
|
||||
|
||||
@@ -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);
|
||||
{
|
||||
|
||||
@@ -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<const RenderObject *> draws;
|
||||
draws.reserve(dc.TransparentSurfaces.size());
|
||||
for (const auto &r: dc.TransparentSurfaces) draws.push_back(&r);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user