SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
VulkanTAA.cpp
Go to the documentation of this file.
3
4#include <array>
5#include <cstring>
6#include <vector>
7#include "Core/Logger.hpp"
8
9namespace Sleak {
10 namespace RenderEngine {
11
12// ==================================================================
13// ======================= TAA ======================================
14// ==================================================================
15
16/// Creates the ping-pong TAA history images, render pass, framebuffers, descriptors, and pipeline.
17bool VulkanRenderer::CreateTAAResources() {
18 if (m_taaResourcesCreated) return true;
19
20 const VkFormat fmt = VK_FORMAT_R16G16B16A16_SFLOAT;
21
22 // ---- 1. Two ping-pong history images ----
23 for (int i = 0; i < 2; ++i) {
24 VkImageCreateInfo ic{};
25 ic.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
26 ic.imageType = VK_IMAGE_TYPE_2D;
27 ic.extent = { scExtent.width, scExtent.height, 1 };
28 ic.mipLevels = 1;
29 ic.arrayLayers = 1;
30 ic.format = fmt;
31 ic.tiling = VK_IMAGE_TILING_OPTIMAL;
32 ic.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
33 ic.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
34 | VK_IMAGE_USAGE_SAMPLED_BIT
35 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
36 | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
37 ic.samples = VK_SAMPLE_COUNT_1_BIT;
38 ic.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
39 if (vkCreateImage(device, &ic, nullptr, &m_taaImages[i]) != VK_SUCCESS) return false;
40
41 VkMemoryRequirements req;
42 vkGetImageMemoryRequirements(device, m_taaImages[i], &req);
43 VkMemoryAllocateInfo alloc{};
44 alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
45 alloc.allocationSize = req.size;
46 alloc.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
47 if (vkAllocateMemory(device, &alloc, nullptr, &m_taaMemory[i]) != VK_SUCCESS) return false;
48 vkBindImageMemory(device, m_taaImages[i], m_taaMemory[i], 0);
49
50 VkImageViewCreateInfo vi{};
51 vi.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
52 vi.image = m_taaImages[i];
53 vi.viewType = VK_IMAGE_VIEW_TYPE_2D;
54 vi.format = fmt;
55 vi.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
56 if (vkCreateImageView(device, &vi, nullptr, &m_taaViews[i]) != VK_SUCCESS) return false;
57 }
58
59 // ---- 2. Initialize both images to SHADER_READ_ONLY (cleared black) ----
60 {
61 VkCommandBufferAllocateInfo ca{};
62 ca.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
63 ca.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
64 ca.commandPool = commands;
65 ca.commandBufferCount = 1;
66 VkCommandBuffer initCmd;
67 vkAllocateCommandBuffers(device, &ca, &initCmd);
68
69 VkCommandBufferBeginInfo bi{};
70 bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
71 bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
72 vkBeginCommandBuffer(initCmd, &bi);
73
74 VkImageSubresourceRange sr = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
75 for (int i = 0; i < 2; ++i) {
76 VkImageMemoryBarrier bar{};
77 bar.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
78 bar.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
79 bar.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
80 bar.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
81 bar.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
82 bar.srcAccessMask = 0;
83 bar.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
84 bar.image = m_taaImages[i];
85 bar.subresourceRange = sr;
86 vkCmdPipelineBarrier(initCmd,
87 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
88 0, 0, nullptr, 0, nullptr, 1, &bar);
89 }
90 VkClearColorValue black{};
91 for (int i = 0; i < 2; ++i)
92 vkCmdClearColorImage(initCmd, m_taaImages[i],
93 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &sr);
94 for (int i = 0; i < 2; ++i) {
95 VkImageMemoryBarrier bar{};
96 bar.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
97 bar.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
98 bar.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
99 bar.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
100 bar.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
101 bar.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
102 bar.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
103 bar.image = m_taaImages[i];
104 bar.subresourceRange = sr;
105 vkCmdPipelineBarrier(initCmd,
106 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
107 0, 0, nullptr, 0, nullptr, 1, &bar);
108 }
109 vkEndCommandBuffer(initCmd);
110
111 VkSubmitInfo sub{};
112 sub.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
113 sub.commandBufferCount = 1;
114 sub.pCommandBuffers = &initCmd;
115 vkQueueSubmit(graphicsQueue, 1, &sub, VK_NULL_HANDLE);
116 vkQueueWaitIdle(graphicsQueue);
117 vkFreeCommandBuffers(device, commands, 1, &initCmd);
118 }
119
120 // ---- 3. Linear-clamp sampler ----
121 {
122 VkSamplerCreateInfo ss{};
123 ss.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
124 ss.magFilter = VK_FILTER_LINEAR;
125 ss.minFilter = VK_FILTER_LINEAR;
126 ss.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
127 ss.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
128 ss.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
129 ss.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
130 ss.minLod = 0.0f; ss.maxLod = 0.0f;
131 if (vkCreateSampler(device, &ss, nullptr, &m_taaSampler) != VK_SUCCESS) return false;
132 }
133
134 // ---- 4. Render pass (shared for both ping-pong targets) ----
135 {
136 VkAttachmentDescription att{};
137 att.format = fmt;
138 att.samples = VK_SAMPLE_COUNT_1_BIT;
139 att.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
140 att.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
141 att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
142 att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
143 att.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
144 att.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
145
146 VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
147 VkSubpassDescription sub{};
148 sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
149 sub.colorAttachmentCount = 1;
150 sub.pColorAttachments = &ref;
151
152 VkSubpassDependency dep{};
153 dep.srcSubpass = VK_SUBPASS_EXTERNAL;
154 dep.dstSubpass = 0;
155 dep.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
156 dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
157 dep.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
158 dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
159
160 VkRenderPassCreateInfo rpi{};
161 rpi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
162 rpi.attachmentCount = 1;
163 rpi.pAttachments = &att;
164 rpi.subpassCount = 1;
165 rpi.pSubpasses = &sub;
166 rpi.dependencyCount = 1;
167 rpi.pDependencies = &dep;
168 if (vkCreateRenderPass(device, &rpi, nullptr, &m_taaRenderPass) != VK_SUCCESS) return false;
169 }
170
171 // ---- 5. Two framebuffers (one per ping-pong target) ----
172 for (int i = 0; i < 2; ++i) {
173 VkFramebufferCreateInfo fi{};
174 fi.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
175 fi.renderPass = m_taaRenderPass;
176 fi.attachmentCount = 1;
177 fi.pAttachments = &m_taaViews[i];
178 fi.width = scExtent.width;
179 fi.height = scExtent.height;
180 fi.layers = 1;
181 if (vkCreateFramebuffer(device, &fi, nullptr, &m_taaFramebufs[i]) != VK_SUCCESS) return false;
182 }
183
184 // ---- 6. Descriptor set layouts ----
185 // set 0: 3 combined image samplers (currentTex, historyTex, gDepth)
186 {
187 std::array<VkDescriptorSetLayoutBinding, 3> binds{};
188 for (uint32_t b = 0; b < 3; ++b) {
189 binds[b].binding = b;
190 binds[b].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
191 binds[b].descriptorCount = 1;
192 binds[b].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
193 }
194 VkDescriptorSetLayoutCreateInfo li{};
195 li.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
196 li.bindingCount = static_cast<uint32_t>(binds.size());
197 li.pBindings = binds.data();
198 if (vkCreateDescriptorSetLayout(device, &li, nullptr, &m_taaInputDSL) != VK_SUCCESS) return false;
199 }
200 // set 1: UBO
201 {
202 VkDescriptorSetLayoutBinding b{};
203 b.binding = 0;
204 b.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
205 b.descriptorCount = 1;
206 b.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
207 VkDescriptorSetLayoutCreateInfo li{};
208 li.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
209 li.bindingCount = 1;
210 li.pBindings = &b;
211 if (vkCreateDescriptorSetLayout(device, &li, nullptr, &m_taaUboDSL) != VK_SUCCESS) return false;
212 }
213
214 // ---- 7. Descriptor pool + sets ----
215 {
216 std::array<VkDescriptorPoolSize, 2> ps{};
217 ps[0] = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3 * MAX_FRAMES_IN_FLIGHT };
218 ps[1] = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 * MAX_FRAMES_IN_FLIGHT };
219 VkDescriptorPoolCreateInfo pi{};
220 pi.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
221 pi.poolSizeCount = static_cast<uint32_t>(ps.size());
222 pi.pPoolSizes = ps.data();
223 pi.maxSets = 2 * MAX_FRAMES_IN_FLIGHT;
224 if (vkCreateDescriptorPool(device, &pi, nullptr, &m_taaPool) != VK_SUCCESS) return false;
225
226 for (uint32_t f = 0; f < MAX_FRAMES_IN_FLIGHT; ++f) {
227 VkDescriptorSetAllocateInfo ai{};
228 ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
229 ai.descriptorPool = m_taaPool;
230 ai.descriptorSetCount = 1;
231 ai.pSetLayouts = &m_taaInputDSL;
232 if (vkAllocateDescriptorSets(device, &ai, &m_taaInputSets[f]) != VK_SUCCESS) return false;
233
234 ai.pSetLayouts = &m_taaUboDSL;
235 if (vkAllocateDescriptorSets(device, &ai, &m_taaUboSets[f]) != VK_SUCCESS) return false;
236 }
237 }
238
239 // ---- 8. UBO buffers (HOST_VISIBLE | HOST_COHERENT) ----
240 {
241 const VkDeviceSize uboSize = sizeof(TAAParams);
242 VkBufferCreateInfo bi{};
243 bi.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
244 bi.size = uboSize;
245 bi.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
246 bi.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
247 for (uint32_t f = 0; f < MAX_FRAMES_IN_FLIGHT; ++f) {
248 if (vkCreateBuffer(device, &bi, nullptr, &m_taaUboBuffers[f]) != VK_SUCCESS) return false;
249 VkMemoryRequirements req;
250 vkGetBufferMemoryRequirements(device, m_taaUboBuffers[f], &req);
251 VkMemoryAllocateInfo alloc{};
252 alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
253 alloc.allocationSize = req.size;
254 alloc.memoryTypeIndex = FindMemoryType(req.memoryTypeBits,
255 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
256 if (vkAllocateMemory(device, &alloc, nullptr, &m_taaUboMemory[f]) != VK_SUCCESS) return false;
257 vkBindBufferMemory(device, m_taaUboBuffers[f], m_taaUboMemory[f], 0);
258 vkMapMemory(device, m_taaUboMemory[f], 0, uboSize, 0, &m_taaUboMapped[f]);
259 memset(m_taaUboMapped[f], 0, uboSize);
260
261 // Wire UBO descriptor
262 VkDescriptorBufferInfo dbi{ m_taaUboBuffers[f], 0, uboSize };
263 VkWriteDescriptorSet wr{};
264 wr.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
265 wr.dstSet = m_taaUboSets[f];
266 wr.dstBinding = 0;
267 wr.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
268 wr.descriptorCount = 1;
269 wr.pBufferInfo = &dbi;
270 vkUpdateDescriptorSets(device, 1, &wr, 0, nullptr);
271 }
272 }
273
274 // ---- 9. Pipeline layout ----
275 {
276 std::array<VkDescriptorSetLayout, 2> layouts{ m_taaInputDSL, m_taaUboDSL };
277 VkPipelineLayoutCreateInfo pli{};
278 pli.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
279 pli.setLayoutCount = static_cast<uint32_t>(layouts.size());
280 pli.pSetLayouts = layouts.data();
281 if (vkCreatePipelineLayout(device, &pli, nullptr, &m_taaPipelineLayout) != VK_SUCCESS) return false;
282 }
283
284 // ---- 10. Pipeline ----
285 {
286 m_taaShader = new VulkanShader(device);
287 if (!m_taaShader->compile("assets/shaders/taa.vert.spv",
288 "assets/shaders/taa.frag.spv")) {
289 SLEAK_ERROR("TAA: failed to compile shaders");
290 return false;
291 }
292
293 VkPipelineShaderStageCreateInfo stages[] = {
294 m_taaShader->GetVertexInfo(),
295 m_taaShader->GetFragInfo()
296 };
297 VkPipelineVertexInputStateCreateInfo vin{};
298 vin.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
299 VkPipelineInputAssemblyStateCreateInfo ia{};
300 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
301 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
302
303 std::vector<VkDynamicState> dynStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
304 VkPipelineDynamicStateCreateInfo ds{};
305 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
306 ds.dynamicStateCount = static_cast<uint32_t>(dynStates.size());
307 ds.pDynamicStates = dynStates.data();
308
309 VkPipelineViewportStateCreateInfo vps{};
310 vps.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
311 vps.viewportCount = 1; vps.scissorCount = 1;
312
313 VkPipelineRasterizationStateCreateInfo rs{};
314 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
315 rs.polygonMode = VK_POLYGON_MODE_FILL;
316 rs.cullMode = VK_CULL_MODE_NONE;
317 rs.lineWidth = 1.0f;
318
319 VkPipelineMultisampleStateCreateInfo ms{};
320 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
321 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
322
323 VkPipelineDepthStencilStateCreateInfo dss{};
324 dss.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
325
326 VkPipelineColorBlendAttachmentState blendAtt{};
327 blendAtt.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
328 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
329 VkPipelineColorBlendStateCreateInfo cb{};
330 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
331 cb.attachmentCount = 1;
332 cb.pAttachments = &blendAtt;
333
334 VkGraphicsPipelineCreateInfo gpi{};
335 gpi.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
336 gpi.stageCount = 2;
337 gpi.pStages = stages;
338 gpi.pVertexInputState = &vin;
339 gpi.pInputAssemblyState = &ia;
340 gpi.pViewportState = &vps;
341 gpi.pRasterizationState = &rs;
342 gpi.pMultisampleState = &ms;
343 gpi.pDepthStencilState = &dss;
344 gpi.pColorBlendState = &cb;
345 gpi.pDynamicState = &ds;
346 gpi.layout = m_taaPipelineLayout;
347 gpi.renderPass = m_taaRenderPass;
348 gpi.subpass = 0;
349 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpi, nullptr,
350 &m_taaPipeline) != VK_SUCCESS) {
351 SLEAK_ERROR("TAA: failed to create pipeline");
352 return false;
353 }
354 }
355
356 m_taaResourcesCreated = true;
357 SLEAK_INFO("TAA resources created ({}x{})", scExtent.width, scExtent.height);
358 return true;
359}
360
361/// Destroys the TAA pipeline, framebuffers, descriptors, images, and sampler.
362void VulkanRenderer::CleanupTAAResources() {
363 if (!m_taaResourcesCreated) return;
364
365 vkDeviceWaitIdle(device);
366
367 if (m_taaPipeline) { vkDestroyPipeline(device, m_taaPipeline, nullptr); m_taaPipeline = VK_NULL_HANDLE; }
368 if (m_taaPipelineLayout) { vkDestroyPipelineLayout(device, m_taaPipelineLayout, nullptr); m_taaPipelineLayout = VK_NULL_HANDLE; }
369 delete m_taaShader; m_taaShader = nullptr;
370
371 for (int i = 0; i < 2; ++i) {
372 if (m_taaFramebufs[i]) { vkDestroyFramebuffer(device, m_taaFramebufs[i], nullptr); m_taaFramebufs[i] = VK_NULL_HANDLE; }
373 }
374 if (m_taaRenderPass) { vkDestroyRenderPass(device, m_taaRenderPass, nullptr); m_taaRenderPass = VK_NULL_HANDLE; }
375
376 if (m_taaPool) { vkDestroyDescriptorPool(device, m_taaPool, nullptr); m_taaPool = VK_NULL_HANDLE; }
377 if (m_taaInputDSL) { vkDestroyDescriptorSetLayout(device, m_taaInputDSL, nullptr); m_taaInputDSL = VK_NULL_HANDLE; }
378 if (m_taaUboDSL) { vkDestroyDescriptorSetLayout(device, m_taaUboDSL, nullptr); m_taaUboDSL = VK_NULL_HANDLE; }
379
380 for (uint32_t f = 0; f < MAX_FRAMES_IN_FLIGHT; ++f) {
381 if (m_taaUboMapped[f]) { vkUnmapMemory(device, m_taaUboMemory[f]); m_taaUboMapped[f] = nullptr; }
382 if (m_taaUboBuffers[f]) { vkDestroyBuffer(device, m_taaUboBuffers[f], nullptr); m_taaUboBuffers[f] = VK_NULL_HANDLE; }
383 if (m_taaUboMemory[f]) { vkFreeMemory(device, m_taaUboMemory[f], nullptr); m_taaUboMemory[f] = VK_NULL_HANDLE; }
384 }
385
386 if (m_taaSampler) { vkDestroySampler(device, m_taaSampler, nullptr); m_taaSampler = VK_NULL_HANDLE; }
387
388 for (int i = 0; i < 2; ++i) {
389 if (m_taaViews[i]) { vkDestroyImageView(device, m_taaViews[i], nullptr); m_taaViews[i] = VK_NULL_HANDLE; }
390 if (m_taaImages[i]) { vkDestroyImage(device, m_taaImages[i], nullptr); m_taaImages[i] = VK_NULL_HANDLE; }
391 if (m_taaMemory[i]) { vkFreeMemory(device, m_taaMemory[i], nullptr); m_taaMemory[i] = VK_NULL_HANDLE; }
392 }
393
394 m_taaResourcesCreated = false;
395}
396
397/// Computes the inverse current view-projection and reprojection blend factor into the TAA UBO.
398void VulkanRenderer::UpdateTAAUBO() {
399 if (!m_taaResourcesCreated || !m_taaUboMapped[currentFrame]) return;
400
401 // VP = View * Projection (row-major)
402 float currentVP[16];
403 MatMul4(m_cachedView, m_cachedProjection, currentVP);
404
405 float invCurrentVP[16];
406 if (!InvertMat4(currentVP, invCurrentVP)) {
407 memset(invCurrentVP, 0, sizeof(invCurrentVP));
408 for (int i = 0; i < 4; ++i) invCurrentVP[i * 4 + i] = 1.0f; // identity fallback
409 }
410
411 TAAParams p{};
412 memcpy(p.InvCurrentVP, invCurrentVP, sizeof(p.InvCurrentVP));
413 memcpy(p.PrevVP, m_prevViewProj, sizeof(p.PrevVP));
414 p.ScreenW = static_cast<float>(scExtent.width);
415 p.ScreenH = static_cast<float>(scExtent.height);
416 // First two frames skip history (prev VP is zero-initialized)
417 p.BlendFactor = (m_taaFrameIdx <= 1) ? 1.0f : 0.1f;
418 p._pad = 0.0f;
419
420 memcpy(m_taaUboMapped[currentFrame], &p, sizeof(p));
421
422 // Save current VP as previous for next frame
423 memcpy(m_prevViewProj, currentVP, sizeof(m_prevViewProj));
424}
425
426/// Resolves the current frame against TAA history and copies the result back into the HDR scene image.
427void VulkanRenderer::RenderTAAPass() {
428 if (!m_taaResourcesCreated || !m_taaEnabled) return;
429
430 const uint32_t writeIdx = static_cast<uint32_t>(m_taaFrameIdx % 2);
431 const uint32_t readIdx = 1u - writeIdx;
432
433 // ---- 1. Depth barrier: DEPTH_STENCIL_ATTACHMENT → READ_ONLY ----
434 // (SSR will skip its own depth barrier since TAA already handles it)
435 {
436 VkImageMemoryBarrier depBar{};
437 depBar.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
438 depBar.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
439 depBar.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
440 depBar.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
441 depBar.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
442 depBar.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
443 depBar.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
444 depBar.image = depthImage;
445 depBar.subresourceRange = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 };
446 vkCmdPipelineBarrier(command,
447 VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
448 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
449 0, 0, nullptr, 0, nullptr, 1, &depBar);
450 }
451
452 // ---- 2. Transition write target: SHADER_READ_ONLY → COLOR_ATTACHMENT ----
453 {
454 VkImageMemoryBarrier bar{};
455 bar.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
456 bar.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
457 bar.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
458 bar.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
459 bar.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
460 bar.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
461 bar.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
462 bar.image = m_taaImages[writeIdx];
463 bar.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
464 vkCmdPipelineBarrier(command,
465 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
466 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
467 0, 0, nullptr, 0, nullptr, 1, &bar);
468 }
469
470 // ---- 3. Update input descriptors for this frame ----
471 {
472 // binding 0: currentTex = hdrScene (already SHADER_READ_ONLY from forward pass)
473 // binding 1: historyTex = taaImages[readIdx] (SHADER_READ_ONLY from init / prev frame)
474 // binding 2: gDepth (now DEPTH_STENCIL_READ_ONLY from step 1)
475 VkDescriptorImageInfo infos[3]{};
476 infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
477 infos[0].imageView = m_hdrSceneView;
478 infos[0].sampler = m_taaSampler;
479 infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
480 infos[1].imageView = m_taaViews[readIdx];
481 infos[1].sampler = m_taaSampler;
482 infos[2].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
483 infos[2].imageView = depthImageView;
484 infos[2].sampler = m_taaSampler;
485
486 VkWriteDescriptorSet writes[3]{};
487 for (int b = 0; b < 3; ++b) {
488 writes[b].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
489 writes[b].dstSet = m_taaInputSets[currentFrame];
490 writes[b].dstBinding = static_cast<uint32_t>(b);
491 writes[b].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
492 writes[b].descriptorCount = 1;
493 writes[b].pImageInfo = &infos[b];
494 }
495 vkUpdateDescriptorSets(device, 3, writes, 0, nullptr);
496 }
497
498 // ---- 4. Update UBO ----
499 UpdateTAAUBO();
500
501 // ---- 5. TAA render pass ----
502 {
503 VkRenderPassBeginInfo rp{};
504 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
505 rp.renderPass = m_taaRenderPass;
506 rp.framebuffer = m_taaFramebufs[writeIdx];
507 rp.renderArea.offset = { 0, 0 };
508 rp.renderArea.extent = scExtent;
509 rp.clearValueCount = 0;
510 vkCmdBeginRenderPass(command, &rp, VK_SUBPASS_CONTENTS_INLINE);
511 FillFullscreenViewportScissor(command, scExtent);
512 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_taaPipeline);
513 VkDescriptorSet sets[2] = { m_taaInputSets[currentFrame], m_taaUboSets[currentFrame] };
514 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
515 m_taaPipelineLayout, 0, 2, sets, 0, nullptr);
516 vkCmdDraw(command, 3, 1, 0, 0);
517 vkCmdEndRenderPass(command);
518 // taaImages[writeIdx] is now SHADER_READ_ONLY_OPTIMAL (render pass finalLayout)
519 }
520
521 // ---- 6. Copy TAA resolve → hdrScene ----
522 // Both images are SHADER_READ_ONLY; transition for transfer then restore.
523 {
524 VkImageMemoryBarrier toTransfer[2]{};
525 toTransfer[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
526 toTransfer[0].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
527 toTransfer[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
528 toTransfer[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
529 toTransfer[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
530 toTransfer[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
531 toTransfer[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
532 toTransfer[0].image = m_taaImages[writeIdx];
533 toTransfer[0].subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
534
535 toTransfer[1] = toTransfer[0];
536 toTransfer[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
537 toTransfer[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
538 toTransfer[1].image = m_hdrSceneImage;
539
540 vkCmdPipelineBarrier(command,
541 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
542 0, 0, nullptr, 0, nullptr, 2, toTransfer);
543
544 VkImageCopy region{};
545 region.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
546 region.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
547 region.extent = { scExtent.width, scExtent.height, 1 };
548 vkCmdCopyImage(command,
549 m_taaImages[writeIdx], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
550 m_hdrSceneImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
551 1, &region);
552
553 VkImageMemoryBarrier toRead[2]{};
554 toRead[0] = toTransfer[0];
555 toRead[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
556 toRead[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
557 toRead[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
558 toRead[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
559
560 toRead[1] = toRead[0];
561 toRead[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
562 toRead[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
563 toRead[1].image = m_hdrSceneImage;
564
565 vkCmdPipelineBarrier(command,
566 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
567 0, 0, nullptr, 0, nullptr, 2, toRead);
568 }
569
570 // Advance ping-pong for next frame
571 m_taaFrameIdx++;
572}
573
574} // namespace RenderEngine
575} // namespace Sleak
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
#define SLEAK_INFO(...)
Definition Logger.hpp:20
Backend-facing rendering layer shared by the four graphics backends.
bool InvertMat4(const float M[16], float out[16])
Row-major 4x4 inverse via Gauss-Jordan elimination.
void MatMul4(const float A[16], const float B[16], float C[16])
Row-major mat4 multiply: C = A * B.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10