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