diff options
Diffstat (limited to '')
16 files changed, 1779 insertions, 295 deletions
diff --git a/src/video_core/renderer_vulkan/present/anti_alias_pass.h b/src/video_core/renderer_vulkan/present/anti_alias_pass.h new file mode 100644 index 000000000..1f20fbd7f --- /dev/null +++ b/src/video_core/renderer_vulkan/present/anti_alias_pass.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Scheduler; + +class AntiAliasPass { +public: + virtual ~AntiAliasPass() = default; + virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) = 0; +}; + +class NoAA final : public AntiAliasPass { +public: + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override {} +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp new file mode 100644 index 000000000..b5e08938e --- /dev/null +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -0,0 +1,56 @@ +// 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/present_bicubic_frag_spv.h" +#include "video_core/host_shaders/present_gaussian_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" +#include "video_core/renderer_vulkan/present/filters.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +namespace { + +vk::ShaderModule SelectScaleForceShader(const Device& device) { + if (device.IsFloat16Supported()) { + return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); + } else { + return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); + } +} + +} // Anonymous namespace + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, + CreateNearestNeighborSampler(device), + BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + SelectScaleForceShader(device)); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h new file mode 100644 index 000000000..6c83726dd --- /dev/null +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/present/window_adapt_pass.h" + +namespace Vulkan { + +class MemoryAllocator; + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp new file mode 100644 index 000000000..3f708be70 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fsr.cpp @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_types.h" +#include "common/div_ceil.h" +#include "common/settings.h" + +#include "video_core/fsr.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h" +#include "video_core/renderer_vulkan/present/fsr.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 { +using namespace FSR; + +using PushConstants = std::array<u32, 4 * 4>; + +FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, + VkExtent2D extent) + : m_device{device}, m_memory_allocator{memory_allocator}, + m_image_count{image_count}, m_extent{extent} { + + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayout(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +void FSR::CreateImages() { + m_dynamic_images.resize(m_image_count); + for (auto& images : m_dynamic_images) { + images.images[Easu] = + CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Rcas] = + CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Easu] = + CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Rcas] = + CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void FSR::CreateRenderPasses() { + m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[Easu] = + CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent); + images.framebuffers[Rcas] = + CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent); + } +} + +void FSR::CreateSampler() { + m_sampler = CreateBilinearSampler(m_device); +} + +void FSR::CreateShaders() { + m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV); + + if (m_device.IsFloat16Supported()) { + m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV); + m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV); + } else { + m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV); + m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV); + } +} + +void FSR::CreateDescriptorPool() { + // EASU: 1 descriptor + // RCAS: 1 descriptor + // 2 descriptors, 2 descriptor sets per invocation + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count); +} + +void FSR::CreateDescriptorSetLayout() { + m_descriptor_set_layout = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void FSR::CreateDescriptorSets() { + std::vector<VkDescriptorSetLayout> layouts(MaxFsrStage, *m_descriptor_set_layout); + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); + } +} + +void FSR::CreatePipelineLayouts() { + const VkPushConstantRange range{ + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof(PushConstants), + }; + VkPipelineLayoutCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = m_descriptor_set_layout.address(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }; + + m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci); +} + +void FSR::CreatePipelines() { + m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vert_shader, m_easu_shader)); + m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vert_shader, m_rcas_shader)); +} + +void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> updates; + image_infos.reserve(2); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[Easu], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu], + images.descriptor_sets[Rcas], 0)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void FSR::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.images[Easu]); + ClearColorImage(cmdbuf, *image.images[Rcas]); + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view, VkExtent2D input_image_extent, + const Common::Rectangle<f32>& crop_rect) { + Images& images = m_dynamic_images[image_index]; + + VkImage easu_image = *images.images[Easu]; + VkImage rcas_image = *images.images[Rcas]; + VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu]; + VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas]; + VkFramebuffer easu_framebuffer = *images.framebuffers[Easu]; + VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas]; + VkPipeline easu_pipeline = *m_easu_pipeline; + VkPipeline rcas_pipeline = *m_rcas_pipeline; + VkPipelineLayout pipeline_layout = *m_pipeline_layout; + VkRenderPass renderpass = *m_renderpass; + VkExtent2D extent = m_extent; + + const f32 input_image_width = static_cast<f32>(input_image_extent.width); + const f32 input_image_height = static_cast<f32>(input_image_extent.height); + const f32 output_image_width = static_cast<f32>(extent.width); + const f32 output_image_height = static_cast<f32>(extent.height); + const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; + const f32 viewport_x = crop_rect.left * input_image_width; + const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; + const f32 viewport_y = crop_rect.top * input_image_height; + + PushConstants easu_con{}; + PushConstants rcas_con{}; + FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8, + easu_con.data() + 12, viewport_width, viewport_height, input_image_width, + input_image_height, output_image_width, output_image_height, viewport_x, + viewport_y); + + const float sharpening = + static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + FsrRcasCon(rcas_con.data(), sharpening); + + UploadImages(scheduler); + UpdateDescriptorSets(source_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, + easu_descriptor_set, {}); + cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, + rcas_descriptor_set, {}); + cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + return *images.image_views[Rcas]; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.h b/src/video_core/renderer_vulkan/present/fsr.h new file mode 100644 index 000000000..8602e8146 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fsr.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/math_util.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 FSR { +public: + explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, + VkExtent2D extent); + VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view, VkExtent2D input_image_extent, + const Common::Rectangle<f32>& crop_rect); + +private: + void CreateImages(); + void CreateRenderPasses(); + void CreateSampler(); + void CreateShaders(); + void CreateDescriptorPool(); + void CreateDescriptorSetLayout(); + void CreateDescriptorSets(); + void CreatePipelineLayouts(); + void CreatePipelines(); + + void UploadImages(Scheduler& scheduler); + void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + + const Device& m_device; + MemoryAllocator& m_memory_allocator; + const size_t m_image_count; + const VkExtent2D m_extent; + + enum FsrStage { + Easu, + Rcas, + MaxFsrStage, + }; + + vk::DescriptorPool m_descriptor_pool; + vk::DescriptorSetLayout m_descriptor_set_layout; + vk::PipelineLayout m_pipeline_layout; + vk::ShaderModule m_vert_shader; + vk::ShaderModule m_easu_shader; + vk::ShaderModule m_rcas_shader; + vk::Pipeline m_easu_pipeline; + vk::Pipeline m_rcas_pipeline; + vk::RenderPass m_renderpass; + vk::Sampler m_sampler; + + struct Images { + vk::DescriptorSets descriptor_sets; + std::array<vk::Image, MaxFsrStage> images; + std::array<vk::ImageView, MaxFsrStage> image_views; + std::array<vk::Framebuffer, MaxFsrStage> framebuffers; + }; + std::vector<Images> m_dynamic_images; + bool m_images_ready{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fxaa.cpp b/src/video_core/renderer_vulkan/present/fxaa.cpp new file mode 100644 index 000000000..bdafd1f4d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fxaa.cpp @@ -0,0 +1,148 @@ +// 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/fxaa_frag_spv.h" +#include "video_core/host_shaders/fxaa_vert_spv.h" +#include "video_core/renderer_vulkan/present/fxaa.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 { + +FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent), + m_image_count(static_cast<u32>(image_count)) { + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +FXAA::~FXAA() = default; + +void FXAA::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 FXAA::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 FXAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void FXAA::CreateShaders() { + m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV); + m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV); +} + +void FXAA::CreateDescriptorPool() { + // 2 descriptors, 1 descriptor set per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count); +} + +void FXAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layout = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void FXAA::CreateDescriptorSets() { + VkDescriptorSetLayout layout = *m_descriptor_set_layout; + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout}); + } +} + +void FXAA::CreatePipelineLayouts() { + m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout); +} + +void FXAA::CreatePipelines() { + m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vertex_shader, m_fragment_shader)); +} + +void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Image& image = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> 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 FXAA::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.image); + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +void FXAA::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/fxaa.h b/src/video_core/renderer_vulkan/present/fxaa.h new file mode 100644 index 000000000..97a2e5c1c --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fxaa.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 FXAA final : public AntiAliasPass { +public: + explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, + VkExtent2D extent); + ~FXAA() 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<Image> m_dynamic_images{}; + bool m_images_ready{}; + + vk::Sampler m_sampler{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp new file mode 100644 index 000000000..cfc04be44 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -0,0 +1,336 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_vulkan/vk_rasterizer.h" + +#include "common/settings.h" +#include "video_core/framebuffer_config.h" +#include "video_core/renderer_vulkan/present/fsr.h" +#include "video_core/renderer_vulkan/present/fxaa.h" +#include "video_core/renderer_vulkan/present/layer.h" +#include "video_core/renderer_vulkan/present/present_push_constants.h" +#include "video_core/renderer_vulkan/present/smaa.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_blit_screen.h" +#include "video_core/textures/decoders.h" + +namespace Vulkan { + +namespace { + +u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { + using namespace VideoCore::Surface; + return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); +} + +std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { + return static_cast<std::size_t>(framebuffer.stride) * + static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); +} + +VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { + switch (framebuffer.pixel_format) { + case Service::android::PixelFormat::Rgba8888: + case Service::android::PixelFormat::Rgbx8888: + return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + case Service::android::PixelFormat::Rgb565: + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case Service::android::PixelFormat::Bgra8888: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", + static_cast<u32>(framebuffer.pixel_format)); + return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + } +} + +} // Anonymous namespace + +Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, + Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, + VkExtent2D output_size, VkDescriptorSetLayout layout) + : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), + device_memory(device_memory_), image_count(image_count_) { + CreateDescriptorPool(); + CreateDescriptorSets(layout); + if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + CreateFSR(output_size); + } +} + +Layer::~Layer() { + ReleaseRawImages(); +} + +void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, + VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, + VkSampler sampler, size_t image_index, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout) { + const auto texture_info = rasterizer.AccelerateDisplay( + framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); + const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; + const u32 texture_height = texture_info ? texture_info->height : framebuffer.height; + const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; + const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; + const bool use_accelerated = texture_info.has_value(); + + RefreshResources(framebuffer); + SetAntiAliasPass(); + + // Finish any pending renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Wait(resource_ticks[image_index]); + SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); + + if (!use_accelerated) { + UpdateRawImage(framebuffer, image_index); + } + + VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; + VkImageView source_image_view = + texture_info ? texture_info->image_view : *raw_image_views[image_index]; + + anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view); + + auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); + const VkExtent2D render_extent{ + .width = scaled_width, + .height = scaled_height, + }; + + if (fsr) { + source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, + render_extent, crop_rect); + crop_rect = {0, 0, 1, 1}; + } + + SetMatrixData(*out_push_constants, layout); + SetVertexData(*out_push_constants, layout, crop_rect); + + UpdateDescriptorSet(source_image_view, sampler, image_index); + *out_descriptor_set = descriptor_sets[image_index]; +} + +void Layer::CreateDescriptorPool() { + descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); +} + +void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) { + const std::vector layouts(image_count, layout); + descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); +} + +void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { + const VkBufferCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = CalculateBufferSize(framebuffer), + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); +} + +void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { + const auto format = GetFormat(framebuffer); + resource_ticks.resize(image_count); + raw_images.resize(image_count); + raw_image_views.resize(image_count); + + for (size_t i = 0; i < image_count; ++i) { + raw_images[i] = + CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format); + raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format); + } +} + +void Layer::CreateFSR(VkExtent2D output_size) { + fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size); +} + +void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { + if (framebuffer.width == raw_width && framebuffer.height == raw_height && + framebuffer.pixel_format == pixel_format && !raw_images.empty()) { + return; + } + + raw_width = framebuffer.width; + raw_height = framebuffer.height; + pixel_format = framebuffer.pixel_format; + anti_alias.reset(); + + ReleaseRawImages(); + CreateStagingBuffer(framebuffer); + CreateRawImages(framebuffer); +} + +void Layer::SetAntiAliasPass() { + if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { + return; + } + + anti_alias_setting = Settings::values.anti_aliasing.GetValue(); + + const VkExtent2D render_area{ + .width = Settings::values.resolution_info.ScaleUp(raw_width), + .height = Settings::values.resolution_info.ScaleUp(raw_height), + }; + + switch (anti_alias_setting) { + case Settings::AntiAliasing::Fxaa: + anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area); + break; + case Settings::AntiAliasing::Smaa: + anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area); + break; + default: + anti_alias = std::make_unique<NoAA>(); + break; + } +} + +void Layer::ReleaseRawImages() { + for (const u64 tick : resource_ticks) { + scheduler.Wait(tick); + } + raw_images.clear(); + buffer.reset(); +} + +u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { + return GetSizeInBytes(framebuffer) * image_count; +} + +u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, + size_t image_index) const { + return GetSizeInBytes(framebuffer) * image_index; +} + +void Layer::SetMatrixData(PresentPushConstants& data, + const Layout::FramebufferLayout& layout) const { + data.modelview_matrix = + MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); +} + +void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, + const Common::Rectangle<f32>& crop) const { + // Map the coordinates to the screen. + const auto& screen = layout.screen; + const auto x = static_cast<f32>(screen.left); + const auto y = static_cast<f32>(screen.top); + const auto w = static_cast<f32>(screen.GetWidth()); + const auto h = static_cast<f32>(screen.GetHeight()); + + data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); + data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); + data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); + data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); +} + +void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) { + const VkDescriptorImageInfo image_info{ + .sampler = sampler, + .imageView = image_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkWriteDescriptorSet sampler_write{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = descriptor_sets[image_index], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; + + device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {}); +} + +void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) { + const std::span<u8> mapped_span = buffer.Mapped(); + + const u64 image_offset = GetRawImageOffset(framebuffer, image_index); + + const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; + const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); + + // TODO(Rodrigo): Read this from HLE + constexpr u32 block_height_log2 = 4; + const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); + const u64 linear_size{GetSizeInBytes(framebuffer)}; + const u64 tiled_size{Tegra::Texture::CalculateSize( + true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; + Tegra::Texture::UnswizzleTexture( + mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), + bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + + const VkBufferImageCopy copy{ + .bufferOffset = image_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {.x = 0, .y = 0, .z = 0}, + .imageExtent = + { + .width = framebuffer.width, + .height = framebuffer.height, + .depth = 1, + }, + }; + scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { + const VkImage image = *raw_images[index]; + const VkImageMemoryBarrier base_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + VkImageMemoryBarrier read_barrier = base_barrier; + read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + VkImageMemoryBarrier write_barrier = base_barrier; + write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); + cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, write_barrier); + }); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h new file mode 100644 index 000000000..88d43fc5f --- /dev/null +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace Service::android { +enum class PixelFormat : u32; +} + +namespace Settings { +enum class AntiAliasing : u32; +} + +namespace Vulkan { + +class AntiAliasPass; +class Device; +class FSR; +class MemoryAllocator; +struct PresentPushConstants; +class RasterizerVulkan; +class Scheduler; + +class Layer final { +public: + explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, + Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, + VkExtent2D output_size, VkDescriptorSetLayout layout); + ~Layer(); + + void ConfigureDraw(PresentPushConstants* out_push_constants, + VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, + VkSampler sampler, size_t image_index, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout); + +private: + void CreateDescriptorPool(); + void CreateDescriptorSets(VkDescriptorSetLayout layout); + void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); + void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); + void CreateFSR(VkExtent2D output_size); + + void RefreshResources(const Tegra::FramebufferConfig& framebuffer); + void SetAntiAliasPass(); + void ReleaseRawImages(); + + u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; + u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const; + + void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; + void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, + const Common::Rectangle<f32>& crop) const; + void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index); + void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index); + +private: + const Device& device; + MemoryAllocator& memory_allocator; + Scheduler& scheduler; + Tegra::MaxwellDeviceMemoryManager& device_memory; + const size_t image_count{}; + vk::DescriptorPool descriptor_pool{}; + vk::DescriptorSets descriptor_sets{}; + + vk::Buffer buffer{}; + std::vector<vk::Image> raw_images{}; + std::vector<vk::ImageView> raw_image_views{}; + u32 raw_width{}; + u32 raw_height{}; + Service::android::PixelFormat pixel_format{}; + + Settings::AntiAliasing anti_alias_setting{}; + std::unique_ptr<AntiAliasPass> anti_alias{}; + + std::unique_ptr<FSR> fsr{}; + std::vector<u64> resource_ticks{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/present_push_constants.h b/src/video_core/renderer_vulkan/present/present_push_constants.h new file mode 100644 index 000000000..f1949e7aa --- /dev/null +++ b/src/video_core/renderer_vulkan/present/present_push_constants.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Vulkan { + +struct ScreenRectVertex { + ScreenRectVertex() = default; + explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} + + std::array<f32, 2> position; + std::array<f32, 2> tex_coord; +}; + +static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { + // clang-format off + return { 2.f / width, 0.f, 0.f, 0.f, + 0.f, 2.f / height, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 1.f}; + // clang-format on +} + +struct PresentPushConstants { + std::array<f32, 4 * 4> modelview_matrix; + std::array<ScreenRectVertex, 4> vertices; +}; + +static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large"); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/smaa.cpp b/src/video_core/renderer_vulkan/present/smaa.cpp new file mode 100644 index 000000000..39645fd1d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/smaa.cpp @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <list> + +#include "common/assert.h" +#include "common/polyfill_ranges.h" + +#include "video_core/renderer_vulkan/present/smaa.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/smaa_area_tex.h" +#include "video_core/smaa_search_tex.h" +#include "video_core/vulkan_common/vulkan_device.h" + +#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" + +namespace Vulkan { + +SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent), + m_image_count(static_cast<u32>(image_count)) { + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +SMAA::~SMAA() = default; + +void SMAA::CreateImages() { + static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); + m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); + + m_static_image_views[Area] = + CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); + m_static_image_views[Search] = + CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); + + for (u32 i = 0; i < m_image_count; i++) { + Images& images = m_dynamic_images.emplace_back(); + + images.images[Blend] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); + images.images[Output] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + + images.image_views[Blend] = + CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Edges] = + CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); + images.image_views[Output] = + CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void SMAA::CreateRenderPasses() { + m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); + m_renderpasses[BlendingWeightCalculation] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + m_renderpasses[NeighborhoodBlending] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( + m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); + + images.framebuffers[BlendingWeightCalculation] = + CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], + images.image_views[Blend], m_extent); + + images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( + m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); + } +} + +void SMAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void SMAA::CreateShaders() { + // These match the order of the SMAAStage enum + static constexpr std::array vert_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), + }; + static constexpr std::array frag_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), + }; + + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); + m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); + } +} + +void SMAA::CreateDescriptorPool() { + // Edge detection: 1 descriptor + // Blending weight calculation: 3 descriptors + // Neighborhood blending: 2 descriptors + + // 6 descriptors, 3 descriptor sets per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); +} + +void SMAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layouts[EdgeDetection] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + m_descriptor_set_layouts[BlendingWeightCalculation] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + m_descriptor_set_layouts[NeighborhoodBlending] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void SMAA::CreateDescriptorSets() { + std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); + std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), + [](auto& layout) { return *layout; }); + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); + } +} + +void SMAA::CreatePipelineLayouts() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); + } +} + +void SMAA::CreatePipelines() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipelines[i] = + CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], + std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); + } +} + +void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> updates; + image_infos.reserve(6); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[EdgeDetection], 0)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], + images.descriptor_sets[BlendingWeightCalculation], + 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], + images.descriptor_sets[BlendingWeightCalculation], + 1)); + updates.push_back( + CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], + images.descriptor_sets[BlendingWeightCalculation], 2)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[NeighborhoodBlending], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], + images.descriptor_sets[NeighborhoodBlending], 1)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void SMAA::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, + VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); + UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, + VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& images : m_dynamic_images) { + for (size_t i = 0; i < MaxDynamicImage; i++) { + ClearColorImage(cmdbuf, *images.images[i]); + } + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) { + Images& images = m_dynamic_images[image_index]; + + VkImage input_image = *inout_image; + VkImage output_image = *images.images[Output]; + VkImage edges_image = *images.images[Edges]; + VkImage blend_image = *images.images[Blend]; + + VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; + VkDescriptorSet blending_weight_calculation_descriptor_set = + images.descriptor_sets[BlendingWeightCalculation]; + VkDescriptorSet neighborhood_blending_descriptor_set = + images.descriptor_sets[NeighborhoodBlending]; + + VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; + VkFramebuffer blending_weight_calculation_framebuffer = + *images.framebuffers[BlendingWeightCalculation]; + VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; + + UploadImages(scheduler); + UpdateDescriptorSets(*inout_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer, + m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[EdgeDetection], 0, + edge_detection_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation], + blending_weight_calculation_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipelines[BlendingWeightCalculation]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[BlendingWeightCalculation], 0, + blending_weight_calculation_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending], + neighborhood_blending_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[NeighborhoodBlending], 0, + neighborhood_blending_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + *inout_image = *images.images[Output]; + *inout_image_view = *images.image_views[Output]; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/present/smaa.h index 0e214258a..fdf6def07 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.h +++ b/src/video_core/renderer_vulkan/present/smaa.h @@ -4,6 +4,7 @@ #pragma once #include <array> +#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" @@ -13,12 +14,14 @@ class Device; class Scheduler; class StagingBufferPool; -class SMAA { +class SMAA final : public AntiAliasPass { public: explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent); - VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view); + ~SMAA() override; + + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override; private: enum SMAAStage { diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 70644ea82..6ee16595d 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -1,29 +1,25 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <list> - #include "common/assert.h" #include "common/polyfill_ranges.h" - -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" -#include "video_core/renderer_vulkan/vk_smaa.h" -#include "video_core/smaa_area_tex.h" -#include "video_core/smaa_search_tex.h" -#include "video_core/vulkan_common/vulkan_device.h" - -#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" -#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" -#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" -#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" +#include "video_core/renderer_vulkan/present/util.h" namespace Vulkan { -namespace { -#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) +vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) { + const VkBufferCreateInfo dst_buffer_info{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + return allocator.CreateBuffer(dst_buffer_info, usage); +} vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { const VkImageCreateInfo image_ci{ @@ -48,7 +44,7 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, } void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, - VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { + VkImageLayout source_layout) { constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; const VkImageMemoryBarrier barrier{ @@ -75,7 +71,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, vk::Image& image, VkExtent2D dimensions, VkFormat format, - std::span<const u8> initial_contents = {}) { + std::span<const u8> initial_contents) { const VkBufferCreateInfo upload_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -114,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc scheduler.Finish(); } +void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, + VkExtent3D extent) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + static constexpr VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; + const VkBufferImageCopy copy{ + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset{.x = 0, .y = 0, .z = 0}, + .imageExtent{extent}, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); + cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, + memory_write_barrier, nullptr, image_write_barrier); +} + vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -131,16 +191,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF }); } -vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout) { const VkAttachmentDescription attachment{ .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, .format = format, .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, - .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .initialLayout = initial_layout, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; @@ -200,13 +262,13 @@ vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& r }); } -vk::Sampler CreateWrappedSampler(const Device& device) { +vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, + .magFilter = filter, + .minFilter = filter, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, @@ -233,30 +295,34 @@ vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const }); } -vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, - u32 max_sets) { - const VkDescriptorPoolSize pool_size{ - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = static_cast<u32>(max_descriptors), - }; +vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, + size_t max_sets, + std::initializer_list<VkDescriptorType> types) { + std::vector<VkDescriptorPoolSize> pool_sizes(types.size()); + for (u32 i = 0; i < types.size(); i++) { + pool_sizes[i] = VkDescriptorPoolSize{ + .type = std::data(types)[i], + .descriptorCount = static_cast<u32>(max_descriptors), + }; + } return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .pNext = nullptr, .flags = 0, - .maxSets = max_sets, - .poolSizeCount = 1, - .pPoolSizes = &pool_size, + .maxSets = static_cast<u32>(max_sets), + .poolSizeCount = static_cast<u32>(pool_sizes.size()), + .pPoolSizes = pool_sizes.data(), }); } -vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, - u32 max_sampler_bindings) { - std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings); - for (u32 i = 0; i < max_sampler_bindings; i++) { +vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( + const Device& device, std::initializer_list<VkDescriptorType> types) { + std::vector<VkDescriptorSetLayoutBinding> bindings(types.size()); + for (size_t i = 0; i < types.size(); i++) { bindings[i] = { - .binding = i, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .binding = static_cast<u32>(i), + .descriptorType = std::data(types)[i], .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr, @@ -298,7 +364,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, - std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, + bool enable_blending) { const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -376,7 +443,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .alphaToOneEnable = VK_FALSE, }; - constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ .blendEnable = VK_FALSE, .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, @@ -388,6 +455,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + const VkPipelineColorBlendStateCreateInfo color_blend_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, @@ -395,7 +474,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, - .pAttachments = &color_blend_attachment, + .pAttachments = + enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, }; @@ -459,6 +539,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo> }; } +vk::Sampler CreateBilinearSampler(const Device& device) { + const VkSamplerCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + return device.GetLogical().CreateSampler(ci); +} + +vk::Sampler CreateNearestNeighborSampler(const Device& device) { + const VkSamplerCreateInfo ci_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + return device.GetLogical().CreateSampler(ci_nn); +} + void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, @@ -471,12 +601,12 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); } -void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, - VkFramebuffer framebuffer, VkExtent2D extent) { +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent) { const VkRenderPassBeginInfo renderpass_bi{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, - .renderPass = *render_pass, + .renderPass = render_pass, .framebuffer = framebuffer, .renderArea{ .offset{}, @@ -503,248 +633,4 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, cmdbuf.SetScissor(0, scissor); } -} // Anonymous namespace - -SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) - : m_device(device), m_allocator(allocator), m_extent(extent), - m_image_count(static_cast<u32>(image_count)) { - CreateImages(); - CreateRenderPasses(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayouts(); - CreateDescriptorSets(); - CreatePipelineLayouts(); - CreatePipelines(); -} - -void SMAA::CreateImages() { - static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; - static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; - - m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); - m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); - - m_static_image_views[Area] = - CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); - m_static_image_views[Search] = - CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); - - for (u32 i = 0; i < m_image_count; i++) { - Images& images = m_dynamic_images.emplace_back(); - - images.images[Blend] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); - images.images[Output] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - - images.image_views[Blend] = - CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); - images.image_views[Edges] = - CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); - images.image_views[Output] = - CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); - } -} - -void SMAA::CreateRenderPasses() { - m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); - m_renderpasses[BlendingWeightCalculation] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); - m_renderpasses[NeighborhoodBlending] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); - - for (auto& images : m_dynamic_images) { - images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( - m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); - - images.framebuffers[BlendingWeightCalculation] = - CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], - images.image_views[Blend], m_extent); - - images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( - m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); - } -} - -void SMAA::CreateSampler() { - m_sampler = CreateWrappedSampler(m_device); -} - -void SMAA::CreateShaders() { - // These match the order of the SMAAStage enum - static constexpr std::array vert_shader_sources{ - ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), - ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), - ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), - }; - static constexpr std::array frag_shader_sources{ - ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), - ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), - ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), - }; - - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); - m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); - } -} - -void SMAA::CreateDescriptorPool() { - // Edge detection: 1 descriptor - // Blending weight calculation: 3 descriptors - // Neighborhood blending: 2 descriptors - - // 6 descriptors, 3 descriptor sets per image - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); -} - -void SMAA::CreateDescriptorSetLayouts() { - m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1); - m_descriptor_set_layouts[BlendingWeightCalculation] = - CreateWrappedDescriptorSetLayout(m_device, 3); - m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2); -} - -void SMAA::CreateDescriptorSets() { - std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); - std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), - [](auto& layout) { return *layout; }); - - for (auto& images : m_dynamic_images) { - images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); - } -} - -void SMAA::CreatePipelineLayouts() { - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); - } -} - -void SMAA::CreatePipelines() { - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_pipelines[i] = - CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], - std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); - } -} - -void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { - Images& images = m_dynamic_images[image_index]; - std::vector<VkDescriptorImageInfo> image_infos; - std::vector<VkWriteDescriptorSet> updates; - image_infos.reserve(6); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[EdgeDetection], 0)); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], - images.descriptor_sets[BlendingWeightCalculation], - 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], - images.descriptor_sets[BlendingWeightCalculation], - 1)); - updates.push_back( - CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], - images.descriptor_sets[BlendingWeightCalculation], 2)); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[NeighborhoodBlending], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], - images.descriptor_sets[NeighborhoodBlending], 1)); - - m_device.GetLogical().UpdateDescriptorSets(updates, {}); -} - -void SMAA::UploadImages(Scheduler& scheduler) { - if (m_images_ready) { - return; - } - - static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; - static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; - - UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, - VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); - UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, - VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); - - scheduler.Record([&](vk::CommandBuffer cmdbuf) { - for (auto& images : m_dynamic_images) { - for (size_t i = 0; i < MaxDynamicImage; i++) { - ClearColorImage(cmdbuf, *images.images[i]); - } - } - }); - scheduler.Finish(); - - m_images_ready = true; -} - -VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view) { - Images& images = m_dynamic_images[image_index]; - - VkImage output_image = *images.images[Output]; - VkImage edges_image = *images.images[Edges]; - VkImage blend_image = *images.images[Blend]; - - VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; - VkDescriptorSet blending_weight_calculation_descriptor_set = - images.descriptor_sets[BlendingWeightCalculation]; - VkDescriptorSet neighborhood_blending_descriptor_set = - images.descriptor_sets[NeighborhoodBlending]; - - VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; - VkFramebuffer blending_weight_calculation_framebuffer = - *images.framebuffers[BlendingWeightCalculation]; - VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; - - UploadImages(scheduler); - UpdateDescriptorSets(source_image_view, image_index); - - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { - TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, - m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[EdgeDetection], 0, - edge_detection_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - - TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation], - blending_weight_calculation_framebuffer, m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipelines[BlendingWeightCalculation]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[BlendingWeightCalculation], 0, - blending_weight_calculation_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - - TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending], - neighborhood_blending_framebuffer, m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[NeighborhoodBlending], 0, - neighborhood_blending_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); - }); - - return *images.image_views[Output]; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h new file mode 100644 index 000000000..1104aaa15 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/util.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) + +vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage); + +vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); +void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, + VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); +void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, + vk::Image& image, VkExtent2D dimensions, VkFormat format, + std::span<const u8> initial_contents = {}); +void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, + VkExtent3D extent); +void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); + +vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL); +vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, + vk::ImageView& dest_image, VkExtent2D extent); +vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); +vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code); +vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, + size_t max_sets, + std::initializer_list<VkDescriptorType> types = { + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( + const Device& device, std::initializer_list<VkDescriptorType> types); +vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool, + vk::Span<VkDescriptorSetLayout> layouts); +vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, + vk::DescriptorSetLayout& layout); +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, + vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, + bool enable_blending = false); +VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, + VkSampler sampler, VkImageView view, + VkDescriptorSet set, u32 binding); +vk::Sampler CreateBilinearSampler(const Device& device); +vk::Sampler CreateNearestNeighborSampler(const Device& device); + +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp new file mode 100644 index 000000000..c5db0230d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/frontend/framebuffer_layout.h" +#include "video_core/framebuffer_config.h" +#include "video_core/host_shaders/vulkan_present_vert_spv.h" +#include "video_core/renderer_vulkan/present/layer.h" +#include "video_core/renderer_vulkan/present/present_push_constants.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/present/window_adapt_pass.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" + +namespace Vulkan { + +WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, + vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) + : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { + CreateDescriptorSetLayout(); + CreatePipelineLayout(); + CreateVertexShader(); + CreateRenderPass(frame_format); + CreatePipeline(); +} + +WindowAdaptPass::~WindowAdaptPass() = default; + +void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, + std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> configs, + const Layout::FramebufferLayout& layout, Frame* dst) { + + const VkFramebuffer host_framebuffer{*dst->framebuffer}; + const VkRenderPass renderpass{*render_pass}; + const VkPipeline graphics_pipeline{*pipeline}; + const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; + const VkExtent2D render_area{ + .width = dst->width, + .height = dst->height, + }; + + const size_t layer_count = configs.size(); + std::vector<PresentPushConstants> push_constants(layer_count); + std::vector<VkDescriptorSet> descriptor_sets(layer_count); + + auto layer_it = layers.begin(); + for (size_t i = 0; i < layer_count; i++) { + layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, + image_index, configs[i], layout); + layer_it++; + } + + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; + const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; + const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; + const VkClearAttachment clear_attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = 0, + .clearValue = + { + .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + }, + }; + const VkClearRect clear_rect{ + .rect = + { + .offset = {0, 0}, + .extent = render_area, + }, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); + cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); + + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); + for (size_t i = 0; i < layer_count; i++) { + cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, + push_constants[i]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, + descriptor_sets[i], {}); + cmdbuf.Draw(4, 1, 0, 0); + } + + cmdbuf.EndRenderPass(); + }); +} + +VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { + return *descriptor_set_layout; +} + +VkRenderPass WindowAdaptPass::GetRenderPass() { + return *render_pass; +} + +void WindowAdaptPass::CreateDescriptorSetLayout() { + descriptor_set_layout = + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void WindowAdaptPass::CreatePipelineLayout() { + const VkPushConstantRange range{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(PresentPushConstants), + }; + + pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = descriptor_set_layout.address(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }); +} + +void WindowAdaptPass::CreateVertexShader() { + vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); +} + +void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { + render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void WindowAdaptPass::CreatePipeline() { + pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, + std::tie(vertex_shader, fragment_shader), false); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h new file mode 100644 index 000000000..0e2edfc31 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> + +#include "common/math_util.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace Vulkan { + +class Device; +struct Frame; +class Layer; +class Scheduler; +class RasterizerVulkan; + +class WindowAdaptPass final { +public: + explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler, + vk::ShaderModule&& fragment_shader); + ~WindowAdaptPass(); + + void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, + std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs, + const Layout::FramebufferLayout& layout, Frame* dst); + + VkDescriptorSetLayout GetDescriptorSetLayout(); + VkRenderPass GetRenderPass(); + +private: + void CreateDescriptorSetLayout(); + void CreatePipelineLayout(); + void CreateVertexShader(); + void CreateRenderPass(VkFormat frame_format); + void CreatePipeline(); + +private: + const Device& device; + vk::DescriptorSetLayout descriptor_set_layout; + vk::PipelineLayout pipeline_layout; + vk::Sampler sampler; + vk::ShaderModule vertex_shader; + vk::ShaderModule fragment_shader; + vk::RenderPass render_pass; + vk::Pipeline pipeline; +}; + +} // namespace Vulkan |