diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 39f0432d8e..8510e146b9 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -750,8 +750,6 @@ هیچ - FXAA - SMAA خودکار diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index f3a2a069e7..44d35b4b84 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -266,6 +266,7 @@ @string/anti_aliasing_none @string/anti_aliasing_fxaa @string/anti_aliasing_smaa + @string/anti_aliasing_ssaa diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 77be250537..50707fe72a 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1087,6 +1087,7 @@ None FXAA SMAA + SSAA Auto diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index da142e8e1c..f21c59be1c 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -146,7 +146,7 @@ ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge, MaxEnum); -ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); +ENUM(AntiAliasing, None, Fxaa, Smaa, Ssaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); ENUM(AppletMode, HLE, LLE); diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 5c63732a3e..45b08d4e5c 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -500,6 +500,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(AntiAliasing, None, tr("None")), PAIR(AntiAliasing, Fxaa, tr("FXAA")), PAIR(AntiAliasing, Smaa, tr("SMAA")), + PAIR(AntiAliasing, Ssaa, tr("SSAA")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/config/shared_translation.h b/src/qt_common/config/shared_translation.h index c34b5162c4..28ca43853c 100644 --- a/src/qt_common/config/shared_translation.h +++ b/src/qt_common/config/shared_translation.h @@ -31,6 +31,7 @@ static const std::map anti_aliasing_texts_map = {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "None"))}, {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FXAA"))}, {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SMAA"))}, + {Settings::AntiAliasing::Ssaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SSAA"))}, }; static const std::map scaling_filter_texts_map = { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 53b0d1638b..67482039aa 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -125,6 +125,8 @@ add_library(video_core STATIC renderer_vulkan/present/fsr.h renderer_vulkan/present/fxaa.cpp renderer_vulkan/present/fxaa.h + renderer_vulkan/present/ssaa.cpp + renderer_vulkan/present/ssaa.h renderer_vulkan/present/layer.cpp renderer_vulkan/present/layer.h renderer_vulkan/present/present_push_constants.h diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 9e8d76b104..c7a5882545 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -60,6 +60,7 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/smaa_blending_weight_calculation.frag ${CMAKE_CURRENT_SOURCE_DIR}/smaa_neighborhood_blending.vert ${CMAKE_CURRENT_SOURCE_DIR}/smaa_neighborhood_blending.frag + ${CMAKE_CURRENT_SOURCE_DIR}/ssaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_blit_depth_stencil.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_color_clear.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_color_clear.vert diff --git a/src/video_core/host_shaders/ssaa.frag b/src/video_core/host_shaders/ssaa.frag new file mode 100644 index 0000000000..cbc5d2f9d7 --- /dev/null +++ b/src/video_core/host_shaders/ssaa.frag @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 +#extension GL_ARB_sample_shading : enable + +#ifdef VULKAN +#define BINDING_COLOR_TEXTURE 1 +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv +#define BINDING_COLOR_TEXTURE 0 +#endif + +layout (location = 0) in vec4 posPos; +layout (location = 0) out vec4 frag_color; +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; + +void main() { + frag_color = texelFetch(input_texture, ivec2(posPos.xy * textureSize(input_texture)), gl_SampleID); +} diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index e20473d2af..6238f56629 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -111,6 +111,8 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, fxaa->Draw(scheduler, image_index, &source_image, &source_image_view); } else if (auto* smaa = std::get_if(&anti_alias)) { smaa->Draw(scheduler, image_index, &source_image, &source_image_view); + } else if (auto* ssaa = std::get_if(&anti_alias)) { + ssaa->Draw(scheduler, image_index, &source_image, &source_image_view); } auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index 47a6a69218..ac8ed69b53 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -16,6 +16,7 @@ #include "video_core/renderer_vulkan/present/sgsr.h" #include "video_core/renderer_vulkan/present/fxaa.h" #include "video_core/renderer_vulkan/present/smaa.h" +#include "video_core/renderer_vulkan/present/ssaa.h" namespace Layout { struct FramebufferLayout; @@ -95,7 +96,7 @@ private: Service::android::PixelFormat pixel_format{}; Settings::AntiAliasing anti_alias_setting{}; - std::variant anti_alias{}; + std::variant anti_alias{}; std::variant sr_filter{}; std::vector resource_ticks{}; }; diff --git a/src/video_core/renderer_vulkan/present/ssaa.cpp b/src/video_core/renderer_vulkan/present/ssaa.cpp new file mode 100644 index 0000000000..14d3b23a1d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/ssaa.cpp @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_types.h" + +#include "video_core/host_shaders/ssaa_frag_spv.h" +#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" +#include "video_core/renderer_vulkan/present/ssaa.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +SSAA::SSAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent) + , m_image_count(u32(image_count)) +{ + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +SSAA::~SSAA() = default; + +void SSAA::CreateImages() { + for (u32 i = 0; i < m_image_count; i++) { + Image& image = m_dynamic_images.emplace_back(); + image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + image.image_view = CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void SSAA::CreateRenderPasses() { + m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + for (auto& image : m_dynamic_images) { + image.framebuffer = CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent); + } +} + +void SSAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void SSAA::CreateShaders() { + m_vertex_shader = CreateWrappedShaderModule(m_device, FULL_SCREEN_TRIANGLE_VERT_SPV); + m_fragment_shader = CreateWrappedShaderModule(m_device, SSAA_FRAG_SPV); +} + +void SSAA::CreateDescriptorPool() { + // 2 descriptors, 1 descriptor set per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count); +} + +void SSAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layout = CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void SSAA::CreateDescriptorSets() { + VkDescriptorSetLayout layout = *m_descriptor_set_layout; + for (auto& images : m_dynamic_images) + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout}); +} + +void SSAA::CreatePipelineLayouts() { + m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout); +} + +void SSAA::CreatePipelines() { + m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, std::tie(m_vertex_shader, m_fragment_shader)); +} + +void SSAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Image& image = m_dynamic_images[image_index]; + std::vector image_infos; + std::vector updates; + image_infos.reserve(2); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void SSAA::UploadImages(Scheduler& scheduler) { + if (!m_images_ready) { + m_images_ready = true; + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) + ClearColorImage(cmdbuf, *image.image); + }); + scheduler.Finish(); + } +} + +void SSAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) { + const Image& image{m_dynamic_images[image_index]}; + const VkImage input_image{*inout_image}; + const VkImage output_image{*image.image}; + const VkDescriptorSet descriptor_set{image.descriptor_sets[0]}; + const VkFramebuffer framebuffer{*image.framebuffer}; + const VkRenderPass renderpass{*m_renderpass}; + const VkPipeline pipeline{*m_pipeline}; + const VkPipelineLayout layout{*m_pipeline_layout}; + const VkExtent2D extent{m_extent}; + + UploadImages(scheduler); + UpdateDescriptorSets(*inout_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + *inout_image = *image.image; + *inout_image_view = *image.image_view; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/ssaa.h b/src/video_core/renderer_vulkan/present/ssaa.h new file mode 100644 index 0000000000..d067c85556 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/ssaa.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/present/anti_alias_pass.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class Scheduler; +class StagingBufferPool; + +class SSAA final : public AntiAliasPass { +public: + explicit SSAA(const Device& device, MemoryAllocator& allocator, size_t image_count, + VkExtent2D extent); + ~SSAA() override; + + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override; + +private: + void CreateImages(); + void CreateRenderPasses(); + void CreateSampler(); + void CreateShaders(); + void CreateDescriptorPool(); + void CreateDescriptorSetLayouts(); + void CreateDescriptorSets(); + void CreatePipelineLayouts(); + void CreatePipelines(); + void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + void UploadImages(Scheduler& scheduler); + + const Device& m_device; + MemoryAllocator& m_allocator; + const VkExtent2D m_extent; + const u32 m_image_count; + + vk::ShaderModule m_vertex_shader{}; + vk::ShaderModule m_fragment_shader{}; + vk::DescriptorPool m_descriptor_pool{}; + vk::DescriptorSetLayout m_descriptor_set_layout{}; + vk::PipelineLayout m_pipeline_layout{}; + vk::Pipeline m_pipeline{}; + vk::RenderPass m_renderpass{}; + + struct Image { + vk::DescriptorSets descriptor_sets{}; + vk::Framebuffer framebuffer{}; + vk::Image image{}; + vk::ImageView image_view{}; + }; + std::vector m_dynamic_images{}; + bool m_images_ready{}; + + vk::Sampler m_sampler{}; +}; + +} // namespace Vulkan