diff options
| author | 2022-12-08 16:52:29 -0500 | |
|---|---|---|
| committer | 2022-12-08 17:17:45 -0500 | |
| commit | 5b837157bde2115cf4781c5353f598eb6f0f7109 (patch) | |
| tree | e69496d0ae0191642de95cd2c27585276cc3669e /src/video_core/renderer_vulkan | |
| parent | Merge pull request #9401 from vonchenplus/draw_manager (diff) | |
| download | yuzu-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.cpp | 23 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_blit_screen.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 1 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_smaa.cpp | 761 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_smaa.h | 86 |
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 | ||
| 1466 | void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { | ||
| 1467 | smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size); | ||
| 1468 | } | ||
| 1469 | |||
| 1451 | void BlitScreen::CreateFSR() { | 1470 | void 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; | |||
| 40 | class FSR; | 40 | class FSR; |
| 41 | class RasterizerVulkan; | 41 | class RasterizerVulkan; |
| 42 | class Scheduler; | 42 | class Scheduler; |
| 43 | class SMAA; | ||
| 43 | class Swapchain; | 44 | class Swapchain; |
| 44 | 45 | ||
| 45 | struct ScreenInfo { | 46 | struct 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 | |||
| 23 | namespace Vulkan { | ||
| 24 | namespace { | ||
| 25 | |||
| 26 | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | ||
| 27 | |||
| 28 | std::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 | |||
| 56 | void 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 | |||
| 82 | void 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 | |||
| 125 | vk::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 | |||
| 142 | vk::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 | |||
| 196 | vk::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 | |||
| 211 | vk::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 | |||
| 234 | vk::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 | |||
| 244 | vk::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 | |||
| 261 | vk::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 | |||
| 283 | vk::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 | |||
| 294 | vk::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 | |||
| 307 | vk::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 | |||
| 446 | VkWriteDescriptorSet 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 | |||
| 470 | void 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 | |||
| 482 | void 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 | |||
| 516 | SMAA::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 | |||
| 530 | void 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 | |||
| 563 | void 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 | |||
| 583 | void SMAA::CreateSampler() { | ||
| 584 | m_sampler = CreateWrappedSampler(m_device); | ||
| 585 | } | ||
| 586 | |||
| 587 | void 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 | |||
| 606 | void 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 | |||
| 615 | void 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 | |||
| 622 | void 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 | |||
| 632 | void 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 | |||
| 638 | void 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 | |||
| 646 | void 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 | |||
| 673 | void 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 | |||
| 698 | VkImageView 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 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | class Device; | ||
| 13 | class Scheduler; | ||
| 14 | class StagingBufferPool; | ||
| 15 | |||
| 16 | class SMAA { | ||
| 17 | public: | ||
| 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 | |||
| 23 | private: | ||
| 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 | ||