summaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
authorGravatar Liam2022-12-08 16:52:29 -0500
committerGravatar Liam2022-12-08 17:17:45 -0500
commit5b837157bde2115cf4781c5353f598eb6f0f7109 (patch)
treee69496d0ae0191642de95cd2c27585276cc3669e /src/video_core/renderer_vulkan
parentMerge pull request #9401 from vonchenplus/draw_manager (diff)
downloadyuzu-5b837157bde2115cf4781c5353f598eb6f0f7109.tar.gz
yuzu-5b837157bde2115cf4781c5353f598eb6f0f7109.tar.xz
yuzu-5b837157bde2115cf4781c5353f598eb6f0f7109.zip
video_core: Integrate SMAA
Co-authored-by: goldenx86 <goldenx86@users.noreply.github.com> Co-authored-by: BreadFish64 <breadfish64@users.noreply.github.com>
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.cpp761
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.h86
5 files changed, 873 insertions, 2 deletions
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 6e5abade4..2f0cc27e8 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -29,6 +29,7 @@
29#include "video_core/renderer_vulkan/vk_fsr.h" 29#include "video_core/renderer_vulkan/vk_fsr.h"
30#include "video_core/renderer_vulkan/vk_scheduler.h" 30#include "video_core/renderer_vulkan/vk_scheduler.h"
31#include "video_core/renderer_vulkan/vk_shader_util.h" 31#include "video_core/renderer_vulkan/vk_shader_util.h"
32#include "video_core/renderer_vulkan/vk_smaa.h"
32#include "video_core/renderer_vulkan/vk_swapchain.h" 33#include "video_core/renderer_vulkan/vk_swapchain.h"
33#include "video_core/surface.h" 34#include "video_core/surface.h"
34#include "video_core/textures/decoders.h" 35#include "video_core/textures/decoders.h"
@@ -156,6 +157,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
156 scheduler.Wait(resource_ticks[image_index]); 157 scheduler.Wait(resource_ticks[image_index]);
157 resource_ticks[image_index] = scheduler.CurrentTick(); 158 resource_ticks[image_index] = scheduler.CurrentTick();
158 159
160 VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index];
159 VkImageView source_image_view = 161 VkImageView source_image_view =
160 use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; 162 use_accelerated ? screen_info.image_view : *raw_image_views[image_index];
161 163
@@ -242,7 +244,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
242 } 244 }
243 245
244 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 246 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue();
245 if (use_accelerated && anti_alias_pass != Settings::AntiAliasing::None) { 247 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) {
246 UpdateAADescriptorSet(image_index, source_image_view, false); 248 UpdateAADescriptorSet(image_index, source_image_view, false);
247 const u32 up_scale = Settings::values.resolution_info.up_scale; 249 const u32 up_scale = Settings::values.resolution_info.up_scale;
248 const u32 down_shift = Settings::values.resolution_info.down_shift; 250 const u32 down_shift = Settings::values.resolution_info.down_shift;
@@ -340,7 +342,18 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
340 }); 342 });
341 source_image_view = *aa_image_view; 343 source_image_view = *aa_image_view;
342 } 344 }
343 345 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) {
346 if (!smaa) {
347 const u32 up_scale = Settings::values.resolution_info.up_scale;
348 const u32 down_shift = Settings::values.resolution_info.down_shift;
349 const VkExtent2D smaa_size{
350 .width = (up_scale * framebuffer.width) >> down_shift,
351 .height = (up_scale * framebuffer.height) >> down_shift,
352 };
353 CreateSMAA(smaa_size);
354 }
355 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
356 }
344 if (fsr) { 357 if (fsr) {
345 auto crop_rect = framebuffer.crop_rect; 358 auto crop_rect = framebuffer.crop_rect;
346 if (crop_rect.GetWidth() == 0) { 359 if (crop_rect.GetWidth() == 0) {
@@ -467,6 +480,7 @@ void BlitScreen::CreateDynamicResources() {
467 CreateFramebuffers(); 480 CreateFramebuffers();
468 CreateGraphicsPipeline(); 481 CreateGraphicsPipeline();
469 fsr.reset(); 482 fsr.reset();
483 smaa.reset();
470 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { 484 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
471 CreateFSR(); 485 CreateFSR();
472 } 486 }
@@ -490,6 +504,7 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
490 raw_height = framebuffer.height; 504 raw_height = framebuffer.height;
491 pixel_format = framebuffer.pixel_format; 505 pixel_format = framebuffer.pixel_format;
492 506
507 smaa.reset();
493 ReleaseRawImages(); 508 ReleaseRawImages();
494 509
495 CreateStagingBuffer(framebuffer); 510 CreateStagingBuffer(framebuffer);
@@ -1448,6 +1463,10 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig&
1448 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v); 1463 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v);
1449} 1464}
1450 1465
1466void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {
1467 smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size);
1468}
1469
1451void BlitScreen::CreateFSR() { 1470void BlitScreen::CreateFSR() {
1452 const auto& layout = render_window.GetFramebufferLayout(); 1471 const auto& layout = render_window.GetFramebufferLayout();
1453 const VkExtent2D fsr_size{ 1472 const VkExtent2D fsr_size{
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index a2b73ec54..ebe10b08b 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -40,9 +40,11 @@ class Device;
40class FSR; 40class FSR;
41class RasterizerVulkan; 41class RasterizerVulkan;
42class Scheduler; 42class Scheduler;
43class SMAA;
43class Swapchain; 44class Swapchain;
44 45
45struct ScreenInfo { 46struct ScreenInfo {
47 VkImage image{};
46 VkImageView image_view{}; 48 VkImageView image_view{};
47 u32 width{}; 49 u32 width{};
48 u32 height{}; 50 u32 height{};
@@ -101,6 +103,7 @@ private:
101 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, 103 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
102 const Layout::FramebufferLayout layout) const; 104 const Layout::FramebufferLayout layout) const;
103 105
106 void CreateSMAA(VkExtent2D smaa_size);
104 void CreateFSR(); 107 void CreateFSR();
105 108
106 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 109 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
@@ -163,6 +166,7 @@ private:
163 Service::android::PixelFormat pixel_format{}; 166 Service::android::PixelFormat pixel_format{};
164 167
165 std::unique_ptr<FSR> fsr; 168 std::unique_ptr<FSR> fsr;
169 std::unique_ptr<SMAA> smaa;
166}; 170};
167 171
168} // namespace Vulkan 172} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 8d7a5e400..823b9ca2a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -583,6 +583,7 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
583 if (!image_view) { 583 if (!image_view) {
584 return false; 584 return false;
585 } 585 }
586 screen_info.image = image_view->ImageHandle();
586 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); 587 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D);
587 screen_info.width = image_view->size.width; 588 screen_info.width = image_view->size.width;
588 screen_info.height = image_view->size.height; 589 screen_info.height = image_view->size.height;
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp
new file mode 100644
index 000000000..8eb735489
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_smaa.cpp
@@ -0,0 +1,761 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <list>
5
6#include "common/assert.h"
7#include "common/polyfill_ranges.h"
8
9#include "video_core/renderer_vulkan/vk_scheduler.h"
10#include "video_core/renderer_vulkan/vk_shader_util.h"
11#include "video_core/renderer_vulkan/vk_smaa.h"
12#include "video_core/smaa_area_tex.h"
13#include "video_core/smaa_search_tex.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15
16#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h"
17#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h"
18#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h"
19#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h"
20#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h"
21#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h"
22
23namespace Vulkan {
24namespace {
25
26#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0])))
27
28std::pair<vk::Image, MemoryCommit> CreateWrappedImage(const Device& device,
29 MemoryAllocator& allocator,
30 VkExtent2D dimensions, VkFormat format) {
31 const VkImageCreateInfo image_ci{
32 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
33 .pNext = nullptr,
34 .flags = 0,
35 .imageType = VK_IMAGE_TYPE_2D,
36 .format = format,
37 .extent = {.width = dimensions.width, .height = dimensions.height, .depth = 1},
38 .mipLevels = 1,
39 .arrayLayers = 1,
40 .samples = VK_SAMPLE_COUNT_1_BIT,
41 .tiling = VK_IMAGE_TILING_OPTIMAL,
42 .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
43 VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
44 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
45 .queueFamilyIndexCount = 0,
46 .pQueueFamilyIndices = nullptr,
47 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
48 };
49
50 auto image = device.GetLogical().CreateImage(image_ci);
51 auto commit = allocator.Commit(image, Vulkan::MemoryUsage::DeviceLocal);
52
53 return std::make_pair(std::move(image), std::move(commit));
54}
55
56void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
57 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
58 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
59 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
60 const VkImageMemoryBarrier barrier{
61 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
62 .pNext = nullptr,
63 .srcAccessMask = flags,
64 .dstAccessMask = flags,
65 .oldLayout = source_layout,
66 .newLayout = target_layout,
67 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
68 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
69 .image = image,
70 .subresourceRange{
71 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
72 .baseMipLevel = 0,
73 .levelCount = 1,
74 .baseArrayLayer = 0,
75 .layerCount = 1,
76 },
77 };
78 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
79 0, barrier);
80}
81
82void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
83 vk::Image& image, VkExtent2D dimensions, VkFormat format,
84 std::span<const u8> initial_contents = {}) {
85 auto upload_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
86 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
87 .pNext = nullptr,
88 .flags = 0,
89 .size = initial_contents.size_bytes(),
90 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
91 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
92 .queueFamilyIndexCount = 0,
93 .pQueueFamilyIndices = nullptr,
94 });
95 auto upload_commit = allocator.Commit(upload_buffer, MemoryUsage::Upload);
96 std::ranges::copy(initial_contents, upload_commit.Map().begin());
97
98 const std::array<VkBufferImageCopy, 1> regions{{{
99 .bufferOffset = 0,
100 .bufferRowLength = dimensions.width,
101 .bufferImageHeight = dimensions.height,
102 .imageSubresource{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
103 .mipLevel = 0,
104 .baseArrayLayer = 0,
105 .layerCount = 1},
106 .imageOffset{},
107 .imageExtent{.width = dimensions.width, .height = dimensions.height, .depth = 1},
108 }}};
109
110 scheduler.RequestOutsideRenderPassOperationContext();
111 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
112 TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
113 VK_IMAGE_LAYOUT_UNDEFINED);
114 cmdbuf.CopyBufferToImage(*upload_buffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
115 regions);
116 TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_GENERAL,
117 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
118 });
119 scheduler.Finish();
120
121 // This should go out of scope before the commit
122 auto upload_buffer2 = std::move(upload_buffer);
123}
124
125vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
126 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
127 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
128 .pNext = nullptr,
129 .flags = 0,
130 .image = *image,
131 .viewType = VK_IMAGE_VIEW_TYPE_2D,
132 .format = format,
133 .components{},
134 .subresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
135 .baseMipLevel = 0,
136 .levelCount = 1,
137 .baseArrayLayer = 0,
138 .layerCount = 1},
139 });
140}
141
142vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) {
143 const VkAttachmentDescription attachment{
144 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
145 .format = format,
146 .samples = VK_SAMPLE_COUNT_1_BIT,
147 .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
148 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
149 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
150 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
151 .initialLayout = VK_IMAGE_LAYOUT_GENERAL,
152 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
153 };
154
155 constexpr VkAttachmentReference color_attachment_ref{
156 .attachment = 0,
157 .layout = VK_IMAGE_LAYOUT_GENERAL,
158 };
159
160 const VkSubpassDescription subpass_description{
161 .flags = 0,
162 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
163 .inputAttachmentCount = 0,
164 .pInputAttachments = nullptr,
165 .colorAttachmentCount = 1,
166 .pColorAttachments = &color_attachment_ref,
167 .pResolveAttachments = nullptr,
168 .pDepthStencilAttachment = nullptr,
169 .preserveAttachmentCount = 0,
170 .pPreserveAttachments = nullptr,
171 };
172
173 constexpr VkSubpassDependency dependency{
174 .srcSubpass = VK_SUBPASS_EXTERNAL,
175 .dstSubpass = 0,
176 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
177 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
178 .srcAccessMask = 0,
179 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
180 .dependencyFlags = 0,
181 };
182
183 return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{
184 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
185 .pNext = nullptr,
186 .flags = 0,
187 .attachmentCount = 1,
188 .pAttachments = &attachment,
189 .subpassCount = 1,
190 .pSubpasses = &subpass_description,
191 .dependencyCount = 1,
192 .pDependencies = &dependency,
193 });
194}
195
196vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass,
197 vk::ImageView& dest_image, VkExtent2D extent) {
198 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
199 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
200 .pNext = nullptr,
201 .flags = 0,
202 .renderPass = *render_pass,
203 .attachmentCount = 1,
204 .pAttachments = dest_image.address(),
205 .width = extent.width,
206 .height = extent.height,
207 .layers = 1,
208 });
209}
210
211vk::Sampler CreateWrappedSampler(const Device& device) {
212 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
213 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
214 .pNext = nullptr,
215 .flags = 0,
216 .magFilter = VK_FILTER_LINEAR,
217 .minFilter = VK_FILTER_LINEAR,
218 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
219 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
220 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
221 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
222 .mipLodBias = 0.0f,
223 .anisotropyEnable = VK_FALSE,
224 .maxAnisotropy = 0.0f,
225 .compareEnable = VK_FALSE,
226 .compareOp = VK_COMPARE_OP_NEVER,
227 .minLod = 0.0f,
228 .maxLod = 0.0f,
229 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
230 .unnormalizedCoordinates = VK_FALSE,
231 });
232}
233
234vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code) {
235 return device.GetLogical().CreateShaderModule(VkShaderModuleCreateInfo{
236 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
237 .pNext = nullptr,
238 .flags = 0,
239 .codeSize = code.size_bytes(),
240 .pCode = code.data(),
241 });
242}
243
244vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors,
245 u32 max_sets) {
246 const VkDescriptorPoolSize pool_size{
247 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
248 .descriptorCount = static_cast<u32>(max_descriptors),
249 };
250
251 return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{
252 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
253 .pNext = nullptr,
254 .flags = 0,
255 .maxSets = max_sets,
256 .poolSizeCount = 1,
257 .pPoolSizes = &pool_size,
258 });
259}
260
261vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device,
262 u32 max_sampler_bindings) {
263 std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings);
264 for (u32 i = 0; i < max_sampler_bindings; i++) {
265 bindings[i] = {
266 .binding = i,
267 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
268 .descriptorCount = 1,
269 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
270 .pImmutableSamplers = nullptr,
271 };
272 }
273
274 return device.GetLogical().CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{
275 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
276 .pNext = nullptr,
277 .flags = 0,
278 .bindingCount = static_cast<u32>(bindings.size()),
279 .pBindings = bindings.data(),
280 });
281}
282
283vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool,
284 vk::Span<VkDescriptorSetLayout> layouts) {
285 return pool.Allocate(VkDescriptorSetAllocateInfo{
286 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
287 .pNext = nullptr,
288 .descriptorPool = *pool,
289 .descriptorSetCount = layouts.size(),
290 .pSetLayouts = layouts.data(),
291 });
292}
293
294vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
295 vk::DescriptorSetLayout& layout) {
296 return device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{
297 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
298 .pNext = nullptr,
299 .flags = 0,
300 .setLayoutCount = 1,
301 .pSetLayouts = layout.address(),
302 .pushConstantRangeCount = 0,
303 .pPushConstantRanges = nullptr,
304 });
305}
306
307vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
308 vk::PipelineLayout& layout,
309 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
310 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
311 {
312 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
313 .pNext = nullptr,
314 .flags = 0,
315 .stage = VK_SHADER_STAGE_VERTEX_BIT,
316 .module = *std::get<0>(shaders),
317 .pName = "main",
318 .pSpecializationInfo = nullptr,
319 },
320 {
321 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
322 .pNext = nullptr,
323 .flags = 0,
324 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
325 .module = *std::get<1>(shaders),
326 .pName = "main",
327 .pSpecializationInfo = nullptr,
328 },
329 }};
330
331 constexpr VkPipelineVertexInputStateCreateInfo vertex_input_ci{
332 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
333 .pNext = nullptr,
334 .flags = 0,
335 .vertexBindingDescriptionCount = 0,
336 .pVertexBindingDescriptions = nullptr,
337 .vertexAttributeDescriptionCount = 0,
338 .pVertexAttributeDescriptions = nullptr,
339 };
340
341 constexpr VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
342 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
343 .pNext = nullptr,
344 .flags = 0,
345 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
346 .primitiveRestartEnable = VK_FALSE,
347 };
348
349 constexpr VkPipelineViewportStateCreateInfo viewport_state_ci{
350 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
351 .pNext = nullptr,
352 .flags = 0,
353 .viewportCount = 1,
354 .pViewports = nullptr,
355 .scissorCount = 1,
356 .pScissors = nullptr,
357 };
358
359 constexpr VkPipelineRasterizationStateCreateInfo rasterization_ci{
360 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
361 .pNext = nullptr,
362 .flags = 0,
363 .depthClampEnable = VK_FALSE,
364 .rasterizerDiscardEnable = VK_FALSE,
365 .polygonMode = VK_POLYGON_MODE_FILL,
366 .cullMode = VK_CULL_MODE_NONE,
367 .frontFace = VK_FRONT_FACE_CLOCKWISE,
368 .depthBiasEnable = VK_FALSE,
369 .depthBiasConstantFactor = 0.0f,
370 .depthBiasClamp = 0.0f,
371 .depthBiasSlopeFactor = 0.0f,
372 .lineWidth = 1.0f,
373 };
374
375 constexpr VkPipelineMultisampleStateCreateInfo multisampling_ci{
376 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
377 .pNext = nullptr,
378 .flags = 0,
379 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
380 .sampleShadingEnable = VK_FALSE,
381 .minSampleShading = 0.0f,
382 .pSampleMask = nullptr,
383 .alphaToCoverageEnable = VK_FALSE,
384 .alphaToOneEnable = VK_FALSE,
385 };
386
387 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{
388 .blendEnable = VK_FALSE,
389 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
390 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
391 .colorBlendOp = VK_BLEND_OP_ADD,
392 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
393 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
394 .alphaBlendOp = VK_BLEND_OP_ADD,
395 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
396 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
397 };
398
399 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
400 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
401 .pNext = nullptr,
402 .flags = 0,
403 .logicOpEnable = VK_FALSE,
404 .logicOp = VK_LOGIC_OP_COPY,
405 .attachmentCount = 1,
406 .pAttachments = &color_blend_attachment,
407 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
408 };
409
410 constexpr std::array dynamic_states{
411 VK_DYNAMIC_STATE_VIEWPORT,
412 VK_DYNAMIC_STATE_SCISSOR,
413 };
414
415 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
416 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
417 .pNext = nullptr,
418 .flags = 0,
419 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
420 .pDynamicStates = dynamic_states.data(),
421 };
422
423 return device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
424 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
425 .pNext = nullptr,
426 .flags = 0,
427 .stageCount = static_cast<u32>(shader_stages.size()),
428 .pStages = shader_stages.data(),
429 .pVertexInputState = &vertex_input_ci,
430 .pInputAssemblyState = &input_assembly_ci,
431 .pTessellationState = nullptr,
432 .pViewportState = &viewport_state_ci,
433 .pRasterizationState = &rasterization_ci,
434 .pMultisampleState = &multisampling_ci,
435 .pDepthStencilState = nullptr,
436 .pColorBlendState = &color_blend_ci,
437 .pDynamicState = &dynamic_state_ci,
438 .layout = *layout,
439 .renderPass = *renderpass,
440 .subpass = 0,
441 .basePipelineHandle = 0,
442 .basePipelineIndex = 0,
443 });
444}
445
446VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
447 VkSampler sampler, VkImageView view,
448 VkDescriptorSet set, u32 binding) {
449 ASSERT(images.capacity() > images.size());
450 auto& image_info = images.emplace_back(VkDescriptorImageInfo{
451 .sampler = sampler,
452 .imageView = view,
453 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
454 });
455
456 return VkWriteDescriptorSet{
457 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
458 .pNext = nullptr,
459 .dstSet = set,
460 .dstBinding = binding,
461 .dstArrayElement = 0,
462 .descriptorCount = 1,
463 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
464 .pImageInfo = &image_info,
465 .pBufferInfo = nullptr,
466 .pTexelBufferView = nullptr,
467 };
468}
469
470void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
471 constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
472 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
473 .baseMipLevel = 0,
474 .levelCount = 1,
475 .baseArrayLayer = 0,
476 .layerCount = 1,
477 }}};
478 TransitionImageLayout(cmdbuf, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_UNDEFINED);
479 cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources);
480}
481
482void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass,
483 VkFramebuffer framebuffer, VkExtent2D extent) {
484 const VkRenderPassBeginInfo renderpass_bi{
485 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
486 .pNext = nullptr,
487 .renderPass = *render_pass,
488 .framebuffer = framebuffer,
489 .renderArea{
490 .offset{},
491 .extent = extent,
492 },
493 .clearValueCount = 0,
494 .pClearValues = nullptr,
495 };
496 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
497
498 const VkViewport viewport{
499 .x = 0.0f,
500 .y = 0.0f,
501 .width = static_cast<float>(extent.width),
502 .height = static_cast<float>(extent.height),
503 .minDepth = 0.0f,
504 .maxDepth = 1.0f,
505 };
506 const VkRect2D scissor{
507 .offset = {0, 0},
508 .extent = extent,
509 };
510 cmdbuf.SetViewport(0, viewport);
511 cmdbuf.SetScissor(0, scissor);
512}
513
514} // Anonymous namespace
515
516SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
517 : m_device(device), m_allocator(allocator), m_extent(extent),
518 m_image_count(static_cast<u32>(image_count)) {
519 CreateImages();
520 CreateRenderPasses();
521 CreateSampler();
522 CreateShaders();
523 CreateDescriptorPool();
524 CreateDescriptorSetLayouts();
525 CreateDescriptorSets();
526 CreatePipelineLayouts();
527 CreatePipelines();
528}
529
530void SMAA::CreateImages() {
531 constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
532 constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
533
534 std::tie(m_static_images[Area], m_static_buffer_commits[Area]) =
535 CreateWrappedImage(m_device, m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
536 std::tie(m_static_images[Search], m_static_buffer_commits[Search]) =
537 CreateWrappedImage(m_device, m_allocator, search_extent, VK_FORMAT_R8_UNORM);
538
539 m_static_image_views[Area] =
540 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
541 m_static_image_views[Search] =
542 CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM);
543
544 for (u32 i = 0; i < m_image_count; i++) {
545 Images& images = m_dynamic_images.emplace_back();
546
547 std::tie(images.images[Blend], images.buffer_commits[Blend]) =
548 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
549 std::tie(images.images[Edges], images.buffer_commits[Edges]) =
550 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
551 std::tie(images.images[Output], images.buffer_commits[Output]) =
552 CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
553
554 images.image_views[Blend] =
555 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
556 images.image_views[Edges] =
557 CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT);
558 images.image_views[Output] =
559 CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT);
560 }
561}
562
563void SMAA::CreateRenderPasses() {
564 m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT);
565 m_renderpasses[BlendingWeightCalculation] =
566 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
567 m_renderpasses[NeighborhoodBlending] =
568 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
569
570 for (auto& images : m_dynamic_images) {
571 images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer(
572 m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent);
573
574 images.framebuffers[BlendingWeightCalculation] =
575 CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation],
576 images.image_views[Blend], m_extent);
577
578 images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer(
579 m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent);
580 }
581}
582
583void SMAA::CreateSampler() {
584 m_sampler = CreateWrappedSampler(m_device);
585}
586
587void SMAA::CreateShaders() {
588 // These match the order of the SMAAStage enum
589 constexpr std::array vert_shader_sources{
590 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
591 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
592 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
593 };
594 constexpr std::array frag_shader_sources{
595 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
596 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
597 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
598 };
599
600 for (size_t i = 0; i < MaxSMAAStage; i++) {
601 m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]);
602 m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]);
603 }
604}
605
606void SMAA::CreateDescriptorPool() {
607 // Edge detection: 1 descriptor
608 // Blending weight calculation: 3 descriptors
609 // Neighborhood blending: 2 descriptors
610
611 // 6 descriptors, 3 descriptor sets per image
612 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count);
613}
614
615void SMAA::CreateDescriptorSetLayouts() {
616 m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1);
617 m_descriptor_set_layouts[BlendingWeightCalculation] =
618 CreateWrappedDescriptorSetLayout(m_device, 3);
619 m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2);
620}
621
622void SMAA::CreateDescriptorSets() {
623 std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size());
624 std::ranges::transform(m_descriptor_set_layouts, layouts.begin(),
625 [](auto& layout) { return *layout; });
626
627 for (auto& images : m_dynamic_images) {
628 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
629 }
630}
631
632void SMAA::CreatePipelineLayouts() {
633 for (size_t i = 0; i < MaxSMAAStage; i++) {
634 m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]);
635 }
636}
637
638void SMAA::CreatePipelines() {
639 for (size_t i = 0; i < MaxSMAAStage; i++) {
640 m_pipelines[i] =
641 CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i],
642 std::tie(m_vertex_shaders[i], m_fragment_shaders[i]));
643 }
644}
645
646void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
647 Images& images = m_dynamic_images[image_index];
648 std::vector<VkDescriptorImageInfo> image_infos;
649 std::vector<VkWriteDescriptorSet> updates;
650 image_infos.reserve(6);
651
652 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
653 images.descriptor_sets[EdgeDetection], 0));
654
655 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges],
656 images.descriptor_sets[BlendingWeightCalculation],
657 0));
658 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area],
659 images.descriptor_sets[BlendingWeightCalculation],
660 1));
661 updates.push_back(
662 CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search],
663 images.descriptor_sets[BlendingWeightCalculation], 2));
664
665 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
666 images.descriptor_sets[NeighborhoodBlending], 0));
667 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend],
668 images.descriptor_sets[NeighborhoodBlending], 1));
669
670 m_device.GetLogical().UpdateDescriptorSets(updates, {});
671}
672
673void SMAA::UploadImages(Scheduler& scheduler) {
674 if (m_images_ready) {
675 return;
676 }
677
678 constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
679 constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
680
681 UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
682 VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
683 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
684 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
685
686 scheduler.Record([&](vk::CommandBuffer& cmdbuf) {
687 for (auto& images : m_dynamic_images) {
688 for (size_t i = 0; i < MaxDynamicImage; i++) {
689 ClearColorImage(cmdbuf, *images.images[i]);
690 }
691 }
692 });
693 scheduler.Finish();
694
695 m_images_ready = true;
696}
697
698VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
699 VkImageView source_image_view) {
700 Images& images = m_dynamic_images[image_index];
701
702 VkImage output_image = *images.images[Output];
703 VkImage edges_image = *images.images[Edges];
704 VkImage blend_image = *images.images[Blend];
705
706 VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection];
707 VkDescriptorSet blending_weight_calculation_descriptor_set =
708 images.descriptor_sets[BlendingWeightCalculation];
709 VkDescriptorSet neighborhood_blending_descriptor_set =
710 images.descriptor_sets[NeighborhoodBlending];
711
712 VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection];
713 VkFramebuffer blending_weight_calculation_framebuffer =
714 *images.framebuffers[BlendingWeightCalculation];
715 VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending];
716
717 UploadImages(scheduler);
718 UpdateDescriptorSets(source_image_view, image_index);
719
720 scheduler.RequestOutsideRenderPassOperationContext();
721 scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) {
722 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
723 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
724 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,
725 m_extent);
726 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
727 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
728 *m_pipeline_layouts[EdgeDetection], 0,
729 edge_detection_descriptor_set, {});
730 cmdbuf.Draw(3, 1, 0, 0);
731 cmdbuf.EndRenderPass();
732
733 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
734 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
735 BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation],
736 blending_weight_calculation_framebuffer, m_extent);
737 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
738 *m_pipelines[BlendingWeightCalculation]);
739 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
740 *m_pipeline_layouts[BlendingWeightCalculation], 0,
741 blending_weight_calculation_descriptor_set, {});
742 cmdbuf.Draw(3, 1, 0, 0);
743 cmdbuf.EndRenderPass();
744
745 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
746 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
747 BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending],
748 neighborhood_blending_framebuffer, m_extent);
749 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
750 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
751 *m_pipeline_layouts[NeighborhoodBlending], 0,
752 neighborhood_blending_descriptor_set, {});
753 cmdbuf.Draw(3, 1, 0, 0);
754 cmdbuf.EndRenderPass();
755 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
756 });
757
758 return *images.image_views[Output];
759}
760
761} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/vk_smaa.h
new file mode 100644
index 000000000..99a369148
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_smaa.h
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14class StagingBufferPool;
15
16class SMAA {
17public:
18 explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
19 VkExtent2D extent);
20 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
21 VkImageView source_image_view);
22
23private:
24 enum SMAAStage {
25 EdgeDetection = 0,
26 BlendingWeightCalculation = 1,
27 NeighborhoodBlending = 2,
28 MaxSMAAStage = 3,
29 };
30
31 enum StaticImageType {
32 Area = 0,
33 Search = 1,
34 MaxStaticImage = 2,
35 };
36
37 enum DynamicImageType {
38 Blend = 0,
39 Edges = 1,
40 Output = 2,
41 MaxDynamicImage = 3,
42 };
43
44 void CreateImages();
45 void CreateRenderPasses();
46 void CreateSampler();
47 void CreateShaders();
48 void CreateDescriptorPool();
49 void CreateDescriptorSetLayouts();
50 void CreateDescriptorSets();
51 void CreatePipelineLayouts();
52 void CreatePipelines();
53 void UpdateDescriptorSets(VkImageView image_view, size_t image_index);
54 void UploadImages(Scheduler& scheduler);
55
56 const Device& m_device;
57 MemoryAllocator& m_allocator;
58 const VkExtent2D m_extent;
59 const u32 m_image_count;
60
61 vk::DescriptorPool m_descriptor_pool{};
62 std::array<vk::DescriptorSetLayout, MaxSMAAStage> m_descriptor_set_layouts{};
63 std::array<vk::PipelineLayout, MaxSMAAStage> m_pipeline_layouts{};
64 std::array<vk::ShaderModule, MaxSMAAStage> m_vertex_shaders{};
65 std::array<vk::ShaderModule, MaxSMAAStage> m_fragment_shaders{};
66 std::array<vk::Pipeline, MaxSMAAStage> m_pipelines{};
67 std::array<vk::RenderPass, MaxSMAAStage> m_renderpasses{};
68
69 std::array<MemoryCommit, MaxStaticImage> m_static_buffer_commits;
70 std::array<vk::Image, MaxStaticImage> m_static_images{};
71 std::array<vk::ImageView, MaxStaticImage> m_static_image_views{};
72
73 struct Images {
74 vk::DescriptorSets descriptor_sets{};
75 std::array<MemoryCommit, MaxDynamicImage> buffer_commits;
76 std::array<vk::Image, MaxDynamicImage> images{};
77 std::array<vk::ImageView, MaxDynamicImage> image_views{};
78 std::array<vk::Framebuffer, MaxSMAAStage> framebuffers{};
79 };
80 std::vector<Images> m_dynamic_images{};
81 bool m_images_ready{};
82
83 vk::Sampler m_sampler{};
84};
85
86} // namespace Vulkan