SleakEngine 0.1.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
VulkanRenderer.hpp
Go to the documentation of this file.
1#ifndef VULKANRENDERER_HPP
2#define VULKANRENDERER_HPP
3
8#include "Core/Logger.hpp"
9#include <vulkan/vulkan.h>
11#include <Runtime/Material.hpp>
12#include <cstdint>
13#include <vector>
14#include <set>
15#include <array>
16#include <unordered_map>
17#include <imgui.h>
18#include <backends/imgui_impl_vulkan.h>
19
20namespace Sleak {
21class ENGINE_API Window;
22 namespace RenderEngine {
23
25 uint32_t GraphicsIndex = UINT32_MAX;
26 uint32_t ComputeIndex = UINT32_MAX;
27 uint32_t TransferIndex = UINT32_MAX;
28 uint32_t PresentIndex = UINT32_MAX;
29
30 const float GraphicsPriority = 0.9f;
31 const float ComputePriority = 0.8f;
32 const float TransferPriority = 0.7f;
33 const float PresentPriority = 1.0f;
34
35 bool isComplete() {
36 return GraphicsIndex != UINT32_MAX && PresentIndex != UINT32_MAX;
37 }
38};
39
41 VkSurfaceCapabilitiesKHR caps;
42 std::vector<VkSurfaceFormatKHR> formats;
43 std::vector<VkPresentModeKHR> presentModes;
44};
45
46/// Vulkan backend for Renderer/RenderContext: owns the device, swapchain,
47/// and per-frame command recording. Implementation spans this file plus
48/// the Vulkan* subsystem TUs (Device, Swapchain, Pipelines, Shadow, etc.).
49class ENGINE_API VulkanRenderer : public Renderer, public RenderContext {
50public:
51 /// Constructs the renderer, sets the clear color, and registers
52 /// ResourceManager factory callbacks.
53 VulkanRenderer(Window* window);
54 /// Calls Cleanup() to tear down all Vulkan resources.
56
57 /// Runs the full Vulkan bring-up sequence: instance, device, swapchain,
58 /// pipelines, and sync objects.
59 virtual bool Initialize() override;
60 /// Prepares the command buffer and begins the shadow/GBuffer/forward
61 /// render pass; drawing happens via the RenderContext methods below.
62 virtual void BeginRender() override;
63 /// Ends the active render pass, submits the command buffer, and presents.
64 virtual void EndRender() override;
65 /// Tears down every Vulkan resource in reverse dependency order.
66 virtual void Cleanup() override;
67 /// Blocks until the device finishes all submitted GPU work.
68 virtual void WaitIdle() override;
69 /// Kicks off the current frame's async buffer upload batch.
70 virtual void FlushPendingTransfers() override;
71
72 // GPU memory tracking
73 /// Returns total bytes currently allocated by VulkanBuffer.
74 virtual size_t GetGPUMemoryUsed() const override;
75 /// Returns the device-local heap size reported by the allocator.
76 virtual size_t GetGPUMemoryBudget() const override;
77
78 /// Recreates the swapchain for the new window dimensions.
79 virtual void Resize(uint32_t width, uint32_t height) override;
80
81 inline void SetRender(bool value) { bRender = value; }
82 inline bool GetRender() { return bRender; }
83
84 /// Initializes ImGui and its Vulkan backend against the active render pass.
85 virtual bool CreateImGUI() override;
86
87 virtual RenderContext* GetContext() override { return this; }
88
89 // Feature capability mask
90 virtual uint32_t GetFeatureCaps() const override {
93 }
94
95 // RenderContext interface
96 /// Issues a non-indexed draw call and updates the vertex/triangle counters.
97 virtual void Draw(uint32_t vertexCount) override;
98 /// Non-indexed draw starting at firstVertex.
99 virtual void Draw(uint32_t vertexCount, uint32_t firstVertex) override;
100 /// Issues an indexed draw call and updates the vertex/triangle counters.
101 virtual void DrawIndexed(uint32_t indexCount) override;
102 /// Indexed draw starting at firstIndex with a base vertex offset.
103 virtual void DrawIndexed(uint32_t indexCount, uint32_t firstIndex,
104 int32_t baseVertex) override;
105 /// Issues an instanced, non-indexed draw call.
106 virtual void DrawInstance(uint32_t instanceCount,
107 uint32_t vertexPerInstance) override;
108 /// Issues an instanced, indexed draw call.
109 virtual void DrawIndexedInstance(uint32_t instanceCount,
110 uint32_t indexPerInstance) override;
111 /// Instanced indexed draw starting at firstIndex with a base vertex offset.
112 virtual void DrawIndexedInstance(uint32_t instanceCount,
113 uint32_t indexPerInstance,
114 uint32_t firstIndex,
115 int32_t baseVertex) override;
116
117 /// Stores the cull face for the next pipeline rebuild (Vulkan state is baked).
118 virtual void SetRenderFace(RenderFace face) override;
119 /// Stores the polygon mode for the next pipeline rebuild (Vulkan state is baked).
120 virtual void SetRenderMode(RenderMode mode) override;
121 /// Sets the dynamic viewport on the active command buffer.
122 virtual void SetViewport(float x, float y, float width, float height,
123 float minDepth = 0.0f,
124 float maxDepth = 1.0f) override;
125 /// Stores the clear color used by the next BeginRender.
126 virtual void ClearRenderTarget(float r, float g, float b,
127 float a) override;
128 /// No-op; depth/stencil clears are driven by the render pass clear values.
129 virtual void ClearDepthStencil(bool clearDepth, bool clearStencil,
130 float depth, uint8_t stencil) override;
131
132 /// Binds a vertex buffer slot, switching to the pipeline built for the
133 /// buffer's registered vertex format.
134 virtual void BindVertexBuffer(RefPtr<BufferBase> buffer,
135 uint32_t slot = 0) override;
136 /// Binds a 32-bit index buffer.
137 virtual void BindIndexBuffer(RefPtr<BufferBase> buffer,
138 uint32_t slot = 0) override;
139 /// Pushes constant-buffer data via push constants, applying TAA jitter
140 /// or the shadow push-constant cache as needed.
141 virtual void BindConstantBuffer(RefPtr<BufferBase> buffer,
142 uint32_t slot = 0) override;
143
144 /// Allocates and initializes a VulkanBuffer.
145 virtual BufferBase* CreateBuffer(BufferType Type, uint32_t size,
146 void* data) override;
147 /// Compiles a VulkanShader from source.
148 virtual Shader* CreateShader(const std::string& shaderSource) override;
149 /// Loads a texture from disk and writes its descriptor sets.
150 virtual Texture* CreateTexture(const std::string& TexturePath) override;
151 /// Loads a texture from an in-memory RGBA8 buffer.
152 virtual Texture* CreateTextureFromData(uint32_t width, uint32_t height,
153 void* data) override;
154
155 /// Loads a cubemap from six face images and writes it into the skybox descriptor sets.
156 Texture* CreateCubemapTexture(const std::array<std::string, 6>& facePaths);
157 /// Loads an equirectangular panorama as a cubemap and writes it into the skybox descriptor sets.
158 Texture* CreateCubemapTextureFromPanorama(const std::string& panoramaPath);
159
160 /// Binds a texture's descriptor set at slot 0, skipping cubemaps and the GBuffer geometry pass.
161 virtual void BindTexture(RefPtr<Sleak::Texture> texture, uint32_t slot = 0) override;
162 /// Raw-pointer variant of BindTexture.
163 virtual void BindTextureRaw(Sleak::Texture* texture, uint32_t slot = 0) override;
164 /// Binds the skybox pipeline and its descriptor set for the current frame.
165 virtual void BeginSkyboxPass() override;
166 /// Restores the previous pipeline and descriptor set after the skybox draw.
167 virtual void EndSkyboxPass() override;
168 /// Copies bone matrices into the current frame's UBO and binds its descriptor set.
169 virtual void BindBoneBuffer(RefPtr<BufferBase> buffer) override;
170 /// Binds the skinned pipeline matching the currently active render pass.
171 virtual void BeginSkinnedPass() override;
172 /// Restores the previous pipeline after skinned draws.
173 virtual void EndSkinnedPass() override;
174 /// Binds the custom-format pipeline matching the currently active render pass.
175 virtual void BeginCustomFormatPass(VertexFormatHandle format) override;
176 /// Restores the previous pipeline and descriptor set after custom-format draws.
177 virtual void EndCustomFormatPass() override;
178 /// Binds the debug line pipeline for the current frame.
179 virtual void BeginDebugLinePass() override;
180 /// Restores the previous pipeline and descriptor set after debug line draws.
181 virtual void EndDebugLinePass() override;
182
183 // Shadow pass support
184 /// Marks the shadow pass active and invalidates the push constant cache.
185 virtual void BeginShadowPass() override;
186 /// Marks the shadow pass inactive.
187 virtual void EndShadowPass() override;
188 virtual bool IsShadowPassActive() const override { return m_shadowPassActive; }
189
190 // Light UBO update (called by LightManager)
191 /// Copies light and shadow data into the current frame's mapped UBO.
192 void UpdateShadowLightUBO(const void* data, uint32_t size) override;
193 /// Stages the light view-projection matrix for commit at the next BeginRender.
194 void SetLightVP(const float* lightVP) override;
195
196 // Deferred rendering overrides
197 virtual bool IsDeferredEnabled() const override { return m_deferredEnabled && m_gbufferResourcesCreated; }
198 virtual bool IsInGeometryPass() const override { return m_inGeometryPass; }
199 /// Binds the GBuffer pipeline and marks the geometry pass active.
200 /// Called by RenderCommandQueue when deferred is active.
201 virtual void BindGBufferShader() override;
202 /// Writes a material's textures and params into its ring slot and binds it at set 0.
203 virtual void BindPBRMaterial(Sleak::Material* material) override;
204 /// Runs the deferred lighting pass, reading the GBuffer and writing the HDR scene image.
205 virtual void ExecuteDeferredLightingPass() override;
206 /// Begins the forward transparent render pass over the HDR scene image.
207 virtual void BeginForwardTransparentPass() override;
208 /// Marks the forward transparent pass ended; EndRender closes the actual render pass.
209 virtual void EndForwardTransparentPass() override;
210 /// Copies deferred CB data into the current frame's UBO and snapshots the camera matrices.
211 virtual void UpdateDeferredCB(const void* data, uint32_t size) override;
212
213 // MSAA
214 /// Rebuilds the swapchain-dependent pipelines and render pass for a
215 /// queued MSAA sample count change.
216 void ApplyMSAAChange() override;
217 /// Recreates the swapchain to apply a queued VSync toggle.
218 void ApplyVSyncChange() override;
219 /// Recreates the extent-dependent shadow map objects at the queued resolution.
220 void ApplyShadowResolutionChange() override;
221
222private:
223 /// Compiles the skybox shaders and creates the skybox descriptor set and pipeline.
224 bool CreateSkyboxPipeline();
225 /// Compiles the skinned shaders and creates the forward skinned pipeline.
226 bool CreateSkinnedPipeline();
227
228 // Deferred rendering
229 /// Creates the GBuffer color images, render passes, descriptors, and
230 /// pipelines that make up the deferred lighting path.
231 bool CreateGBufferResources();
232 /// Creates the GBuffer render pass with its three color attachments and depth.
233 bool CreateGBufferRenderPass();
234 /// Creates the GBuffer framebuffer binding the GBuffer images and depth.
235 bool CreateGBufferFramebuffer();
236 /// Compiles the GBuffer shaders and creates the geometry pipeline, reusing pipelineLay.
237 bool CreateGBufferPipeline();
238 /// Compiles the skinned GBuffer shaders so skinned meshes write into the GBuffer.
239 bool CreateSkinnedGbufferPipeline();
240 /// Creates the deferred lighting render pass with a single color attachment.
241 bool CreateLightingRenderPass();
242 /// Creates one lighting pass framebuffer per swapchain image, all aliasing the HDR target.
243 bool CreateLightingFramebuffers();
244 /// Compiles the lighting shaders and creates the fullscreen lighting pipeline.
245 bool CreateLightingPipeline();
246 /// Creates the forward transparent render pass writing into the HDR scene image.
247 bool CreateForwardRenderPass();
248 /// Creates one forward transparent framebuffer per swapchain image, all aliasing the HDR target.
249 bool CreateForwardFramebuffers();
250 /// Creates the GBuffer sampler descriptor set layout, pool, and per-frame sets.
251 bool CreateGBufferDescriptorSets();
252 /// Creates the per-frame deferred constant buffer holding InvViewProj and screen size.
253 bool CreateDeferredCBResources();
254 /// Creates the PBR material descriptor layout, pool, ring of sets, and GBuffer geometry pipeline layout.
255 bool CreatePBRMaterialResources();
256 /// Appends one chunk of PBR material sets and UBO slots to a frame's ring.
257 bool AddPBRMaterialChunk(uint32_t frame);
258 /// Destroys every PBR material chunk of every frame.
259 void DestroyPBRMaterialChunks();
260 /// Creates stub IBL irradiance, prefilter, and BRDF LUT images, samplers, and descriptor set.
261 bool CreateIBLResources();
262 /// Destroys all GBuffer, lighting, and forward transparent pass resources.
263 void CleanupGBufferResources();
264 /// Destroys the IBL images, samplers, and descriptor resources.
265 void CleanupIBLResources();
266 /// Writes the GBuffer, depth, and shadow images into the sampler descriptor sets before the lighting pass.
267 void UpdateGBufferDescriptors();
268
269 // Shadow mapping
270 /// Creates the shadow depth image, sampler, render pass, and framebuffer.
271 bool CreateShadowResources();
272 /// Compiles the shadow depth shader and creates the shadow pass pipeline.
273 bool CreateShadowPipeline();
274 /// Creates the per-frame light and shadow UBO buffers and descriptor sets.
275 bool CreateShadowLightUBOResources();
276 /// Destroys the shadow map image, pipeline, render pass, and light UBO resources.
277 void CleanupShadowResources();
278 /// Creates the Vulkan instance with validation layers when available.
279 bool InitVulkan();
280 /// Creates the SDL-backed Vulkan presentation surface.
281 bool CreateSurface();
282 /// Selects the physical GPU and creates the logical device and queues.
283 bool CreateDevice();
284 /// Creates the swapchain from the queried surface capabilities.
285 bool CreateSwapChain();
286 /// Rebuilds the swapchain and its dependents after resize or resolution change.
287 bool RecreateSwapChain();
288 /// Creates an image view for each swapchain image.
289 bool CreateImageViews();
290 /// Creates the main forward graphics pipeline and its pipeline layout.
291 bool CreateGraphicsPipeline();
292 /// Creates the main forward render pass with optional MSAA color and resolve attachments.
293 bool CreateRenderPass();
294 /// Creates one framebuffer per swapchain image for the main render pass.
295 bool CreateFrameBuffer();
296 /// Creates the graphics command pool.
297 bool CreateCommandPool();
298 /// Allocates one primary command buffer per frame in flight.
299 bool CreateCommandBuffer();
300 /// Creates the per-swapchain-image semaphores and per-frame fences and
301 /// transfer semaphores.
302 bool CreateSyncObjects();
303 /// Creates the depth image, memory, and image view.
304 bool CreateDepthResources();
305 /// Creates the texture, bone UBO, light UBO, and shadow sampler descriptor set layouts.
306 bool CreateDescriptorSetLayout();
307 /// Creates the descriptor pool backing the per-texture descriptor sets.
308 bool CreateDescriptorPool();
309 /// Allocates one texture descriptor set per swapchain image.
310 bool AllocateDescriptorSets();
311 /// Creates the fallback 1x1 white texture and writes it into the global descriptor sets.
312 bool CreateDefaultTexture();
313 /// Allocates and writes a per-texture descriptor set for the given texture.
314 void WriteTextureDescriptors(VulkanTexture* texture);
315 /// Registers the debug messenger callback for validation output.
316 bool SetupDebugMessenger();
317
318 /// Destroys the swapchain, its image views, and framebuffers.
319 void CleanupSwapChain();
320 /// Destroys the depth image, memory, and image view.
321 void CleanupDepthResources();
322
323 // MSAA resources
324 /// Creates the MSAA color image used as the multisampled render target.
325 bool CreateMSAAColorResources();
326 /// Destroys the MSAA color image, view, and memory.
327 void CleanupMSAAColorResources();
328 /// Queries the highest MSAA sample count the GPU supports.
329 VkSampleCountFlagBits GetMaxUsableSampleCount();
330
331 /// No-op; Vulkan polygon mode changes require pipeline recreation.
332 virtual void ConfigureRenderMode() override;
333 /// No-op; Vulkan cull mode changes require pipeline recreation.
334 virtual void ConfigureRenderFace() override;
335
336 /// Builds one queue create info per unique queue family index.
337 std::vector<VkDeviceQueueCreateInfo>
338 GetUniqueQueueCreateInfos();
339
340 /// Queries surface capabilities, formats, and present modes.
341 std::optional<SwapchainDetails> QuerySwapchain();
342
343 /// Picks a UNORM surface format to avoid double sRGB encoding.
344 VkSurfaceFormatKHR ChooseFormat(
345 const std::vector<VkSurfaceFormatKHR>& formats);
346 /// Picks FIFO when VSync is on, otherwise MAILBOX or IMMEDIATE.
347 VkPresentModeKHR ChoosePresentMode(
348 const std::vector<VkPresentModeKHR>& modes);
349 /// Clamps the window size to the surface's supported extent.
350 VkExtent2D ChooseExtend(SwapchainDetails details);
351
352 /// Picks the first supported depth-stencil format from the candidate list.
353 VkFormat FindDepthFormat();
354 /// Finds a memory type index matching the filter and property flags.
355 uint32_t FindMemoryType(uint32_t typeFilter,
356 VkMemoryPropertyFlags properties);
357
358 /// Fills the debug messenger create info with severity and callback.
359 void PopulateDebugMessengerCreateInfo(
360 VkDebugUtilsMessengerCreateInfoEXT& createInfo);
361
362 /// Debug messenger callback that routes Vulkan messages to the logger.
363 static VkBool32 Validation(
364 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
365 VkDebugUtilsMessageTypeFlagsEXT messageTypes,
366 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
367 void* pUserData);
368
369 bool bRender = true;
370 bool bFrameStarted = false;
371
372 static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
373 uint32_t currentFrame = 0;
374 uint32_t CurrentFrameIndex = 0;
375 uint32_t m_semaphoreIndex = 0; // cycles through swapchain image count
376 VkInstance instance = VK_NULL_HANDLE;
377 VkSurfaceKHR surface = VK_NULL_HANDLE;
378 VkRenderPass renderPass = VK_NULL_HANDLE;
379 VkSwapchainKHR swapChain = VK_NULL_HANDLE;
380 VkCommandPool commands = VK_NULL_HANDLE;
381 std::vector<VkCommandBuffer> commandBuffers;
382 VkCommandBuffer command = VK_NULL_HANDLE; // alias for commandBuffers[currentFrame]
383 std::vector<VkFramebuffer> swapChainFramebuffers;
384 std::vector<VkImage> swapChainImages;
385 std::vector<VkImageView> swapChainImageViews;
386 VkFormat scImageFormat;
387 VkExtent2D scExtent;
388 VkDevice device = VK_NULL_HANDLE;
389 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
390 std::vector<VkPhysicalDevice> GPUs;
391 VkPipeline pipeline = VK_NULL_HANDLE;
392 VkPipelineLayout pipelineLay = VK_NULL_HANDLE;
393 std::vector<VkSemaphore> imageAvailableSemaphores;
394 std::vector<VkSemaphore> renderFinishedSemaphores;
395 std::vector<VkFence> inFlightFences;
396 std::vector<VkFence> imagesInFlight;
397 VulkanShader* simpleShader = nullptr;
398 VkClearValue clearColor;
399
400 // Depth buffer
401 VkImage depthImage = VK_NULL_HANDLE;
402 VkDeviceMemory depthImageMemory = VK_NULL_HANDLE;
403 VkImageView depthImageView = VK_NULL_HANDLE;
404 VkFormat depthFormat;
405
406 // MSAA color buffer (multisample resolve target)
407 VkSampleCountFlagBits m_msaaSamples = VK_SAMPLE_COUNT_1_BIT;
408 VkImage m_msaaColorImage = VK_NULL_HANDLE;
409 VkDeviceMemory m_msaaColorImageMemory = VK_NULL_HANDLE;
410 VkImageView m_msaaColorImageView = VK_NULL_HANDLE;
411
412 // Descriptor sets for uniform buffers
413 VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
414 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
415 std::vector<VkDescriptorSet> descriptorSets;
416
417 QueueIndices QueueIDs;
418 VkQueue graphicsQueue = VK_NULL_HANDLE;
419 VkQueue computeQueue = VK_NULL_HANDLE;
420 VkQueue transferQueue = VK_NULL_HANDLE;
421 VkQueue presentQueue = VK_NULL_HANDLE;
422
423 VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
424 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT =
425 nullptr;
426
427 Window* sdlWindow;
428
429 // Texture binding
430 bool m_textureDescriptorsWritten = false;
431 VulkanTexture* m_defaultTexture = nullptr;
432
433 // Skybox pipeline
434 VkPipeline skyboxPipeline = VK_NULL_HANDLE;
435 VulkanShader* skyboxShader = nullptr;
436 VkDescriptorPool skyboxDescriptorPool = VK_NULL_HANDLE;
437 std::vector<VkDescriptorSet> skyboxDescriptorSets;
438 bool m_skyboxDescriptorsWritten = false;
439 VkImageView m_skyboxCubemapView = VK_NULL_HANDLE; // cached for MSAA re-bind
440 VkSampler m_skyboxCubemapSampler = VK_NULL_HANDLE; // cached for MSAA re-bind
441
442 // Skinned pipeline (uses skinned shaders with bone UBO)
443 VkPipeline skinnedPipeline = VK_NULL_HANDLE;
444 VulkanShader* skinnedShader = nullptr;
445
446 // Debug line pipeline
447 VkPipeline debugLinePipeline = VK_NULL_HANDLE;
448 VulkanShader* debugLineShader = nullptr;
449 /// Compiles the debug line shaders and creates the line-list pipeline.
450 bool CreateDebugLinePipeline();
451
452 // Custom vertex format pipelines (built lazily per registered VertexFormatHandle)
453 /// The four pipeline variants a registered vertex layout can drive.
454 /// A failed flag marks a variant as permanently absent so draws skip it
455 /// instead of retrying compilation every frame.
456 struct CustomFormatPipelines {
457 VkPipeline main = VK_NULL_HANDLE;
458 VkPipeline shadow = VK_NULL_HANDLE;
459 VkPipeline gbuffer = VK_NULL_HANDLE;
460 VkPipeline transparent = VK_NULL_HANDLE;
461 bool mainFailed = false;
462 bool shadowFailed = false;
463 bool gbufferFailed = false;
464 bool transparentFailed = false;
465 };
466 std::unordered_map<VertexFormatHandle, CustomFormatPipelines>
467 m_customFormatPipelines;
468 VertexFormatHandle m_activeCustomFormat = 0;
469 /// Set while the active custom format has no pipeline for the current pass;
470 /// draws are dropped rather than issued against a mismatched vertex layout.
471 bool m_customFormatUnbound = false;
472 /// True while the bound vertex buffer's format has no pipeline for this pass.
473 bool CustomFormatDrawsSuppressed() const;
474 /// Creates any missing pipeline variant for a registered format; returns false when the main variant is unusable.
475 bool CreateCustomFormatPipelines(VertexFormatHandle format);
476 /// Destroys every cached custom-format pipeline (all variants, all formats).
477 void DestroyCustomFormatPipelines();
478
479 // Bone UBO (for skeletal animation — set 1, binding 0)
480 VkDescriptorSetLayout boneDescriptorSetLayout = VK_NULL_HANDLE;
481 VkDescriptorPool boneDescriptorPool = VK_NULL_HANDLE;
482 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> boneUBOBuffers = {};
483 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> boneUBOMemory = {};
484 std::array<void*, MAX_FRAMES_IN_FLIGHT> boneUBOMapped = {};
485 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> boneDescriptorSets = {};
486 bool m_boneUBOCreated = false;
487 /// Creates the per-frame bone UBO buffers and their descriptor sets.
488 bool CreateBoneUBOResources();
489 /// Destroys the bone UBO buffers, memory, and descriptor pool.
490 void CleanupBoneUBOResources();
491
492 // ImGUI
493 VkDescriptorPool imguiDescriptorPool = VK_NULL_HANDLE;
494
495 // Shadow mapping resources
496 VkImage m_shadowImage = VK_NULL_HANDLE;
497 VkDeviceMemory m_shadowImageMemory = VK_NULL_HANDLE;
498 VkImageView m_shadowImageView = VK_NULL_HANDLE;
499 VkSampler m_shadowSampler = VK_NULL_HANDLE; // compare sampler (hardware PCF)
500 VkSampler m_shadowRawSampler = VK_NULL_HANDLE; // non-compare sampler (PCSS blocker search)
501 VkRenderPass m_shadowRenderPass = VK_NULL_HANDLE;
502 VkFramebuffer m_shadowFramebuffer = VK_NULL_HANDLE;
503 VkPipeline m_shadowPipeline = VK_NULL_HANDLE;
504 VulkanShader* m_shadowShader = nullptr;
505 bool m_shadowPassActive = false;
506 // m_shadowResourcesCreated lives in the Renderer base (shared with the
507 // shadow-resolution change-request logic)
508
509 // Shadow push-constant memo: LightVP*World is frame-constant per unique
510 // World, so cache it and skip the matmul when consecutive casters (all
511 // draws share the identity transform) reuse the same World matrix.
512 float m_shadowWorldCache[16] = {};
513 float m_shadowPCCache[32] = {};
514 bool m_shadowPCCacheValid = false;
515
516 // Light VP matrix (stored as raw floats for push constant computation)
517 float m_lightVP[16] = {};
518 // Staging slot: SetLightVP writes here; BeginRender copies it to
519 // m_lightVP before the shadow pass. Keeps m_lightVP stable for the
520 // entire frame so shadow pass and main pass agree on the transform.
521 float m_pendingLightVP[16] = {};
522 bool m_hasPendingLightVP = false;
523
524 // Light/Shadow UBO (set 2, binding 0)
525 VkDescriptorSetLayout m_lightUBODescriptorSetLayout = VK_NULL_HANDLE;
526 VkDescriptorPool m_lightUBODescriptorPool = VK_NULL_HANDLE;
527 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_lightUBOBuffers = {};
528 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_lightUBOMemory = {};
529 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_lightUBOMapped = {};
530 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_lightUBODescriptorSets = {};
531
532 // Shadow map sampler descriptor (set 3, binding 0)
533 VkDescriptorSetLayout m_shadowSamplerDescriptorSetLayout = VK_NULL_HANDLE;
534 VkDescriptorPool m_shadowSamplerDescriptorPool = VK_NULL_HANDLE;
535 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_shadowSamplerDescriptorSets = {};
536 bool m_lightUBOCreated = false;
537
538 // Async buffer transfer (zero-CPU-blocking GPU uploads)
539 std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_transferSemaphores = {};
540 std::array<VulkanBuffer::AsyncFlushResult, MAX_FRAMES_IN_FLIGHT> m_asyncFlush;
541
542 // ---- Deferred GBuffer ----
543 // 3 color attachments: RT0=AlbedoAO, RT1=NormalRough, RT2=MetalEmit.
544 // World position is reconstructed from the depth buffer + InvViewProj in
545 // the lighting/SSAO/SSR passes (no RGBA32F worldpos RT) to cut GBuffer
546 // bandwidth on a fill-bound renderer.
547 static constexpr uint32_t GBUFFER_COUNT = 3;
548 VkImage m_gbufferImages[GBUFFER_COUNT] = {};
549 VkDeviceMemory m_gbufferMemory[GBUFFER_COUNT] = {};
550 VkImageView m_gbufferViews[GBUFFER_COUNT] = {};
551 /// GBuffer attachment formats: RT0 AlbedoAO, RT1 NormalRough, RT2 MetalEmit. Defined in VulkanDeferred.cpp.
552 static const VkFormat m_gbufferFormats[GBUFFER_COUNT];
553 VkRenderPass m_gbufferRenderPass = VK_NULL_HANDLE;
554 VkFramebuffer m_gbufferFramebuffer = VK_NULL_HANDLE;
555 VkPipeline m_gbufferPipeline = VK_NULL_HANDLE;
556 VkPipeline m_skinnedGbufferPipeline = VK_NULL_HANDLE;
557 VkPipelineLayout m_gbufferPipelineLayout = VK_NULL_HANDLE;
558 VulkanShader* m_gbufferShader = nullptr;
559 bool m_gbufferResourcesCreated = false;
560 bool m_inGeometryPass = false;
561
562 // Lighting pass
563 VkRenderPass m_lightingRenderPass = VK_NULL_HANDLE;
564 std::vector<VkFramebuffer> m_lightingFramebuffers; // one per swapchain image
565 VkPipeline m_lightingPipeline = VK_NULL_HANDLE;
566 VkPipelineLayout m_lightingPipelineLayout = VK_NULL_HANDLE;
567 VulkanShader* m_lightingShader = nullptr;
568
569 // Forward transparent pass
570 VkRenderPass m_forwardRenderPass = VK_NULL_HANDLE;
571 std::vector<VkFramebuffer> m_forwardFramebuffers; // one per swapchain image (color+depth, non-MSAA)
572 bool m_inForwardTransparentPass = false;
573 bool m_forwardPassOpen = false; // true when forward RP is currently recording
574
575 // GBuffer sampler descriptor set (set 0 in lighting pass)
576 VkDescriptorSetLayout m_gbufferSamplerDSL = VK_NULL_HANDLE;
577 VkDescriptorPool m_gbufferSamplerPool = VK_NULL_HANDLE;
578 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_gbufferSamplerSets = {};
579 VkSampler m_gbufferSampler = VK_NULL_HANDLE;
580 VkSampler m_depthSampler = VK_NULL_HANDLE;
581
582 // Deferred CB UBO (set 1 in lighting pass): InvViewProj + screenSize + near/far
583 struct DeferredCBData {
584 float InvViewProj[16];
585 float ScreenW, ScreenH, NearP, FarP;
586 };
587 VkDescriptorSetLayout m_deferredCBDSL = VK_NULL_HANDLE;
588 VkDescriptorPool m_deferredCBPool = VK_NULL_HANDLE;
589 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_deferredCBBuffers = {};
590 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_deferredCBMemory = {};
591 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_deferredCBMapped = {};
592 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_deferredCBSets = {};
593 bool m_deferredCBCreated = false;
594
595 // PBR material descriptor resources (GBuffer geometry pass, set 0)
596 // One per-frame descriptor set updated with each BindPBRMaterial() call.
597 struct alignas(16) PBRMaterialParams {
598 float albedoFactorR, albedoFactorG, albedoFactorB, albedoFactorA; // vec4
599 float metallicFactor, roughnessFactor, aoFactor, normalIntensity; // 4 floats
600 float emissiveR, emissiveG, emissiveB, emissiveIntensity; // vec4
601 float tilingX, tilingY, offsetX, offsetY; // 4 floats
602 uint32_t hasNormalMap, hasMetallicMap, hasRoughnessMap, hasAOMap; // 4 uints
603 uint32_t hasEmissiveMap; float _pad0, _pad1, _pad2; // 4 floats
604 };
605 // Per-frame RING of PBR material sets: each material drawn in a frame gets
606 // its own set + its own UBO sub-region, so a set/region is never rewritten
607 // while already bound in the recording command buffer (UPDATE_AFTER_BIND
608 // VUID). The ring grows by one chunk when a frame runs out of slots.
609 static constexpr uint32_t PBR_SETS_PER_FRAME = 64;
610 struct PBRMaterialChunk {
611 VkDescriptorPool pool = VK_NULL_HANDLE;
612 std::array<VkDescriptorSet, PBR_SETS_PER_FRAME> sets = {};
613 VkBuffer ubo = VK_NULL_HANDLE;
614 VkDeviceMemory memory = VK_NULL_HANDLE;
615 void* mapped = nullptr;
616 };
617 VkDescriptorSetLayout m_pbrMaterialDSL = VK_NULL_HANDLE;
618 std::array<std::vector<PBRMaterialChunk>, MAX_FRAMES_IN_FLIGHT>
619 m_pbrMaterialChunks;
620 VkDeviceSize m_pbrMaterialUBOStride = 0;
621 uint32_t m_pbrMaterialSlot[MAX_FRAMES_IN_FLIGHT] = {};
622 bool m_pbrMaterialResourcesCreated = false;
623 VkPipelineLayout m_gbufferGeomLayout = VK_NULL_HANDLE;
624
625 // IBL resources (lighting pass, set 3)
626 VkImage m_iblIrradianceImage = VK_NULL_HANDLE;
627 VkDeviceMemory m_iblIrradianceMemory = VK_NULL_HANDLE;
628 VkImageView m_iblIrradianceView = VK_NULL_HANDLE;
629 VkSampler m_iblIrradianceSampler = VK_NULL_HANDLE;
630
631 VkImage m_iblPrefilterImage = VK_NULL_HANDLE;
632 VkDeviceMemory m_iblPrefilterMemory = VK_NULL_HANDLE;
633 VkImageView m_iblPrefilterView = VK_NULL_HANDLE;
634 VkSampler m_iblPrefilterSampler = VK_NULL_HANDLE;
635 static constexpr uint32_t IBL_PREFILTER_MIP_LEVELS = 5;
636
637 VkImage m_iblBrdfLutImage = VK_NULL_HANDLE;
638 VkDeviceMemory m_iblBrdfLutMemory = VK_NULL_HANDLE;
639 VkImageView m_iblBrdfLutView = VK_NULL_HANDLE;
640 VkSampler m_iblBrdfLutSampler = VK_NULL_HANDLE;
641
642 VkDescriptorSetLayout m_iblDSL = VK_NULL_HANDLE;
643 VkDescriptorPool m_iblPool = VK_NULL_HANDLE;
644 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_iblSettingsBuffers = {};
645 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_iblSettingsMemory = {};
646 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_iblSettingsMapped = {};
647 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_iblSets = {};
648 bool m_iblResourcesCreated = false;
649 bool m_iblReady = false;
650
651 // ---- SSAO resources ----
652 // Half-resolution R8 occlusion buffer (raw SSAO + blurred).
653 static constexpr uint32_t SSAO_KERNEL_SIZE = 32;
654 static constexpr uint32_t SSAO_NOISE_SIZE = 4;
655 bool m_ssaoResourcesCreated = false;
656 VkExtent2D m_ssaoExtent = {0, 0};
657 VkFormat m_ssaoFormat = VK_FORMAT_R8_UNORM;
658
659 VkImage m_ssaoRawImage = VK_NULL_HANDLE;
660 VkDeviceMemory m_ssaoRawMemory = VK_NULL_HANDLE;
661 VkImageView m_ssaoRawView = VK_NULL_HANDLE;
662 VkFramebuffer m_ssaoRawFramebuffer = VK_NULL_HANDLE;
663
664 VkImage m_ssaoBlurImage = VK_NULL_HANDLE;
665 VkDeviceMemory m_ssaoBlurMemory = VK_NULL_HANDLE;
666 VkImageView m_ssaoBlurView = VK_NULL_HANDLE;
667 VkFramebuffer m_ssaoBlurFramebuffer = VK_NULL_HANDLE;
668
669 VkRenderPass m_ssaoRenderPass = VK_NULL_HANDLE; // shared for raw + blur
670 VkPipeline m_ssaoPipeline = VK_NULL_HANDLE;
671 VkPipeline m_ssaoBlurPipeline = VK_NULL_HANDLE;
672 VkPipelineLayout m_ssaoPipelineLayout = VK_NULL_HANDLE;
673 VkPipelineLayout m_ssaoBlurPipelineLayout = VK_NULL_HANDLE;
674 VulkanShader* m_ssaoShader = nullptr;
675 VulkanShader* m_ssaoBlurShader = nullptr;
676
677 VkSampler m_ssaoSampler = VK_NULL_HANDLE; // linear clamp
678 VkSampler m_ssaoPointSampler = VK_NULL_HANDLE; // nearest clamp for depth
679
680 // SSAO noise texture (4x4 RGBA random rotation vectors)
681 VkImage m_ssaoNoiseImage = VK_NULL_HANDLE;
682 VkDeviceMemory m_ssaoNoiseMemory = VK_NULL_HANDLE;
683 VkImageView m_ssaoNoiseView = VK_NULL_HANDLE;
684 VkSampler m_ssaoNoiseSampler = VK_NULL_HANDLE;
685
686 // SSAO pass descriptor sets
687 struct alignas(16) SSAOParams {
688 float View[16];
689 float Projection[16];
690 float InvViewProj[16]; // inverse(View*Proj) for depth recon
691 float Kernel[SSAO_KERNEL_SIZE][4]; // xyz=dir, w=pad
692 float ScreenW, ScreenH;
693 float NoiseScaleX, NoiseScaleY;
694 float Radius, Bias, Power, Intensity;
695 uint32_t KernelSize;
696 float _pad0, _pad1, _pad2;
697 };
698 VkDescriptorSetLayout m_ssaoInputDSL = VK_NULL_HANDLE; // set 0: samplers
699 VkDescriptorSetLayout m_ssaoUboDSL = VK_NULL_HANDLE; // set 1: UBO
700 VkDescriptorSetLayout m_ssaoBlurDSL = VK_NULL_HANDLE; // set 0: blur input + depth
701 VkDescriptorPool m_ssaoDescriptorPool = VK_NULL_HANDLE;
702 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_ssaoInputSets = {};
703 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_ssaoUboSets = {};
704 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_ssaoBlurSets = {};
705 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_ssaoUboBuffers = {};
706 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_ssaoUboMemory = {};
707 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_ssaoUboMapped = {};
708
709 // Cached camera state for SSAO UBO fill (set from SetViewProj or inferred
710 // from DeferredCB's InvViewProj). We populate View+Projection at SSAO time.
711 float m_cachedView[16] = {};
712 float m_cachedProjection[16] = {};
713 // inverse(View*Proj) snapshot from the lighting DeferredCB — reused by the
714 // SSAO/SSR passes to reconstruct world position from the depth buffer.
715 float m_cachedInvViewProj[16] = {};
716
717 /// Creates all SSAO images, render pass, framebuffers, descriptors, and pipelines.
718 bool CreateSSAOResources();
719 /// Primes the disabled-effect fallback images (ssaoBlur=white, ssr=black, bloom
720 /// mip0=black) once after (re)creation, leaving them SHADER_READ_ONLY. Per-frame
721 /// disabled paths then skip the redundant clear since the content is static.
722 void InitDisabledEffectFallbacks();
723 /// Destroys all SSAO pipelines, framebuffers, descriptors, images, and samplers.
724 void CleanupSSAOResources();
725 /// Creates the full-res raw and blurred SSAO color images, views, and samplers.
726 bool CreateSSAOImages();
727 /// Creates the shared SSAO render pass (R8 color, DONT_CARE load, shader-read-only output).
728 bool CreateSSAORenderPass();
729 /// Creates the raw and blur SSAO framebuffers.
730 bool CreateSSAOFramebuffers();
731 /// Compiles the SSAO and SSAO-blur shaders and creates their pipelines.
732 bool CreateSSAOPipelines();
733 /// Creates the SSAO input/UBO/blur descriptor layouts, pool, sets, and UBO buffers.
734 bool CreateSSAODescriptorResources();
735 /// Generates and uploads the 4x4 tangent-plane rotation noise texture.
736 bool CreateSSAONoiseTexture();
737 /// Writes the GBuffer, depth, and noise samplers into the SSAO input and blur descriptor sets.
738 void UpdateSSAODescriptors();
739 /// Fills the SSAO UBO with the cached camera matrices and a cosine-weighted hemisphere kernel.
740 void UpdateSSAOUBO();
741 /// Runs the raw SSAO and bilateral blur passes, or clears the blur target when SSAO is disabled.
742 void RenderSSAOPasses();
743
744 // ---- SSR (Screen-Space Reflections) resources ----
745 // Full-resolution R16G16B16A16 premultiplied reflection buffer. Rendered
746 // after the forward pass, added into the HDR scene by the bloom composite.
747 struct alignas(16) SSRParams {
748 float View[16];
749 float Projection[16];
750 float InvViewProj[16]; // inverse(View*Proj) for depth recon
751 float CameraPos[4]; // xyz = world pos, w = pad
752 float ScreenW, ScreenH;
753 float MaxDistance; // view-space ray march distance
754 float Thickness; // depth intersection thickness
755 int NumSteps; // coarse steps
756 int NumBinarySteps; // refinement steps
757 float RoughnessThreshold; // beyond this, no reflections
758 float _pad;
759 };
760
761 bool m_ssrResourcesCreated = false;
762 // m_ssrEnabled is inherited from RenderEngine::Renderer (base class).
763 VkFormat m_ssrFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
764
765 VkImage m_ssrImage = VK_NULL_HANDLE;
766 VkDeviceMemory m_ssrMemory = VK_NULL_HANDLE;
767 VkImageView m_ssrView = VK_NULL_HANDLE;
768 VkSampler m_ssrSampler = VK_NULL_HANDLE;
769 VkRenderPass m_ssrRenderPass = VK_NULL_HANDLE;
770 VkFramebuffer m_ssrFramebuffer = VK_NULL_HANDLE;
771 VkPipeline m_ssrPipeline = VK_NULL_HANDLE;
772 VkPipelineLayout m_ssrPipelineLayout = VK_NULL_HANDLE;
773 VulkanShader* m_ssrShader = nullptr;
774
775 VkDescriptorSetLayout m_ssrInputDSL = VK_NULL_HANDLE; // set 0: 6 samplers
776 VkDescriptorSetLayout m_ssrUboDSL = VK_NULL_HANDLE; // set 1: UBO
777 VkDescriptorPool m_ssrPool = VK_NULL_HANDLE;
778
779 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_ssrInputSets{};
780 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_ssrUboSets{};
781 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_ssrUboBuffers{};
782 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_ssrUboMemory{};
783 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_ssrUboMapped{};
784
785 /// Creates the SSR image, render pass, framebuffer, descriptors, UBOs, and pipeline.
786 bool CreateSSRResources();
787 /// Destroys the SSR pipeline, framebuffer, descriptors, image, and sampler.
788 void CleanupSSRResources();
789 /// Fills the SSR UBO with the cached camera matrices, camera position, and ray march parameters.
790 void UpdateSSRUBO();
791 /// Writes the GBuffer and HDR scene samplers into the SSR input descriptor sets.
792 void UpdateSSRDescriptors();
793 /// Ray marches screen-space reflections into the SSR buffer, or clears it when SSR is disabled.
794 void RenderSSRPass();
795
796 // ---- TAA (Temporal Anti-Aliasing) resources ----
797 // Ping-pong history accumulation with depth-based reprojection
798 // and 3x3 neighborhood AABB clamping.
799 struct alignas(16) TAAParams {
800 float InvCurrentVP[16]; // inverse of unjittered current VP
801 float PrevVP[16]; // previous frame unjittered VP
802 float ScreenW, ScreenH;
803 float BlendFactor;
804 float _pad;
805 };
806
807 bool m_taaResourcesCreated = false;
808 // m_taaEnabled is inherited from RenderEngine::Renderer (base class).
809 uint64_t m_taaFrameIdx = 0; // global counter; ping-pong = idx % 2
810
811 VkImage m_taaImages[2] = {};
812 VkDeviceMemory m_taaMemory[2] = {};
813 VkImageView m_taaViews[2] = {};
814 VkRenderPass m_taaRenderPass = VK_NULL_HANDLE;
815 VkFramebuffer m_taaFramebufs[2] = {};
816 VkSampler m_taaSampler = VK_NULL_HANDLE;
817
818 VkDescriptorSetLayout m_taaInputDSL = VK_NULL_HANDLE;
819 VkDescriptorSetLayout m_taaUboDSL = VK_NULL_HANDLE;
820 VkDescriptorPool m_taaPool = VK_NULL_HANDLE;
821
822 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_taaInputSets{};
823 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_taaUboSets{};
824 std::array<VkBuffer, MAX_FRAMES_IN_FLIGHT> m_taaUboBuffers{};
825 std::array<VkDeviceMemory, MAX_FRAMES_IN_FLIGHT> m_taaUboMemory{};
826 std::array<void*, MAX_FRAMES_IN_FLIGHT> m_taaUboMapped{};
827
828 VkPipelineLayout m_taaPipelineLayout = VK_NULL_HANDLE;
829 VkPipeline m_taaPipeline = VK_NULL_HANDLE;
830 VulkanShader* m_taaShader = nullptr;
831
832 float m_prevViewProj[16] = {}; // previous frame unjittered VP (row-major)
833 float m_taaJitter[2] = {}; // current frame jitter in UV space
834
835 /// Creates the ping-pong TAA history images, render pass, framebuffers, descriptors, and pipeline.
836 bool CreateTAAResources();
837 /// Destroys the TAA pipeline, framebuffers, descriptors, images, and sampler.
838 void CleanupTAAResources();
839 /// Computes the inverse current view-projection and reprojection blend factor into the TAA UBO.
840 void UpdateTAAUBO();
841 /// Resolves the current frame against TAA history and copies the result back into the HDR scene image.
842 void RenderTAAPass();
843
844 // Per-image "fallback content is valid" flags. Set true once the image is
845 // primed (static black/white in SHADER_READ_ONLY) by either
846 // InitDisabledEffectFallbacks or a disabled-path clear; set false whenever
847 // the enabled path renders into the image (dirtying it). When true, the
848 // disabled path skips its redundant per-frame clear. This stays correct
849 // across runtime enable->disable toggles: the first disabled frame after a
850 // toggle re-primes, then subsequent disabled frames skip.
851 bool m_ssaoFallbackPrimed = false;
852 bool m_ssrFallbackPrimed = false;
853 bool m_bloomFallbackPrimed = false;
854
855 // ---- Bloom + HDR post-processing resources ----
856 // We render the lighting pass into an HDR scene-color image (not the
857 // swapchain). Then we run the bloom pyramid and a final composite that
858 // tonemaps + combines bloom into the swapchain.
859 static constexpr uint32_t BLOOM_MIP_COUNT = 6;
860 bool m_bloomResourcesCreated = false;
861 VkFormat m_hdrSceneFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
862
863 // HDR scene color image (lighting pass target)
864 VkImage m_hdrSceneImage = VK_NULL_HANDLE;
865 VkDeviceMemory m_hdrSceneMemory = VK_NULL_HANDLE;
866 VkImageView m_hdrSceneView = VK_NULL_HANDLE;
867 VkFramebuffer m_hdrSceneFramebuffer = VK_NULL_HANDLE;
868 VkRenderPass m_hdrLightingRenderPass = VK_NULL_HANDLE;
869
870 // Bloom mip chain — single image with BLOOM_MIP_COUNT mip levels; each
871 // level gets its own VkImageView so we can render into/out of it.
872 VkImage m_bloomImage = VK_NULL_HANDLE;
873 VkDeviceMemory m_bloomMemory = VK_NULL_HANDLE;
874 std::array<VkImageView, BLOOM_MIP_COUNT> m_bloomMipViews = {};
875 std::array<VkFramebuffer, BLOOM_MIP_COUNT> m_bloomMipFramebuffers = {};
876 std::array<VkExtent2D, BLOOM_MIP_COUNT> m_bloomMipExtents = {};
877
878 VkRenderPass m_bloomRenderPass = VK_NULL_HANDLE; // shared, loadOp=DONT_CARE, color output
879 VkRenderPass m_bloomAddRenderPass = VK_NULL_HANDLE; // LOAD, blend-add
880 VkRenderPass m_bloomCompositeRenderPass = VK_NULL_HANDLE; // swapchain target (DONT_CARE -> PRESENT_SRC)
881 std::vector<VkFramebuffer> m_bloomCompositeFramebuffers; // one per swapchain image
882
883 VkPipeline m_bloomThresholdPipeline = VK_NULL_HANDLE;
884 VkPipeline m_bloomDownsamplePipeline = VK_NULL_HANDLE;
885 VkPipeline m_bloomUpsamplePipeline = VK_NULL_HANDLE;
886 VkPipeline m_bloomCompositePipeline = VK_NULL_HANDLE;
887 VkPipelineLayout m_bloomFilterPipelineLayout = VK_NULL_HANDLE; // src-only + push constant
888 VkPipelineLayout m_bloomCompositePipelineLayout = VK_NULL_HANDLE; // two inputs + push constant
889 VulkanShader* m_bloomThresholdShader = nullptr;
890 VulkanShader* m_bloomDownsampleShader = nullptr;
891 VulkanShader* m_bloomUpsampleShader = nullptr;
892 VulkanShader* m_bloomCompositeShader = nullptr;
893
894 // One descriptor set per bloom transition (threshold + BLOOM_MIP_COUNT-1
895 // downsamples + BLOOM_MIP_COUNT-1 upsamples) per frame slot.
896 // Simpler: allocate a pool with enough sets for all transitions and
897 // re-write them each frame.
898 VkDescriptorSetLayout m_bloomFilterDSL = VK_NULL_HANDLE; // 1 sampler
899 VkDescriptorSetLayout m_bloomCompositeDSL = VK_NULL_HANDLE; // 2 samplers
900 VkDescriptorPool m_bloomDescriptorPool = VK_NULL_HANDLE;
901
902 // Pre-allocated descriptor sets (per frame slot, per transition)
903 // transitions = 1 (threshold -> mip0) + (BLOOM_MIP_COUNT-1) downsample
904 // + (BLOOM_MIP_COUNT-1) upsample
905 static constexpr uint32_t BLOOM_TRANSITION_COUNT = 1 + (BLOOM_MIP_COUNT - 1) + (BLOOM_MIP_COUNT - 1);
906 std::array<std::array<VkDescriptorSet, BLOOM_TRANSITION_COUNT>, MAX_FRAMES_IN_FLIGHT> m_bloomFilterSets = {};
907 std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_bloomCompositeSets = {};
908
909 VkSampler m_bloomSampler = VK_NULL_HANDLE; // linear clamp
910
911 /// Creates the HDR scene, bloom mip chain, passes, descriptors, pipelines, and TAA resources.
912 bool CreateBloomResources();
913 /// Destroys all bloom and HDR scene resources, then cleans up TAA resources.
914 void CleanupBloomResources();
915 /// Creates the HDR scene color image, view, and the shared bloom-source sampler.
916 bool CreateHDRSceneResources();
917 /// Creates the multi-mip bloom image and a per-mip image view.
918 bool CreateBloomImages();
919 /// Creates the bloom threshold/downsample, additive-upsample, and composite render passes.
920 bool CreateBloomRenderPasses();
921 /// Creates the per-mip bloom framebuffers and one composite framebuffer per swapchain image.
922 bool CreateBloomFramebuffers();
923 /// Compiles the bloom threshold/downsample/upsample/composite shaders and creates their pipelines.
924 bool CreateBloomPipelines();
925 /// Creates the bloom filter and composite descriptor layouts, pool, and sets.
926 bool CreateBloomDescriptorResources();
927 /// Runs the threshold, downsample, and additive-upsample bloom mip chain, or clears mip 0 when bloom is disabled.
928 void RenderBloomPass();
929 /// Tonemaps and composites the HDR scene, bloom, and SSR into the swapchain image, then draws ImGui.
930 void RenderBloomCompositePass();
931
932 /// Sets the dynamic viewport and scissor to fill the given extent. Defined in VulkanBloom.cpp (most call sites of the four post-effect TUs).
933 static void FillFullscreenViewportScissor(VkCommandBuffer cmd, VkExtent2D ext);
934};
935
936} // namespace RenderEngine
937} // namespace Sleak
938
939#endif // VULKANRENDERER_HPP
int width
int height
Backend-agnostic GPU buffer: vertex, index, constant, or resource view target.
Abstract interface for high-level graphics command execution and resource management.
Abstract render backend: swapchain, frame lifecycle, and post-effect toggles.
Definition Renderer.hpp:37
Backend-agnostic compiled shader program.
Definition Shader.hpp:11
virtual bool IsShadowPassActive() const override
virtual size_t GetGPUMemoryBudget() const override
Returns the device-local heap size reported by the allocator.
virtual RenderContext * GetContext() override
Returns the backend's command-recording interface.
virtual size_t GetGPUMemoryUsed() const override
Returns total bytes currently allocated by VulkanBuffer.
virtual bool IsInGeometryPass() const override
virtual bool IsDeferredEnabled() const override
virtual void FlushPendingTransfers() override
Kicks off the current frame's async buffer upload batch.
virtual void WaitIdle() override
Blocks until the device finishes all submitted GPU work.
virtual void Cleanup() override
Tears down every Vulkan resource in reverse dependency order.
virtual void EndRender() override
Ends the active render pass, submits the command buffer, and presents.
virtual void Resize(uint32_t width, uint32_t height) override
Recreates the swapchain for the new window dimensions.
virtual uint32_t GetFeatureCaps() const override
Feature capability mask; backends override with the audited truth.
Vulkan vertex+fragment shader pair, loaded from precompiled SPIR-V modules.
Vulkan 2D texture: image + view + sampler, with per-swapchain-image descriptor sets.
Backend-facing rendering layer shared by the four graphics backends.
RenderMode
Rasterizer fill style for a draw.
BufferType
GPU buffer usage kind, drives backend binding flags and layout.
RenderFace
Which triangle winding gets culled.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
uint32_t VertexFormatHandle
std::vector< VkSurfaceFormatKHR > formats
std::vector< VkPresentModeKHR > presentModes