summaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
authorGravatar Fernando S2024-02-02 15:08:06 +0100
committerGravatar GitHub2024-02-02 15:08:06 +0100
commit58cf2ee1f93ebfa0e6b25b71d349ad2ad7895f53 (patch)
treea6efdfb4de7a7bac87926f6f1e93e7275634c731 /src/video_core/renderer_vulkan
parentMerge pull request #12878 from zhaobot/tx-update-20240201020554 (diff)
parenthardware_composer: implement speed limit extensions (diff)
downloadyuzu-58cf2ee1f93ebfa0e6b25b71d349ad2ad7895f53.tar.gz
yuzu-58cf2ee1f93ebfa0e6b25b71d349ad2ad7895f53.tar.xz
yuzu-58cf2ee1f93ebfa0e6b25b71d349ad2ad7895f53.zip
Merge pull request #12761 from liamwhite/mp-composite
video_core: rewrite presentation for layer composition
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/present/anti_alias_pass.h25
-rw-r--r--src/video_core/renderer_vulkan/present/filters.cpp56
-rw-r--r--src/video_core/renderer_vulkan/present/filters.h18
-rw-r--r--src/video_core/renderer_vulkan/present/fsr.cpp226
-rw-r--r--src/video_core/renderer_vulkan/present/fsr.h69
-rw-r--r--src/video_core/renderer_vulkan/present/fxaa.cpp148
-rw-r--r--src/video_core/renderer_vulkan/present/fxaa.h63
-rw-r--r--src/video_core/renderer_vulkan/present/layer.cpp336
-rw-r--r--src/video_core/renderer_vulkan/present/layer.h92
-rw-r--r--src/video_core/renderer_vulkan/present/present_push_constants.h34
-rw-r--r--src/video_core/renderer_vulkan/present/smaa.cpp277
-rw-r--r--src/video_core/renderer_vulkan/present/smaa.h (renamed from src/video_core/renderer_vulkan/vk_smaa.h)9
-rw-r--r--src/video_core/renderer_vulkan/present/util.cpp (renamed from src/video_core/renderer_vulkan/vk_smaa.cpp)470
-rw-r--r--src/video_core/renderer_vulkan/present/util.h56
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.cpp137
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.h58
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp177
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp1536
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h144
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp420
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.h52
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h14
24 files changed, 1969 insertions, 2490 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/vulkan_common/vulkan_wrapper.h"
7
8namespace Vulkan {
9
10class Scheduler;
11
12class AntiAliasPass {
13public:
14 virtual ~AntiAliasPass() = default;
15 virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
16 VkImageView* inout_image_view) = 0;
17};
18
19class NoAA final : public AntiAliasPass {
20public:
21 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
22 VkImageView* inout_image_view) override {}
23};
24
25} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5
6#include "video_core/host_shaders/present_bicubic_frag_spv.h"
7#include "video_core/host_shaders/present_gaussian_frag_spv.h"
8#include "video_core/host_shaders/vulkan_present_frag_spv.h"
9#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
10#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
11#include "video_core/renderer_vulkan/present/filters.h"
12#include "video_core/renderer_vulkan/present/util.h"
13#include "video_core/renderer_vulkan/vk_shader_util.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15
16namespace Vulkan {
17
18namespace {
19
20vk::ShaderModule SelectScaleForceShader(const Device& device) {
21 if (device.IsFloat16Supported()) {
22 return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV);
23 } else {
24 return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV);
25 }
26}
27
28} // Anonymous namespace
29
30std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) {
31 return std::make_unique<WindowAdaptPass>(device, frame_format,
32 CreateNearestNeighborSampler(device),
33 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
34}
35
36std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) {
37 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
38 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
39}
40
41std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) {
42 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
43 BuildShader(device, PRESENT_BICUBIC_FRAG_SPV));
44}
45
46std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) {
47 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
48 BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV));
49}
50
51std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) {
52 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
53 SelectScaleForceShader(device));
54}
55
56} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/present/window_adapt_pass.h"
7
8namespace Vulkan {
9
10class MemoryAllocator;
11
12std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format);
13std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format);
14std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format);
15std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format);
16std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format);
17
18} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5#include "common/div_ceil.h"
6#include "common/settings.h"
7
8#include "video_core/fsr.h"
9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_frag_spv.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_frag_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h"
13#include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h"
14#include "video_core/renderer_vulkan/present/fsr.h"
15#include "video_core/renderer_vulkan/present/util.h"
16#include "video_core/renderer_vulkan/vk_scheduler.h"
17#include "video_core/renderer_vulkan/vk_shader_util.h"
18#include "video_core/vulkan_common/vulkan_device.h"
19
20namespace Vulkan {
21using namespace FSR;
22
23using PushConstants = std::array<u32, 4 * 4>;
24
25FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
26 VkExtent2D extent)
27 : m_device{device}, m_memory_allocator{memory_allocator},
28 m_image_count{image_count}, m_extent{extent} {
29
30 CreateImages();
31 CreateRenderPasses();
32 CreateSampler();
33 CreateShaders();
34 CreateDescriptorPool();
35 CreateDescriptorSetLayout();
36 CreateDescriptorSets();
37 CreatePipelineLayouts();
38 CreatePipelines();
39}
40
41void FSR::CreateImages() {
42 m_dynamic_images.resize(m_image_count);
43 for (auto& images : m_dynamic_images) {
44 images.images[Easu] =
45 CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
46 images.images[Rcas] =
47 CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
48 images.image_views[Easu] =
49 CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT);
50 images.image_views[Rcas] =
51 CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT);
52 }
53}
54
55void FSR::CreateRenderPasses() {
56 m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
57
58 for (auto& images : m_dynamic_images) {
59 images.framebuffers[Easu] =
60 CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent);
61 images.framebuffers[Rcas] =
62 CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent);
63 }
64}
65
66void FSR::CreateSampler() {
67 m_sampler = CreateBilinearSampler(m_device);
68}
69
70void FSR::CreateShaders() {
71 m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV);
72
73 if (m_device.IsFloat16Supported()) {
74 m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV);
75 m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV);
76 } else {
77 m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV);
78 m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV);
79 }
80}
81
82void FSR::CreateDescriptorPool() {
83 // EASU: 1 descriptor
84 // RCAS: 1 descriptor
85 // 2 descriptors, 2 descriptor sets per invocation
86 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count);
87}
88
89void FSR::CreateDescriptorSetLayout() {
90 m_descriptor_set_layout =
91 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
92}
93
94void FSR::CreateDescriptorSets() {
95 std::vector<VkDescriptorSetLayout> layouts(MaxFsrStage, *m_descriptor_set_layout);
96
97 for (auto& images : m_dynamic_images) {
98 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
99 }
100}
101
102void FSR::CreatePipelineLayouts() {
103 const VkPushConstantRange range{
104 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
105 .offset = 0,
106 .size = sizeof(PushConstants),
107 };
108 VkPipelineLayoutCreateInfo ci{
109 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
110 .pNext = nullptr,
111 .flags = 0,
112 .setLayoutCount = 1,
113 .pSetLayouts = m_descriptor_set_layout.address(),
114 .pushConstantRangeCount = 1,
115 .pPushConstantRanges = &range,
116 };
117
118 m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci);
119}
120
121void FSR::CreatePipelines() {
122 m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
123 std::tie(m_vert_shader, m_easu_shader));
124 m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
125 std::tie(m_vert_shader, m_rcas_shader));
126}
127
128void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
129 Images& images = m_dynamic_images[image_index];
130 std::vector<VkDescriptorImageInfo> image_infos;
131 std::vector<VkWriteDescriptorSet> updates;
132 image_infos.reserve(2);
133
134 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
135 images.descriptor_sets[Easu], 0));
136 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu],
137 images.descriptor_sets[Rcas], 0));
138
139 m_device.GetLogical().UpdateDescriptorSets(updates, {});
140}
141
142void FSR::UploadImages(Scheduler& scheduler) {
143 if (m_images_ready) {
144 return;
145 }
146
147 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
148 for (auto& image : m_dynamic_images) {
149 ClearColorImage(cmdbuf, *image.images[Easu]);
150 ClearColorImage(cmdbuf, *image.images[Rcas]);
151 }
152 });
153 scheduler.Finish();
154
155 m_images_ready = true;
156}
157
158VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
159 VkImageView source_image_view, VkExtent2D input_image_extent,
160 const Common::Rectangle<f32>& crop_rect) {
161 Images& images = m_dynamic_images[image_index];
162
163 VkImage easu_image = *images.images[Easu];
164 VkImage rcas_image = *images.images[Rcas];
165 VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu];
166 VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas];
167 VkFramebuffer easu_framebuffer = *images.framebuffers[Easu];
168 VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas];
169 VkPipeline easu_pipeline = *m_easu_pipeline;
170 VkPipeline rcas_pipeline = *m_rcas_pipeline;
171 VkPipelineLayout pipeline_layout = *m_pipeline_layout;
172 VkRenderPass renderpass = *m_renderpass;
173 VkExtent2D extent = m_extent;
174
175 const f32 input_image_width = static_cast<f32>(input_image_extent.width);
176 const f32 input_image_height = static_cast<f32>(input_image_extent.height);
177 const f32 output_image_width = static_cast<f32>(extent.width);
178 const f32 output_image_height = static_cast<f32>(extent.height);
179 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
180 const f32 viewport_x = crop_rect.left * input_image_width;
181 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
182 const f32 viewport_y = crop_rect.top * input_image_height;
183
184 PushConstants easu_con{};
185 PushConstants rcas_con{};
186 FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
187 easu_con.data() + 12, viewport_width, viewport_height, input_image_width,
188 input_image_height, output_image_width, output_image_height, viewport_x,
189 viewport_y);
190
191 const float sharpening =
192 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
193 FsrRcasCon(rcas_con.data(), sharpening);
194
195 UploadImages(scheduler);
196 UpdateDescriptorSets(source_image_view, image_index);
197
198 scheduler.RequestOutsideRenderPassOperationContext();
199 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
200 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
201 TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
202 BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent);
203 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline);
204 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
205 easu_descriptor_set, {});
206 cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con);
207 cmdbuf.Draw(3, 1, 0, 0);
208 cmdbuf.EndRenderPass();
209
210 TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
211 TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
212 BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent);
213 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline);
214 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
215 rcas_descriptor_set, {});
216 cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con);
217 cmdbuf.Draw(3, 1, 0, 0);
218 cmdbuf.EndRenderPass();
219
220 TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
221 });
222
223 return *images.image_views[Rcas];
224}
225
226} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14
15class FSR {
16public:
17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
18 VkExtent2D extent);
19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
20 VkImageView source_image_view, VkExtent2D input_image_extent,
21 const Common::Rectangle<f32>& crop_rect);
22
23private:
24 void CreateImages();
25 void CreateRenderPasses();
26 void CreateSampler();
27 void CreateShaders();
28 void CreateDescriptorPool();
29 void CreateDescriptorSetLayout();
30 void CreateDescriptorSets();
31 void CreatePipelineLayouts();
32 void CreatePipelines();
33
34 void UploadImages(Scheduler& scheduler);
35 void UpdateDescriptorSets(VkImageView image_view, size_t image_index);
36
37 const Device& m_device;
38 MemoryAllocator& m_memory_allocator;
39 const size_t m_image_count;
40 const VkExtent2D m_extent;
41
42 enum FsrStage {
43 Easu,
44 Rcas,
45 MaxFsrStage,
46 };
47
48 vk::DescriptorPool m_descriptor_pool;
49 vk::DescriptorSetLayout m_descriptor_set_layout;
50 vk::PipelineLayout m_pipeline_layout;
51 vk::ShaderModule m_vert_shader;
52 vk::ShaderModule m_easu_shader;
53 vk::ShaderModule m_rcas_shader;
54 vk::Pipeline m_easu_pipeline;
55 vk::Pipeline m_rcas_pipeline;
56 vk::RenderPass m_renderpass;
57 vk::Sampler m_sampler;
58
59 struct Images {
60 vk::DescriptorSets descriptor_sets;
61 std::array<vk::Image, MaxFsrStage> images;
62 std::array<vk::ImageView, MaxFsrStage> image_views;
63 std::array<vk::Framebuffer, MaxFsrStage> framebuffers;
64 };
65 std::vector<Images> m_dynamic_images;
66 bool m_images_ready{};
67};
68
69} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5
6#include "video_core/host_shaders/fxaa_frag_spv.h"
7#include "video_core/host_shaders/fxaa_vert_spv.h"
8#include "video_core/renderer_vulkan/present/fxaa.h"
9#include "video_core/renderer_vulkan/present/util.h"
10#include "video_core/renderer_vulkan/vk_scheduler.h"
11#include "video_core/renderer_vulkan/vk_shader_util.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13
14namespace Vulkan {
15
16FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
17 : m_device(device), m_allocator(allocator), m_extent(extent),
18 m_image_count(static_cast<u32>(image_count)) {
19 CreateImages();
20 CreateRenderPasses();
21 CreateSampler();
22 CreateShaders();
23 CreateDescriptorPool();
24 CreateDescriptorSetLayouts();
25 CreateDescriptorSets();
26 CreatePipelineLayouts();
27 CreatePipelines();
28}
29
30FXAA::~FXAA() = default;
31
32void FXAA::CreateImages() {
33 for (u32 i = 0; i < m_image_count; i++) {
34 Image& image = m_dynamic_images.emplace_back();
35
36 image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
37 image.image_view =
38 CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT);
39 }
40}
41
42void FXAA::CreateRenderPasses() {
43 m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
44
45 for (auto& image : m_dynamic_images) {
46 image.framebuffer =
47 CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent);
48 }
49}
50
51void FXAA::CreateSampler() {
52 m_sampler = CreateWrappedSampler(m_device);
53}
54
55void FXAA::CreateShaders() {
56 m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV);
57 m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV);
58}
59
60void FXAA::CreateDescriptorPool() {
61 // 2 descriptors, 1 descriptor set per image
62 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count);
63}
64
65void FXAA::CreateDescriptorSetLayouts() {
66 m_descriptor_set_layout =
67 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
68 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
69}
70
71void FXAA::CreateDescriptorSets() {
72 VkDescriptorSetLayout layout = *m_descriptor_set_layout;
73
74 for (auto& images : m_dynamic_images) {
75 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout});
76 }
77}
78
79void FXAA::CreatePipelineLayouts() {
80 m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout);
81}
82
83void FXAA::CreatePipelines() {
84 m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
85 std::tie(m_vertex_shader, m_fragment_shader));
86}
87
88void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
89 Image& image = m_dynamic_images[image_index];
90 std::vector<VkDescriptorImageInfo> image_infos;
91 std::vector<VkWriteDescriptorSet> updates;
92 image_infos.reserve(2);
93
94 updates.push_back(
95 CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0));
96 updates.push_back(
97 CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1));
98
99 m_device.GetLogical().UpdateDescriptorSets(updates, {});
100}
101
102void FXAA::UploadImages(Scheduler& scheduler) {
103 if (m_images_ready) {
104 return;
105 }
106
107 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
108 for (auto& image : m_dynamic_images) {
109 ClearColorImage(cmdbuf, *image.image);
110 }
111 });
112 scheduler.Finish();
113
114 m_images_ready = true;
115}
116
117void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
118 VkImageView* inout_image_view) {
119 const Image& image{m_dynamic_images[image_index]};
120 const VkImage input_image{*inout_image};
121 const VkImage output_image{*image.image};
122 const VkDescriptorSet descriptor_set{image.descriptor_sets[0]};
123 const VkFramebuffer framebuffer{*image.framebuffer};
124 const VkRenderPass renderpass{*m_renderpass};
125 const VkPipeline pipeline{*m_pipeline};
126 const VkPipelineLayout layout{*m_pipeline_layout};
127 const VkExtent2D extent{m_extent};
128
129 UploadImages(scheduler);
130 UpdateDescriptorSets(*inout_image_view, image_index);
131
132 scheduler.RequestOutsideRenderPassOperationContext();
133 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
134 TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
135 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
136 BeginRenderPass(cmdbuf, renderpass, framebuffer, extent);
137 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
138 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {});
139 cmdbuf.Draw(3, 1, 0, 0);
140 cmdbuf.EndRenderPass();
141 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
142 });
143
144 *inout_image = *image.image;
145 *inout_image_view = *image.image_view;
146}
147
148} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/present/anti_alias_pass.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14class StagingBufferPool;
15
16class FXAA final : public AntiAliasPass {
17public:
18 explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
19 VkExtent2D extent);
20 ~FXAA() override;
21
22 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
23 VkImageView* inout_image_view) override;
24
25private:
26 void CreateImages();
27 void CreateRenderPasses();
28 void CreateSampler();
29 void CreateShaders();
30 void CreateDescriptorPool();
31 void CreateDescriptorSetLayouts();
32 void CreateDescriptorSets();
33 void CreatePipelineLayouts();
34 void CreatePipelines();
35 void UpdateDescriptorSets(VkImageView image_view, size_t image_index);
36 void UploadImages(Scheduler& scheduler);
37
38 const Device& m_device;
39 MemoryAllocator& m_allocator;
40 const VkExtent2D m_extent;
41 const u32 m_image_count;
42
43 vk::ShaderModule m_vertex_shader{};
44 vk::ShaderModule m_fragment_shader{};
45 vk::DescriptorPool m_descriptor_pool{};
46 vk::DescriptorSetLayout m_descriptor_set_layout{};
47 vk::PipelineLayout m_pipeline_layout{};
48 vk::Pipeline m_pipeline{};
49 vk::RenderPass m_renderpass{};
50
51 struct Image {
52 vk::DescriptorSets descriptor_sets{};
53 vk::Framebuffer framebuffer{};
54 vk::Image image{};
55 vk::ImageView image_view{};
56 };
57 std::vector<Image> m_dynamic_images{};
58 bool m_images_ready{};
59
60 vk::Sampler m_sampler{};
61};
62
63} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/renderer_vulkan/vk_rasterizer.h"
5
6#include "common/settings.h"
7#include "video_core/framebuffer_config.h"
8#include "video_core/renderer_vulkan/present/fsr.h"
9#include "video_core/renderer_vulkan/present/fxaa.h"
10#include "video_core/renderer_vulkan/present/layer.h"
11#include "video_core/renderer_vulkan/present/present_push_constants.h"
12#include "video_core/renderer_vulkan/present/smaa.h"
13#include "video_core/renderer_vulkan/present/util.h"
14#include "video_core/renderer_vulkan/vk_blit_screen.h"
15#include "video_core/textures/decoders.h"
16
17namespace Vulkan {
18
19namespace {
20
21u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
22 using namespace VideoCore::Surface;
23 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
24}
25
26std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
27 return static_cast<std::size_t>(framebuffer.stride) *
28 static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer);
29}
30
31VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
32 switch (framebuffer.pixel_format) {
33 case Service::android::PixelFormat::Rgba8888:
34 case Service::android::PixelFormat::Rgbx8888:
35 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
36 case Service::android::PixelFormat::Rgb565:
37 return VK_FORMAT_R5G6B5_UNORM_PACK16;
38 case Service::android::PixelFormat::Bgra8888:
39 return VK_FORMAT_B8G8R8A8_UNORM;
40 default:
41 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
42 static_cast<u32>(framebuffer.pixel_format));
43 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
44 }
45}
46
47} // Anonymous namespace
48
49Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
50 Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
51 VkExtent2D output_size, VkDescriptorSetLayout layout)
52 : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
53 device_memory(device_memory_), image_count(image_count_) {
54 CreateDescriptorPool();
55 CreateDescriptorSets(layout);
56 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
57 CreateFSR(output_size);
58 }
59}
60
61Layer::~Layer() {
62 ReleaseRawImages();
63}
64
65void Layer::ConfigureDraw(PresentPushConstants* out_push_constants,
66 VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer,
67 VkSampler sampler, size_t image_index,
68 const Tegra::FramebufferConfig& framebuffer,
69 const Layout::FramebufferLayout& layout) {
70 const auto texture_info = rasterizer.AccelerateDisplay(
71 framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride);
72 const u32 texture_width = texture_info ? texture_info->width : framebuffer.width;
73 const u32 texture_height = texture_info ? texture_info->height : framebuffer.height;
74 const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
75 const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
76 const bool use_accelerated = texture_info.has_value();
77
78 RefreshResources(framebuffer);
79 SetAntiAliasPass();
80
81 // Finish any pending renderpass
82 scheduler.RequestOutsideRenderPassOperationContext();
83 scheduler.Wait(resource_ticks[image_index]);
84 SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); });
85
86 if (!use_accelerated) {
87 UpdateRawImage(framebuffer, image_index);
88 }
89
90 VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index];
91 VkImageView source_image_view =
92 texture_info ? texture_info->image_view : *raw_image_views[image_index];
93
94 anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view);
95
96 auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
97 const VkExtent2D render_extent{
98 .width = scaled_width,
99 .height = scaled_height,
100 };
101
102 if (fsr) {
103 source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view,
104 render_extent, crop_rect);
105 crop_rect = {0, 0, 1, 1};
106 }
107
108 SetMatrixData(*out_push_constants, layout);
109 SetVertexData(*out_push_constants, layout, crop_rect);
110
111 UpdateDescriptorSet(source_image_view, sampler, image_index);
112 *out_descriptor_set = descriptor_sets[image_index];
113}
114
115void Layer::CreateDescriptorPool() {
116 descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count);
117}
118
119void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) {
120 const std::vector layouts(image_count, layout);
121 descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts);
122}
123
124void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
125 const VkBufferCreateInfo ci{
126 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
127 .pNext = nullptr,
128 .flags = 0,
129 .size = CalculateBufferSize(framebuffer),
130 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
131 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
132 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
133 .queueFamilyIndexCount = 0,
134 .pQueueFamilyIndices = nullptr,
135 };
136
137 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
138}
139
140void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
141 const auto format = GetFormat(framebuffer);
142 resource_ticks.resize(image_count);
143 raw_images.resize(image_count);
144 raw_image_views.resize(image_count);
145
146 for (size_t i = 0; i < image_count; ++i) {
147 raw_images[i] =
148 CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format);
149 raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format);
150 }
151}
152
153void Layer::CreateFSR(VkExtent2D output_size) {
154 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size);
155}
156
157void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
158 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
159 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
160 return;
161 }
162
163 raw_width = framebuffer.width;
164 raw_height = framebuffer.height;
165 pixel_format = framebuffer.pixel_format;
166 anti_alias.reset();
167
168 ReleaseRawImages();
169 CreateStagingBuffer(framebuffer);
170 CreateRawImages(framebuffer);
171}
172
173void Layer::SetAntiAliasPass() {
174 if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) {
175 return;
176 }
177
178 anti_alias_setting = Settings::values.anti_aliasing.GetValue();
179
180 const VkExtent2D render_area{
181 .width = Settings::values.resolution_info.ScaleUp(raw_width),
182 .height = Settings::values.resolution_info.ScaleUp(raw_height),
183 };
184
185 switch (anti_alias_setting) {
186 case Settings::AntiAliasing::Fxaa:
187 anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area);
188 break;
189 case Settings::AntiAliasing::Smaa:
190 anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area);
191 break;
192 default:
193 anti_alias = std::make_unique<NoAA>();
194 break;
195 }
196}
197
198void Layer::ReleaseRawImages() {
199 for (const u64 tick : resource_ticks) {
200 scheduler.Wait(tick);
201 }
202 raw_images.clear();
203 buffer.reset();
204}
205
206u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const {
207 return GetSizeInBytes(framebuffer) * image_count;
208}
209
210u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
211 size_t image_index) const {
212 return GetSizeInBytes(framebuffer) * image_index;
213}
214
215void Layer::SetMatrixData(PresentPushConstants& data,
216 const Layout::FramebufferLayout& layout) const {
217 data.modelview_matrix =
218 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
219}
220
221void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout,
222 const Common::Rectangle<f32>& crop) const {
223 // Map the coordinates to the screen.
224 const auto& screen = layout.screen;
225 const auto x = static_cast<f32>(screen.left);
226 const auto y = static_cast<f32>(screen.top);
227 const auto w = static_cast<f32>(screen.GetWidth());
228 const auto h = static_cast<f32>(screen.GetHeight());
229
230 data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
231 data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
232 data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
233 data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
234}
235
236void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) {
237 const VkDescriptorImageInfo image_info{
238 .sampler = sampler,
239 .imageView = image_view,
240 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
241 };
242
243 const VkWriteDescriptorSet sampler_write{
244 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
245 .pNext = nullptr,
246 .dstSet = descriptor_sets[image_index],
247 .dstBinding = 0,
248 .dstArrayElement = 0,
249 .descriptorCount = 1,
250 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
251 .pImageInfo = &image_info,
252 .pBufferInfo = nullptr,
253 .pTexelBufferView = nullptr,
254 };
255
256 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {});
257}
258
259void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) {
260 const std::span<u8> mapped_span = buffer.Mapped();
261
262 const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
263
264 const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
265 const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr);
266
267 // TODO(Rodrigo): Read this from HLE
268 constexpr u32 block_height_log2 = 4;
269 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
270 const u64 linear_size{GetSizeInBytes(framebuffer)};
271 const u64 tiled_size{Tegra::Texture::CalculateSize(
272 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
273 Tegra::Texture::UnswizzleTexture(
274 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
275 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
276
277 const VkBufferImageCopy copy{
278 .bufferOffset = image_offset,
279 .bufferRowLength = 0,
280 .bufferImageHeight = 0,
281 .imageSubresource =
282 {
283 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
284 .mipLevel = 0,
285 .baseArrayLayer = 0,
286 .layerCount = 1,
287 },
288 .imageOffset = {.x = 0, .y = 0, .z = 0},
289 .imageExtent =
290 {
291 .width = framebuffer.width,
292 .height = framebuffer.height,
293 .depth = 1,
294 },
295 };
296 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
297 const VkImage image = *raw_images[index];
298 const VkImageMemoryBarrier base_barrier{
299 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
300 .pNext = nullptr,
301 .srcAccessMask = 0,
302 .dstAccessMask = 0,
303 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
304 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
305 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
306 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
307 .image = image,
308 .subresourceRange{
309 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
310 .baseMipLevel = 0,
311 .levelCount = 1,
312 .baseArrayLayer = 0,
313 .layerCount = 1,
314 },
315 };
316 VkImageMemoryBarrier read_barrier = base_barrier;
317 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
318 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
319 read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
320
321 VkImageMemoryBarrier write_barrier = base_barrier;
322 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
323 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
324 write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
325
326 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
327 read_barrier);
328 cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
329 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
330 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
331 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
332 0, write_barrier);
333 });
334}
335
336} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/host1x/gpu_device_memory_manager.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Layout {
11struct FramebufferLayout;
12}
13
14namespace Tegra {
15struct FramebufferConfig;
16}
17
18namespace Service::android {
19enum class PixelFormat : u32;
20}
21
22namespace Settings {
23enum class AntiAliasing : u32;
24}
25
26namespace Vulkan {
27
28class AntiAliasPass;
29class Device;
30class FSR;
31class MemoryAllocator;
32struct PresentPushConstants;
33class RasterizerVulkan;
34class Scheduler;
35
36class Layer final {
37public:
38 explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
39 Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
40 VkExtent2D output_size, VkDescriptorSetLayout layout);
41 ~Layer();
42
43 void ConfigureDraw(PresentPushConstants* out_push_constants,
44 VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer,
45 VkSampler sampler, size_t image_index,
46 const Tegra::FramebufferConfig& framebuffer,
47 const Layout::FramebufferLayout& layout);
48
49private:
50 void CreateDescriptorPool();
51 void CreateDescriptorSets(VkDescriptorSetLayout layout);
52 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
53 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
54 void CreateFSR(VkExtent2D output_size);
55
56 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
57 void SetAntiAliasPass();
58 void ReleaseRawImages();
59
60 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
61 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const;
62
63 void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const;
64 void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout,
65 const Common::Rectangle<f32>& crop) const;
66 void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index);
67 void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index);
68
69private:
70 const Device& device;
71 MemoryAllocator& memory_allocator;
72 Scheduler& scheduler;
73 Tegra::MaxwellDeviceMemoryManager& device_memory;
74 const size_t image_count{};
75 vk::DescriptorPool descriptor_pool{};
76 vk::DescriptorSets descriptor_sets{};
77
78 vk::Buffer buffer{};
79 std::vector<vk::Image> raw_images{};
80 std::vector<vk::ImageView> raw_image_views{};
81 u32 raw_width{};
82 u32 raw_height{};
83 Service::android::PixelFormat pixel_format{};
84
85 Settings::AntiAliasing anti_alias_setting{};
86 std::unique_ptr<AntiAliasPass> anti_alias{};
87
88 std::unique_ptr<FSR> fsr{};
89 std::vector<u64> resource_ticks{};
90};
91
92} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Vulkan {
9
10struct ScreenRectVertex {
11 ScreenRectVertex() = default;
12 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {}
13
14 std::array<f32, 2> position;
15 std::array<f32, 2> tex_coord;
16};
17
18static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
19 // clang-format off
20 return { 2.f / width, 0.f, 0.f, 0.f,
21 0.f, 2.f / height, 0.f, 0.f,
22 0.f, 0.f, 1.f, 0.f,
23 -1.f, -1.f, 0.f, 1.f};
24 // clang-format on
25}
26
27struct PresentPushConstants {
28 std::array<f32, 4 * 4> modelview_matrix;
29 std::array<ScreenRectVertex, 4> vertices;
30};
31
32static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large");
33
34} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <list>
5
6#include "common/assert.h"
7#include "common/polyfill_ranges.h"
8
9#include "video_core/renderer_vulkan/present/smaa.h"
10#include "video_core/renderer_vulkan/present/util.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_shader_util.h"
13#include "video_core/smaa_area_tex.h"
14#include "video_core/smaa_search_tex.h"
15#include "video_core/vulkan_common/vulkan_device.h"
16
17#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h"
18#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h"
19#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h"
20#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h"
21#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h"
22#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h"
23
24namespace Vulkan {
25
26SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
27 : m_device(device), m_allocator(allocator), m_extent(extent),
28 m_image_count(static_cast<u32>(image_count)) {
29 CreateImages();
30 CreateRenderPasses();
31 CreateSampler();
32 CreateShaders();
33 CreateDescriptorPool();
34 CreateDescriptorSetLayouts();
35 CreateDescriptorSets();
36 CreatePipelineLayouts();
37 CreatePipelines();
38}
39
40SMAA::~SMAA() = default;
41
42void SMAA::CreateImages() {
43 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
44 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
45
46 m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
47 m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM);
48
49 m_static_image_views[Area] =
50 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
51 m_static_image_views[Search] =
52 CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM);
53
54 for (u32 i = 0; i < m_image_count; i++) {
55 Images& images = m_dynamic_images.emplace_back();
56
57 images.images[Blend] =
58 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
59 images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
60 images.images[Output] =
61 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
62
63 images.image_views[Blend] =
64 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
65 images.image_views[Edges] =
66 CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT);
67 images.image_views[Output] =
68 CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT);
69 }
70}
71
72void SMAA::CreateRenderPasses() {
73 m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT);
74 m_renderpasses[BlendingWeightCalculation] =
75 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
76 m_renderpasses[NeighborhoodBlending] =
77 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
78
79 for (auto& images : m_dynamic_images) {
80 images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer(
81 m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent);
82
83 images.framebuffers[BlendingWeightCalculation] =
84 CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation],
85 images.image_views[Blend], m_extent);
86
87 images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer(
88 m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent);
89 }
90}
91
92void SMAA::CreateSampler() {
93 m_sampler = CreateWrappedSampler(m_device);
94}
95
96void SMAA::CreateShaders() {
97 // These match the order of the SMAAStage enum
98 static constexpr std::array vert_shader_sources{
99 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
100 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
101 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
102 };
103 static constexpr std::array frag_shader_sources{
104 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
105 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
106 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
107 };
108
109 for (size_t i = 0; i < MaxSMAAStage; i++) {
110 m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]);
111 m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]);
112 }
113}
114
115void SMAA::CreateDescriptorPool() {
116 // Edge detection: 1 descriptor
117 // Blending weight calculation: 3 descriptors
118 // Neighborhood blending: 2 descriptors
119
120 // 6 descriptors, 3 descriptor sets per image
121 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count);
122}
123
124void SMAA::CreateDescriptorSetLayouts() {
125 m_descriptor_set_layouts[EdgeDetection] =
126 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
127 m_descriptor_set_layouts[BlendingWeightCalculation] =
128 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
129 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
130 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
131 m_descriptor_set_layouts[NeighborhoodBlending] =
132 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
133 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
134}
135
136void SMAA::CreateDescriptorSets() {
137 std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size());
138 std::ranges::transform(m_descriptor_set_layouts, layouts.begin(),
139 [](auto& layout) { return *layout; });
140
141 for (auto& images : m_dynamic_images) {
142 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
143 }
144}
145
146void SMAA::CreatePipelineLayouts() {
147 for (size_t i = 0; i < MaxSMAAStage; i++) {
148 m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]);
149 }
150}
151
152void SMAA::CreatePipelines() {
153 for (size_t i = 0; i < MaxSMAAStage; i++) {
154 m_pipelines[i] =
155 CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i],
156 std::tie(m_vertex_shaders[i], m_fragment_shaders[i]));
157 }
158}
159
160void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
161 Images& images = m_dynamic_images[image_index];
162 std::vector<VkDescriptorImageInfo> image_infos;
163 std::vector<VkWriteDescriptorSet> updates;
164 image_infos.reserve(6);
165
166 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
167 images.descriptor_sets[EdgeDetection], 0));
168
169 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges],
170 images.descriptor_sets[BlendingWeightCalculation],
171 0));
172 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area],
173 images.descriptor_sets[BlendingWeightCalculation],
174 1));
175 updates.push_back(
176 CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search],
177 images.descriptor_sets[BlendingWeightCalculation], 2));
178
179 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
180 images.descriptor_sets[NeighborhoodBlending], 0));
181 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend],
182 images.descriptor_sets[NeighborhoodBlending], 1));
183
184 m_device.GetLogical().UpdateDescriptorSets(updates, {});
185}
186
187void SMAA::UploadImages(Scheduler& scheduler) {
188 if (m_images_ready) {
189 return;
190 }
191
192 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
193 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
194
195 UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
196 VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
197 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
198 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
199
200 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
201 for (auto& images : m_dynamic_images) {
202 for (size_t i = 0; i < MaxDynamicImage; i++) {
203 ClearColorImage(cmdbuf, *images.images[i]);
204 }
205 }
206 });
207 scheduler.Finish();
208
209 m_images_ready = true;
210}
211
212void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
213 VkImageView* inout_image_view) {
214 Images& images = m_dynamic_images[image_index];
215
216 VkImage input_image = *inout_image;
217 VkImage output_image = *images.images[Output];
218 VkImage edges_image = *images.images[Edges];
219 VkImage blend_image = *images.images[Blend];
220
221 VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection];
222 VkDescriptorSet blending_weight_calculation_descriptor_set =
223 images.descriptor_sets[BlendingWeightCalculation];
224 VkDescriptorSet neighborhood_blending_descriptor_set =
225 images.descriptor_sets[NeighborhoodBlending];
226
227 VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection];
228 VkFramebuffer blending_weight_calculation_framebuffer =
229 *images.framebuffers[BlendingWeightCalculation];
230 VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending];
231
232 UploadImages(scheduler);
233 UpdateDescriptorSets(*inout_image_view, image_index);
234
235 scheduler.RequestOutsideRenderPassOperationContext();
236 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
237 TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
238 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
239 BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer,
240 m_extent);
241 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
242 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
243 *m_pipeline_layouts[EdgeDetection], 0,
244 edge_detection_descriptor_set, {});
245 cmdbuf.Draw(3, 1, 0, 0);
246 cmdbuf.EndRenderPass();
247
248 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
249 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
250 BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation],
251 blending_weight_calculation_framebuffer, m_extent);
252 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
253 *m_pipelines[BlendingWeightCalculation]);
254 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
255 *m_pipeline_layouts[BlendingWeightCalculation], 0,
256 blending_weight_calculation_descriptor_set, {});
257 cmdbuf.Draw(3, 1, 0, 0);
258 cmdbuf.EndRenderPass();
259
260 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
261 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
262 BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending],
263 neighborhood_blending_framebuffer, m_extent);
264 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
265 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
266 *m_pipeline_layouts[NeighborhoodBlending], 0,
267 neighborhood_blending_descriptor_set, {});
268 cmdbuf.Draw(3, 1, 0, 0);
269 cmdbuf.EndRenderPass();
270 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
271 });
272
273 *inout_image = *images.images[Output];
274 *inout_image_view = *images.image_views[Output];
275}
276
277} // 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 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include "video_core/renderer_vulkan/present/anti_alias_pass.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h" 8#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h" 9#include "video_core/vulkan_common/vulkan_wrapper.h"
9 10
@@ -13,12 +14,14 @@ class Device;
13class Scheduler; 14class Scheduler;
14class StagingBufferPool; 15class StagingBufferPool;
15 16
16class SMAA { 17class SMAA final : public AntiAliasPass {
17public: 18public:
18 explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, 19 explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
19 VkExtent2D extent); 20 VkExtent2D extent);
20 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, 21 ~SMAA() override;
21 VkImageView source_image_view); 22
23 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
24 VkImageView* inout_image_view) override;
22 25
23private: 26private:
24 enum SMAAStage { 27 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <list>
5
6#include "common/assert.h" 4#include "common/assert.h"
7#include "common/polyfill_ranges.h" 5#include "common/polyfill_ranges.h"
8 6#include "video_core/renderer_vulkan/present/util.h"
9#include "video_core/renderer_vulkan/vk_scheduler.h"
10#include "video_core/renderer_vulkan/vk_shader_util.h"
11#include "video_core/renderer_vulkan/vk_smaa.h"
12#include "video_core/smaa_area_tex.h"
13#include "video_core/smaa_search_tex.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15
16#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h"
17#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h"
18#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h"
19#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h"
20#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h"
21#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h"
22 7
23namespace Vulkan { 8namespace Vulkan {
24namespace {
25 9
26#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) 10vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) {
11 const VkBufferCreateInfo dst_buffer_info{
12 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
13 .pNext = nullptr,
14 .flags = 0,
15 .size = size,
16 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
17 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
18 .queueFamilyIndexCount = 0,
19 .pQueueFamilyIndices = nullptr,
20 };
21 return allocator.CreateBuffer(dst_buffer_info, usage);
22}
27 23
28vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { 24vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) {
29 const VkImageCreateInfo image_ci{ 25 const VkImageCreateInfo image_ci{
@@ -48,7 +44,7 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions,
48} 44}
49 45
50void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, 46void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
51 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { 47 VkImageLayout source_layout) {
52 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | 48 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
53 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; 49 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
54 const VkImageMemoryBarrier barrier{ 50 const VkImageMemoryBarrier barrier{
@@ -75,7 +71,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
75 71
76void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, 72void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
77 vk::Image& image, VkExtent2D dimensions, VkFormat format, 73 vk::Image& image, VkExtent2D dimensions, VkFormat format,
78 std::span<const u8> initial_contents = {}) { 74 std::span<const u8> initial_contents) {
79 const VkBufferCreateInfo upload_ci = { 75 const VkBufferCreateInfo upload_ci = {
80 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 76 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
81 .pNext = nullptr, 77 .pNext = nullptr,
@@ -114,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
114 scheduler.Finish(); 110 scheduler.Finish();
115} 111}
116 112
113void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer,
114 VkExtent3D extent) {
115 const VkImageMemoryBarrier read_barrier{
116 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
117 .pNext = nullptr,
118 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
119 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
120 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
121 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
122 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
123 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
124 .image = image,
125 .subresourceRange{
126 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
127 .baseMipLevel = 0,
128 .levelCount = VK_REMAINING_MIP_LEVELS,
129 .baseArrayLayer = 0,
130 .layerCount = VK_REMAINING_ARRAY_LAYERS,
131 },
132 };
133 const VkImageMemoryBarrier image_write_barrier{
134 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
135 .pNext = nullptr,
136 .srcAccessMask = 0,
137 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
138 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
139 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
140 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
141 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
142 .image = image,
143 .subresourceRange{
144 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
145 .baseMipLevel = 0,
146 .levelCount = VK_REMAINING_MIP_LEVELS,
147 .baseArrayLayer = 0,
148 .layerCount = VK_REMAINING_ARRAY_LAYERS,
149 },
150 };
151 static constexpr VkMemoryBarrier memory_write_barrier{
152 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
153 .pNext = nullptr,
154 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
155 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
156 };
157 const VkBufferImageCopy copy{
158 .bufferOffset = 0,
159 .bufferRowLength = 0,
160 .bufferImageHeight = 0,
161 .imageSubresource{
162 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
163 .mipLevel = 0,
164 .baseArrayLayer = 0,
165 .layerCount = 1,
166 },
167 .imageOffset{.x = 0, .y = 0, .z = 0},
168 .imageExtent{extent},
169 };
170 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
171 read_barrier);
172 cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy);
173 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
174 memory_write_barrier, nullptr, image_write_barrier);
175}
176
117vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { 177vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
118 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 178 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
119 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 179 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -131,16 +191,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF
131 }); 191 });
132} 192}
133 193
134vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { 194vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
195 VkImageLayout initial_layout) {
135 const VkAttachmentDescription attachment{ 196 const VkAttachmentDescription attachment{
136 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, 197 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
137 .format = format, 198 .format = format,
138 .samples = VK_SAMPLE_COUNT_1_BIT, 199 .samples = VK_SAMPLE_COUNT_1_BIT,
139 .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, 200 .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
201 : VK_ATTACHMENT_LOAD_OP_LOAD,
140 .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 202 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
141 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, 203 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
142 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, 204 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
143 .initialLayout = VK_IMAGE_LAYOUT_GENERAL, 205 .initialLayout = initial_layout,
144 .finalLayout = VK_IMAGE_LAYOUT_GENERAL, 206 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
145 }; 207 };
146 208
@@ -200,13 +262,13 @@ vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& r
200 }); 262 });
201} 263}
202 264
203vk::Sampler CreateWrappedSampler(const Device& device) { 265vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) {
204 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ 266 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
205 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 267 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
206 .pNext = nullptr, 268 .pNext = nullptr,
207 .flags = 0, 269 .flags = 0,
208 .magFilter = VK_FILTER_LINEAR, 270 .magFilter = filter,
209 .minFilter = VK_FILTER_LINEAR, 271 .minFilter = filter,
210 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, 272 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
211 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 273 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
212 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 274 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
@@ -233,30 +295,34 @@ vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const
233 }); 295 });
234} 296}
235 297
236vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, 298vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors,
237 u32 max_sets) { 299 size_t max_sets,
238 const VkDescriptorPoolSize pool_size{ 300 std::initializer_list<VkDescriptorType> types) {
239 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 301 std::vector<VkDescriptorPoolSize> pool_sizes(types.size());
240 .descriptorCount = static_cast<u32>(max_descriptors), 302 for (u32 i = 0; i < types.size(); i++) {
241 }; 303 pool_sizes[i] = VkDescriptorPoolSize{
304 .type = std::data(types)[i],
305 .descriptorCount = static_cast<u32>(max_descriptors),
306 };
307 }
242 308
243 return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ 309 return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{
244 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 310 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
245 .pNext = nullptr, 311 .pNext = nullptr,
246 .flags = 0, 312 .flags = 0,
247 .maxSets = max_sets, 313 .maxSets = static_cast<u32>(max_sets),
248 .poolSizeCount = 1, 314 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
249 .pPoolSizes = &pool_size, 315 .pPoolSizes = pool_sizes.data(),
250 }); 316 });
251} 317}
252 318
253vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, 319vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(
254 u32 max_sampler_bindings) { 320 const Device& device, std::initializer_list<VkDescriptorType> types) {
255 std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings); 321 std::vector<VkDescriptorSetLayoutBinding> bindings(types.size());
256 for (u32 i = 0; i < max_sampler_bindings; i++) { 322 for (size_t i = 0; i < types.size(); i++) {
257 bindings[i] = { 323 bindings[i] = {
258 .binding = i, 324 .binding = static_cast<u32>(i),
259 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 325 .descriptorType = std::data(types)[i],
260 .descriptorCount = 1, 326 .descriptorCount = 1,
261 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 327 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
262 .pImmutableSamplers = nullptr, 328 .pImmutableSamplers = nullptr,
@@ -298,7 +364,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
298 364
299vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, 365vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
300 vk::PipelineLayout& layout, 366 vk::PipelineLayout& layout,
301 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { 367 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
368 bool enable_blending) {
302 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ 369 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
303 { 370 {
304 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 371 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -376,7 +443,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
376 .alphaToOneEnable = VK_FALSE, 443 .alphaToOneEnable = VK_FALSE,
377 }; 444 };
378 445
379 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ 446 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
380 .blendEnable = VK_FALSE, 447 .blendEnable = VK_FALSE,
381 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, 448 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
382 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, 449 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
@@ -388,6 +455,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
388 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 455 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
389 }; 456 };
390 457
458 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{
459 .blendEnable = VK_TRUE,
460 .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
461 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
462 .colorBlendOp = VK_BLEND_OP_ADD,
463 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
464 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
465 .alphaBlendOp = VK_BLEND_OP_ADD,
466 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
467 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
468 };
469
391 const VkPipelineColorBlendStateCreateInfo color_blend_ci{ 470 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
392 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 471 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
393 .pNext = nullptr, 472 .pNext = nullptr,
@@ -395,7 +474,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
395 .logicOpEnable = VK_FALSE, 474 .logicOpEnable = VK_FALSE,
396 .logicOp = VK_LOGIC_OP_COPY, 475 .logicOp = VK_LOGIC_OP_COPY,
397 .attachmentCount = 1, 476 .attachmentCount = 1,
398 .pAttachments = &color_blend_attachment, 477 .pAttachments =
478 enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled,
399 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, 479 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
400 }; 480 };
401 481
@@ -459,6 +539,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>
459 }; 539 };
460} 540}
461 541
542vk::Sampler CreateBilinearSampler(const Device& device) {
543 const VkSamplerCreateInfo ci{
544 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
545 .pNext = nullptr,
546 .flags = 0,
547 .magFilter = VK_FILTER_LINEAR,
548 .minFilter = VK_FILTER_LINEAR,
549 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
550 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
551 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
552 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
553 .mipLodBias = 0.0f,
554 .anisotropyEnable = VK_FALSE,
555 .maxAnisotropy = 0.0f,
556 .compareEnable = VK_FALSE,
557 .compareOp = VK_COMPARE_OP_NEVER,
558 .minLod = 0.0f,
559 .maxLod = 0.0f,
560 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
561 .unnormalizedCoordinates = VK_FALSE,
562 };
563
564 return device.GetLogical().CreateSampler(ci);
565}
566
567vk::Sampler CreateNearestNeighborSampler(const Device& device) {
568 const VkSamplerCreateInfo ci_nn{
569 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
570 .pNext = nullptr,
571 .flags = 0,
572 .magFilter = VK_FILTER_NEAREST,
573 .minFilter = VK_FILTER_NEAREST,
574 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
575 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
576 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
577 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
578 .mipLodBias = 0.0f,
579 .anisotropyEnable = VK_FALSE,
580 .maxAnisotropy = 0.0f,
581 .compareEnable = VK_FALSE,
582 .compareOp = VK_COMPARE_OP_NEVER,
583 .minLod = 0.0f,
584 .maxLod = 0.0f,
585 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
586 .unnormalizedCoordinates = VK_FALSE,
587 };
588
589 return device.GetLogical().CreateSampler(ci_nn);
590}
591
462void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { 592void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
463 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ 593 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
464 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 594 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
@@ -471,12 +601,12 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
471 cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); 601 cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources);
472} 602}
473 603
474void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, 604void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
475 VkFramebuffer framebuffer, VkExtent2D extent) { 605 VkExtent2D extent) {
476 const VkRenderPassBeginInfo renderpass_bi{ 606 const VkRenderPassBeginInfo renderpass_bi{
477 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 607 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
478 .pNext = nullptr, 608 .pNext = nullptr,
479 .renderPass = *render_pass, 609 .renderPass = render_pass,
480 .framebuffer = framebuffer, 610 .framebuffer = framebuffer,
481 .renderArea{ 611 .renderArea{
482 .offset{}, 612 .offset{},
@@ -503,248 +633,4 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass,
503 cmdbuf.SetScissor(0, scissor); 633 cmdbuf.SetScissor(0, scissor);
504} 634}
505 635
506} // Anonymous namespace
507
508SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
509 : m_device(device), m_allocator(allocator), m_extent(extent),
510 m_image_count(static_cast<u32>(image_count)) {
511 CreateImages();
512 CreateRenderPasses();
513 CreateSampler();
514 CreateShaders();
515 CreateDescriptorPool();
516 CreateDescriptorSetLayouts();
517 CreateDescriptorSets();
518 CreatePipelineLayouts();
519 CreatePipelines();
520}
521
522void SMAA::CreateImages() {
523 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
524 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
525
526 m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
527 m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM);
528
529 m_static_image_views[Area] =
530 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
531 m_static_image_views[Search] =
532 CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM);
533
534 for (u32 i = 0; i < m_image_count; i++) {
535 Images& images = m_dynamic_images.emplace_back();
536
537 images.images[Blend] =
538 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
539 images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
540 images.images[Output] =
541 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
542
543 images.image_views[Blend] =
544 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
545 images.image_views[Edges] =
546 CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT);
547 images.image_views[Output] =
548 CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT);
549 }
550}
551
552void SMAA::CreateRenderPasses() {
553 m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT);
554 m_renderpasses[BlendingWeightCalculation] =
555 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
556 m_renderpasses[NeighborhoodBlending] =
557 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
558
559 for (auto& images : m_dynamic_images) {
560 images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer(
561 m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent);
562
563 images.framebuffers[BlendingWeightCalculation] =
564 CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation],
565 images.image_views[Blend], m_extent);
566
567 images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer(
568 m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent);
569 }
570}
571
572void SMAA::CreateSampler() {
573 m_sampler = CreateWrappedSampler(m_device);
574}
575
576void SMAA::CreateShaders() {
577 // These match the order of the SMAAStage enum
578 static constexpr std::array vert_shader_sources{
579 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
580 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
581 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
582 };
583 static constexpr std::array frag_shader_sources{
584 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
585 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
586 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
587 };
588
589 for (size_t i = 0; i < MaxSMAAStage; i++) {
590 m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]);
591 m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]);
592 }
593}
594
595void SMAA::CreateDescriptorPool() {
596 // Edge detection: 1 descriptor
597 // Blending weight calculation: 3 descriptors
598 // Neighborhood blending: 2 descriptors
599
600 // 6 descriptors, 3 descriptor sets per image
601 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count);
602}
603
604void SMAA::CreateDescriptorSetLayouts() {
605 m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1);
606 m_descriptor_set_layouts[BlendingWeightCalculation] =
607 CreateWrappedDescriptorSetLayout(m_device, 3);
608 m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2);
609}
610
611void SMAA::CreateDescriptorSets() {
612 std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size());
613 std::ranges::transform(m_descriptor_set_layouts, layouts.begin(),
614 [](auto& layout) { return *layout; });
615
616 for (auto& images : m_dynamic_images) {
617 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
618 }
619}
620
621void SMAA::CreatePipelineLayouts() {
622 for (size_t i = 0; i < MaxSMAAStage; i++) {
623 m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]);
624 }
625}
626
627void SMAA::CreatePipelines() {
628 for (size_t i = 0; i < MaxSMAAStage; i++) {
629 m_pipelines[i] =
630 CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i],
631 std::tie(m_vertex_shaders[i], m_fragment_shaders[i]));
632 }
633}
634
635void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
636 Images& images = m_dynamic_images[image_index];
637 std::vector<VkDescriptorImageInfo> image_infos;
638 std::vector<VkWriteDescriptorSet> updates;
639 image_infos.reserve(6);
640
641 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
642 images.descriptor_sets[EdgeDetection], 0));
643
644 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges],
645 images.descriptor_sets[BlendingWeightCalculation],
646 0));
647 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area],
648 images.descriptor_sets[BlendingWeightCalculation],
649 1));
650 updates.push_back(
651 CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search],
652 images.descriptor_sets[BlendingWeightCalculation], 2));
653
654 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
655 images.descriptor_sets[NeighborhoodBlending], 0));
656 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend],
657 images.descriptor_sets[NeighborhoodBlending], 1));
658
659 m_device.GetLogical().UpdateDescriptorSets(updates, {});
660}
661
662void SMAA::UploadImages(Scheduler& scheduler) {
663 if (m_images_ready) {
664 return;
665 }
666
667 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
668 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
669
670 UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
671 VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
674
675 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
676 for (auto& images : m_dynamic_images) {
677 for (size_t i = 0; i < MaxDynamicImage; i++) {
678 ClearColorImage(cmdbuf, *images.images[i]);
679 }
680 }
681 });
682 scheduler.Finish();
683
684 m_images_ready = true;
685}
686
687VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
688 VkImageView source_image_view) {
689 Images& images = m_dynamic_images[image_index];
690
691 VkImage output_image = *images.images[Output];
692 VkImage edges_image = *images.images[Edges];
693 VkImage blend_image = *images.images[Blend];
694
695 VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection];
696 VkDescriptorSet blending_weight_calculation_descriptor_set =
697 images.descriptor_sets[BlendingWeightCalculation];
698 VkDescriptorSet neighborhood_blending_descriptor_set =
699 images.descriptor_sets[NeighborhoodBlending];
700
701 VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection];
702 VkFramebuffer blending_weight_calculation_framebuffer =
703 *images.framebuffers[BlendingWeightCalculation];
704 VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending];
705
706 UploadImages(scheduler);
707 UpdateDescriptorSets(source_image_view, image_index);
708
709 scheduler.RequestOutsideRenderPassOperationContext();
710 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,
714 m_extent);
715 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
716 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
717 *m_pipeline_layouts[EdgeDetection], 0,
718 edge_detection_descriptor_set, {});
719 cmdbuf.Draw(3, 1, 0, 0);
720 cmdbuf.EndRenderPass();
721
722 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
723 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
724 BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation],
725 blending_weight_calculation_framebuffer, m_extent);
726 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
727 *m_pipelines[BlendingWeightCalculation]);
728 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
729 *m_pipeline_layouts[BlendingWeightCalculation], 0,
730 blending_weight_calculation_descriptor_set, {});
731 cmdbuf.Draw(3, 1, 0, 0);
732 cmdbuf.EndRenderPass();
733
734 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
735 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
736 BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending],
737 neighborhood_blending_framebuffer, m_extent);
738 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
739 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
740 *m_pipeline_layouts[NeighborhoodBlending], 0,
741 neighborhood_blending_descriptor_set, {});
742 cmdbuf.Draw(3, 1, 0, 0);
743 cmdbuf.EndRenderPass();
744 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
745 });
746
747 return *images.image_views[Output];
748}
749
750} // namespace Vulkan 636} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/vk_scheduler.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0])))
13
14vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage);
15
16vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format);
17void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
18 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL);
19void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
20 vk::Image& image, VkExtent2D dimensions, VkFormat format,
21 std::span<const u8> initial_contents = {});
22void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer,
23 VkExtent3D extent);
24void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image);
25
26vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format);
27vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
28 VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL);
29vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass,
30 vk::ImageView& dest_image, VkExtent2D extent);
31vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR);
32vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code);
33vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors,
34 size_t max_sets,
35 std::initializer_list<VkDescriptorType> types = {
36 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
37vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(
38 const Device& device, std::initializer_list<VkDescriptorType> types);
39vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool,
40 vk::Span<VkDescriptorSetLayout> layouts);
41vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
42 vk::DescriptorSetLayout& layout);
43vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
44 vk::PipelineLayout& layout,
45 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
46 bool enable_blending = false);
47VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
48 VkSampler sampler, VkImageView view,
49 VkDescriptorSet set, u32 binding);
50vk::Sampler CreateBilinearSampler(const Device& device);
51vk::Sampler CreateNearestNeighborSampler(const Device& device);
52
53void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
54 VkExtent2D extent);
55
56} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/frontend/framebuffer_layout.h"
5#include "video_core/framebuffer_config.h"
6#include "video_core/host_shaders/vulkan_present_vert_spv.h"
7#include "video_core/renderer_vulkan/present/layer.h"
8#include "video_core/renderer_vulkan/present/present_push_constants.h"
9#include "video_core/renderer_vulkan/present/util.h"
10#include "video_core/renderer_vulkan/present/window_adapt_pass.h"
11#include "video_core/renderer_vulkan/vk_present_manager.h"
12#include "video_core/renderer_vulkan/vk_shader_util.h"
13#include "video_core/vulkan_common/vulkan_device.h"
14#include "video_core/vulkan_common/vulkan_memory_allocator.h"
15
16namespace Vulkan {
17
18WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format,
19 vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_)
20 : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) {
21 CreateDescriptorSetLayout();
22 CreatePipelineLayout();
23 CreateVertexShader();
24 CreateRenderPass(frame_format);
25 CreatePipeline();
26}
27
28WindowAdaptPass::~WindowAdaptPass() = default;
29
30void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index,
31 std::list<Layer>& layers,
32 std::span<const Tegra::FramebufferConfig> configs,
33 const Layout::FramebufferLayout& layout, Frame* dst) {
34
35 const VkFramebuffer host_framebuffer{*dst->framebuffer};
36 const VkRenderPass renderpass{*render_pass};
37 const VkPipeline graphics_pipeline{*pipeline};
38 const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
39 const VkExtent2D render_area{
40 .width = dst->width,
41 .height = dst->height,
42 };
43
44 const size_t layer_count = configs.size();
45 std::vector<PresentPushConstants> push_constants(layer_count);
46 std::vector<VkDescriptorSet> descriptor_sets(layer_count);
47
48 auto layer_it = layers.begin();
49 for (size_t i = 0; i < layer_count; i++) {
50 layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
51 image_index, configs[i], layout);
52 layer_it++;
53 }
54
55 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
56 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
57 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
58 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
59 const VkClearAttachment clear_attachment{
60 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
61 .colorAttachment = 0,
62 .clearValue =
63 {
64 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
65 },
66 };
67 const VkClearRect clear_rect{
68 .rect =
69 {
70 .offset = {0, 0},
71 .extent = render_area,
72 },
73 .baseArrayLayer = 0,
74 .layerCount = 1,
75 };
76
77 BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
78 cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
79
80 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
81 for (size_t i = 0; i < layer_count; i++) {
82 cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
83 push_constants[i]);
84 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
85 descriptor_sets[i], {});
86 cmdbuf.Draw(4, 1, 0, 0);
87 }
88
89 cmdbuf.EndRenderPass();
90 });
91}
92
93VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() {
94 return *descriptor_set_layout;
95}
96
97VkRenderPass WindowAdaptPass::GetRenderPass() {
98 return *render_pass;
99}
100
101void WindowAdaptPass::CreateDescriptorSetLayout() {
102 descriptor_set_layout =
103 CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
104}
105
106void WindowAdaptPass::CreatePipelineLayout() {
107 const VkPushConstantRange range{
108 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
109 .offset = 0,
110 .size = sizeof(PresentPushConstants),
111 };
112
113 pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{
114 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
115 .pNext = nullptr,
116 .flags = 0,
117 .setLayoutCount = 1,
118 .pSetLayouts = descriptor_set_layout.address(),
119 .pushConstantRangeCount = 1,
120 .pPushConstantRanges = &range,
121 });
122}
123
124void WindowAdaptPass::CreateVertexShader() {
125 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
126}
127
128void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
129 render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
130}
131
132void WindowAdaptPass::CreatePipeline() {
133 pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
134 std::tie(vertex_shader, fragment_shader), false);
135}
136
137} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7
8#include "common/math_util.h"
9#include "video_core/vulkan_common/vulkan_wrapper.h"
10
11namespace Layout {
12struct FramebufferLayout;
13}
14
15namespace Tegra {
16struct FramebufferConfig;
17}
18
19namespace Vulkan {
20
21class Device;
22struct Frame;
23class Layer;
24class Scheduler;
25class RasterizerVulkan;
26
27class WindowAdaptPass final {
28public:
29 explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler,
30 vk::ShaderModule&& fragment_shader);
31 ~WindowAdaptPass();
32
33 void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index,
34 std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs,
35 const Layout::FramebufferLayout& layout, Frame* dst);
36
37 VkDescriptorSetLayout GetDescriptorSetLayout();
38 VkRenderPass GetRenderPass();
39
40private:
41 void CreateDescriptorSetLayout();
42 void CreatePipelineLayout();
43 void CreateVertexShader();
44 void CreateRenderPass(VkFormat frame_format);
45 void CreatePipeline();
46
47private:
48 const Device& device;
49 vk::DescriptorSetLayout descriptor_set_layout;
50 vk::PipelineLayout pipeline_layout;
51 vk::Sampler sampler;
52 vk::ShaderModule vertex_shader;
53 vk::ShaderModule fragment_shader;
54 vk::RenderPass render_pass;
55 vk::Pipeline pipeline;
56};
57
58} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 1631276c6..48a105327 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -20,12 +20,14 @@
20#include "core/frontend/graphics_context.h" 20#include "core/frontend/graphics_context.h"
21#include "core/telemetry_session.h" 21#include "core/telemetry_session.h"
22#include "video_core/gpu.h" 22#include "video_core/gpu.h"
23#include "video_core/renderer_vulkan/present/util.h"
23#include "video_core/renderer_vulkan/renderer_vulkan.h" 24#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 25#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_rasterizer.h" 26#include "video_core/renderer_vulkan/vk_rasterizer.h"
26#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
27#include "video_core/renderer_vulkan/vk_state_tracker.h" 28#include "video_core/renderer_vulkan/vk_state_tracker.h"
28#include "video_core/renderer_vulkan/vk_swapchain.h" 29#include "video_core/renderer_vulkan/vk_swapchain.h"
30#include "video_core/textures/decoders.h"
29#include "video_core/vulkan_common/vulkan_debug_callback.h" 31#include "video_core/vulkan_common/vulkan_debug_callback.h"
30#include "video_core/vulkan_common/vulkan_device.h" 32#include "video_core/vulkan_common/vulkan_device.h"
31#include "video_core/vulkan_common/vulkan_instance.h" 33#include "video_core/vulkan_common/vulkan_instance.h"
@@ -97,10 +99,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
97 render_window.GetFramebufferLayout().height), 99 render_window.GetFramebufferLayout().height),
98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 100 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
99 surface), 101 surface),
100 blit_screen(device_memory, render_window, device, memory_allocator, swapchain, 102 blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler),
101 present_manager, scheduler, screen_info), 103 blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler),
102 rasterizer(render_window, gpu, device_memory, screen_info, device, memory_allocator, 104 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
103 state_tracker, scheduler) { 105 scheduler) {
104 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 106 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
105 turbo_mode.emplace(instance, dld); 107 turbo_mode.emplace(instance, dld);
106 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); 108 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
@@ -116,25 +118,22 @@ RendererVulkan::~RendererVulkan() {
116 void(device.GetLogical().WaitIdle()); 118 void(device.GetLogical().WaitIdle());
117} 119}
118 120
119void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 121void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
120 if (!framebuffer) { 122 if (framebuffers.empty()) {
121 return; 123 return;
122 } 124 }
125
123 SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); 126 SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
127
124 if (!render_window.IsShown()) { 128 if (!render_window.IsShown()) {
125 return; 129 return;
126 } 130 }
127 // Update screen info if the framebuffer size has changed.
128 screen_info.width = framebuffer->width;
129 screen_info.height = framebuffer->height;
130
131 const DAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
132 const bool use_accelerated =
133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
134 RenderScreenshot(*framebuffer, use_accelerated);
135 131
132 RenderScreenshot(framebuffers);
136 Frame* frame = present_manager.GetRenderFrame(); 133 Frame* frame = present_manager.GetRenderFrame();
137 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated); 134 blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers,
135 render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
136 swapchain.GetImageViewFormat());
138 scheduler.Flush(*frame->render_ready); 137 scheduler.Flush(*frame->render_ready);
139 present_manager.Present(frame); 138 present_manager.Present(frame);
140 139
@@ -168,143 +167,37 @@ void RendererVulkan::Report() const {
168 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 167 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
169} 168}
170 169
171void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, 170void Vulkan::RendererVulkan::RenderScreenshot(
172 bool use_accelerated) { 171 std::span<const Tegra::FramebufferConfig> framebuffers) {
173 if (!renderer_settings.screenshot_requested) { 172 if (!renderer_settings.screenshot_requested) {
174 return; 173 return;
175 } 174 }
175
176 constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM};
176 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 177 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
177 vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{
178 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
179 .pNext = nullptr,
180 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
181 .imageType = VK_IMAGE_TYPE_2D,
182 .format = VK_FORMAT_B8G8R8A8_UNORM,
183 .extent =
184 {
185 .width = layout.width,
186 .height = layout.height,
187 .depth = 1,
188 },
189 .mipLevels = 1,
190 .arrayLayers = 1,
191 .samples = VK_SAMPLE_COUNT_1_BIT,
192 .tiling = VK_IMAGE_TILING_OPTIMAL,
193 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
194 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
195 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
196 .queueFamilyIndexCount = 0,
197 .pQueueFamilyIndices = nullptr,
198 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
199 });
200 178
201 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 179 auto frame = [&]() {
202 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 180 Frame f{};
203 .pNext = nullptr, 181 f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height},
204 .flags = 0, 182 ScreenshotFormat);
205 .image = *staging_image, 183 f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat);
206 .viewType = VK_IMAGE_VIEW_TYPE_2D, 184 f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat);
207 .format = VK_FORMAT_B8G8R8A8_UNORM, 185 return f;
208 .components{ 186 }();
209 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
210 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
211 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
212 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
213 },
214 .subresourceRange{
215 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
216 .baseMipLevel = 0,
217 .levelCount = 1,
218 .baseArrayLayer = 0,
219 .layerCount = VK_REMAINING_ARRAY_LAYERS,
220 },
221 });
222 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
223 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
224 blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated);
225 187
226 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); 188 blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1,
227 const VkBufferCreateInfo dst_buffer_info{ 189 VK_FORMAT_B8G8R8A8_UNORM);
228 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 190
229 .pNext = nullptr, 191 const auto dst_buffer = CreateWrappedBuffer(
230 .flags = 0, 192 memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4),
231 .size = buffer_size, 193 MemoryUsage::Download);
232 .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
233 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
234 .queueFamilyIndexCount = 0,
235 .pQueueFamilyIndices = nullptr,
236 };
237 const vk::Buffer dst_buffer =
238 memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download);
239 194
240 scheduler.RequestOutsideRenderPassOperationContext(); 195 scheduler.RequestOutsideRenderPassOperationContext();
241 scheduler.Record([&](vk::CommandBuffer cmdbuf) { 196 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
242 const VkImageMemoryBarrier read_barrier{ 197 DownloadColorImage(cmdbuf, *frame.image, *dst_buffer,
243 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 198 VkExtent3D{layout.width, layout.height, 1});
244 .pNext = nullptr,
245 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
246 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
247 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
248 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
249 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
250 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
251 .image = *staging_image,
252 .subresourceRange{
253 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
254 .baseMipLevel = 0,
255 .levelCount = VK_REMAINING_MIP_LEVELS,
256 .baseArrayLayer = 0,
257 .layerCount = VK_REMAINING_ARRAY_LAYERS,
258 },
259 };
260 const VkImageMemoryBarrier image_write_barrier{
261 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
262 .pNext = nullptr,
263 .srcAccessMask = 0,
264 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
265 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
266 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
267 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
268 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
269 .image = *staging_image,
270 .subresourceRange{
271 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
272 .baseMipLevel = 0,
273 .levelCount = VK_REMAINING_MIP_LEVELS,
274 .baseArrayLayer = 0,
275 .layerCount = VK_REMAINING_ARRAY_LAYERS,
276 },
277 };
278 static constexpr VkMemoryBarrier memory_write_barrier{
279 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
280 .pNext = nullptr,
281 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
282 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
283 };
284 const VkBufferImageCopy copy{
285 .bufferOffset = 0,
286 .bufferRowLength = 0,
287 .bufferImageHeight = 0,
288 .imageSubresource{
289 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
290 .mipLevel = 0,
291 .baseArrayLayer = 0,
292 .layerCount = 1,
293 },
294 .imageOffset{.x = 0, .y = 0, .z = 0},
295 .imageExtent{
296 .width = layout.width,
297 .height = layout.height,
298 .depth = 1,
299 },
300 };
301 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
302 0, read_barrier);
303 cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
304 copy);
305 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
306 0, memory_write_barrier, nullptr, image_write_barrier);
307 }); 199 });
200
308 // Ensure the copy is fully completed before saving the screenshot 201 // Ensure the copy is fully completed before saving the screenshot
309 scheduler.Finish(); 202 scheduler.Finish();
310 203
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 11c52287a..c6d8a0f21 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -46,7 +46,7 @@ public:
46 std::unique_ptr<Core::Frontend::GraphicsContext> context_); 46 std::unique_ptr<Core::Frontend::GraphicsContext> context_);
47 ~RendererVulkan() override; 47 ~RendererVulkan() override;
48 48
49 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 49 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
50 50
51 VideoCore::RasterizerInterface* ReadRasterizer() override { 51 VideoCore::RasterizerInterface* ReadRasterizer() override {
52 return &rasterizer; 52 return &rasterizer;
@@ -59,7 +59,7 @@ public:
59private: 59private:
60 void Report() const; 60 void Report() const;
61 61
62 void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated); 62 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
63 63
64 Core::TelemetrySession& telemetry_session; 64 Core::TelemetrySession& telemetry_session;
65 Tegra::MaxwellDeviceMemoryManager& device_memory; 65 Tegra::MaxwellDeviceMemoryManager& device_memory;
@@ -72,15 +72,14 @@ private:
72 vk::DebugUtilsMessenger debug_messenger; 72 vk::DebugUtilsMessenger debug_messenger;
73 vk::SurfaceKHR surface; 73 vk::SurfaceKHR surface;
74 74
75 ScreenInfo screen_info;
76
77 Device device; 75 Device device;
78 MemoryAllocator memory_allocator; 76 MemoryAllocator memory_allocator;
79 StateTracker state_tracker; 77 StateTracker state_tracker;
80 Scheduler scheduler; 78 Scheduler scheduler;
81 Swapchain swapchain; 79 Swapchain swapchain;
82 PresentManager present_manager; 80 PresentManager present_manager;
83 BlitScreen blit_screen; 81 BlitScreen blit_swapchain;
82 BlitScreen blit_screenshot;
84 RasterizerVulkan rasterizer; 83 RasterizerVulkan rasterizer;
85 std::optional<TurboMode> turbo_mode; 84 std::optional<TurboMode> turbo_mode;
86}; 85};
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 610f27c84..2275fcc46 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -1,522 +1,143 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include "video_core/framebuffer_config.h"
5#include <array> 5#include "video_core/renderer_vulkan/present/filters.h"
6#include <cstring> 6#include "video_core/renderer_vulkan/present/layer.h"
7#include <memory>
8#include <vector>
9
10#include "common/assert.h"
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "common/polyfill_ranges.h"
14#include "common/settings.h"
15#include "core/core.h"
16#include "core/frontend/emu_window.h"
17#include "video_core/gpu.h"
18#include "video_core/host1x/gpu_device_memory_manager.h"
19#include "video_core/host_shaders/fxaa_frag_spv.h"
20#include "video_core/host_shaders/fxaa_vert_spv.h"
21#include "video_core/host_shaders/present_bicubic_frag_spv.h"
22#include "video_core/host_shaders/present_gaussian_frag_spv.h"
23#include "video_core/host_shaders/vulkan_present_frag_spv.h"
24#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
25#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
26#include "video_core/host_shaders/vulkan_present_vert_spv.h"
27#include "video_core/renderer_vulkan/renderer_vulkan.h"
28#include "video_core/renderer_vulkan/vk_blit_screen.h" 7#include "video_core/renderer_vulkan/vk_blit_screen.h"
29#include "video_core/renderer_vulkan/vk_fsr.h" 8#include "video_core/renderer_vulkan/vk_present_manager.h"
30#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
31#include "video_core/renderer_vulkan/vk_shader_util.h"
32#include "video_core/renderer_vulkan/vk_smaa.h"
33#include "video_core/renderer_vulkan/vk_swapchain.h"
34#include "video_core/surface.h"
35#include "video_core/textures/decoders.h"
36#include "video_core/vulkan_common/vulkan_device.h"
37#include "video_core/vulkan_common/vulkan_memory_allocator.h"
38#include "video_core/vulkan_common/vulkan_wrapper.h"
39 10
40namespace Vulkan { 11namespace Vulkan {
41 12
42namespace { 13BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
43 14 MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
44struct ScreenRectVertex { 15 Scheduler& scheduler_)
45 ScreenRectVertex() = default; 16 : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
46 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} 17 present_manager{present_manager_}, scheduler{scheduler_}, image_count{1},
47 18 swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
48 std::array<f32, 2> position;
49 std::array<f32, 2> tex_coord;
50
51 static VkVertexInputBindingDescription GetDescription() {
52 return {
53 .binding = 0,
54 .stride = sizeof(ScreenRectVertex),
55 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
56 };
57 }
58
59 static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
60 return {{
61 {
62 .location = 0,
63 .binding = 0,
64 .format = VK_FORMAT_R32G32_SFLOAT,
65 .offset = offsetof(ScreenRectVertex, position),
66 },
67 {
68 .location = 1,
69 .binding = 0,
70 .format = VK_FORMAT_R32G32_SFLOAT,
71 .offset = offsetof(ScreenRectVertex, tex_coord),
72 },
73 }};
74 }
75};
76 19
77std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { 20BlitScreen::~BlitScreen() = default;
78 // clang-format off
79 return { 2.f / width, 0.f, 0.f, 0.f,
80 0.f, 2.f / height, 0.f, 0.f,
81 0.f, 0.f, 1.f, 0.f,
82 -1.f, -1.f, 0.f, 1.f};
83 // clang-format on
84}
85
86u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
87 using namespace VideoCore::Surface;
88 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
89}
90 21
91std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { 22void BlitScreen::WaitIdle() {
92 return static_cast<std::size_t>(framebuffer.stride) * 23 present_manager.WaitPresent();
93 static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); 24 scheduler.Finish();
25 device.GetLogical().WaitIdle();
94} 26}
95 27
96VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 28void BlitScreen::SetWindowAdaptPass() {
97 switch (framebuffer.pixel_format) { 29 layers.clear();
98 case Service::android::PixelFormat::Rgba8888: 30 scaling_filter = Settings::values.scaling_filter.GetValue();
99 case Service::android::PixelFormat::Rgbx8888: 31
100 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 32 switch (scaling_filter) {
101 case Service::android::PixelFormat::Rgb565: 33 case Settings::ScalingFilter::NearestNeighbor:
102 return VK_FORMAT_R5G6B5_UNORM_PACK16; 34 window_adapt = MakeNearestNeighbor(device, swapchain_view_format);
103 case Service::android::PixelFormat::Bgra8888: 35 break;
104 return VK_FORMAT_B8G8R8A8_UNORM; 36 case Settings::ScalingFilter::Bicubic:
37 window_adapt = MakeBicubic(device, swapchain_view_format);
38 break;
39 case Settings::ScalingFilter::Gaussian:
40 window_adapt = MakeGaussian(device, swapchain_view_format);
41 break;
42 case Settings::ScalingFilter::ScaleForce:
43 window_adapt = MakeScaleForce(device, swapchain_view_format);
44 break;
45 case Settings::ScalingFilter::Fsr:
46 case Settings::ScalingFilter::Bilinear:
105 default: 47 default:
106 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 48 window_adapt = MakeBilinear(device, swapchain_view_format);
107 static_cast<u32>(framebuffer.pixel_format)); 49 break;
108 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
109 } 50 }
110} 51}
111 52
112} // Anonymous namespace 53void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
113 54 std::span<const Tegra::FramebufferConfig> framebuffers,
114struct BlitScreen::BufferData { 55 const Layout::FramebufferLayout& layout,
115 struct { 56 size_t current_swapchain_image_count,
116 std::array<f32, 4 * 4> modelview_matrix; 57 VkFormat current_swapchain_view_format) {
117 } uniform; 58 bool resource_update_required = false;
118 59 bool presentation_recreate_required = false;
119 std::array<ScreenRectVertex, 4> vertices;
120
121 // Unaligned image data goes here
122};
123
124BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_,
125 Core::Frontend::EmuWindow& render_window_, const Device& device_,
126 MemoryAllocator& memory_allocator_, Swapchain& swapchain_,
127 PresentManager& present_manager_, Scheduler& scheduler_,
128 const ScreenInfo& screen_info_)
129 : device_memory{device_memory_}, render_window{render_window_}, device{device_},
130 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
131 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
132 resource_ticks.resize(image_count);
133 swapchain_view_format = swapchain.GetImageViewFormat();
134
135 CreateStaticResources();
136 CreateDynamicResources();
137}
138
139BlitScreen::~BlitScreen() = default;
140
141static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer,
142 const ScreenInfo& screen_info) {
143 f32 left, top, right, bottom;
144
145 if (!framebuffer.crop_rect.IsEmpty()) {
146 // If crop rectangle is not empty, apply properties from rectangle.
147 left = static_cast<f32>(framebuffer.crop_rect.left);
148 top = static_cast<f32>(framebuffer.crop_rect.top);
149 right = static_cast<f32>(framebuffer.crop_rect.right);
150 bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
151 } else {
152 // Otherwise, fall back to framebuffer dimensions.
153 left = 0;
154 top = 0;
155 right = static_cast<f32>(framebuffer.width);
156 bottom = static_cast<f32>(framebuffer.height);
157 }
158
159 // Apply transformation flags.
160 auto framebuffer_transform_flags = framebuffer.transform_flags;
161 60
162 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { 61 // Recreate dynamic resources if the adapting filter changed
163 // Switch left and right. 62 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) {
164 std::swap(left, right); 63 resource_update_required = true;
165 }
166 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
167 // Switch top and bottom.
168 std::swap(top, bottom);
169 } 64 }
170 65
171 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; 66 // Recreate dynamic resources if the image count changed
172 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; 67 const size_t old_swapchain_image_count =
173 if (True(framebuffer_transform_flags)) { 68 std::exchange(image_count, current_swapchain_image_count);
174 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", 69 if (old_swapchain_image_count != current_swapchain_image_count) {
175 static_cast<u32>(framebuffer_transform_flags)); 70 resource_update_required = true;
176 } 71 }
177 72
178 // Get the screen properties. 73 // Recreate the presentation frame if the format or dimensions of the window changed
179 const f32 screen_width = static_cast<f32>(screen_info.width); 74 const VkFormat old_swapchain_view_format =
180 const f32 screen_height = static_cast<f32>(screen_info.height); 75 std::exchange(swapchain_view_format, current_swapchain_view_format);
181 76 if (old_swapchain_view_format != current_swapchain_view_format ||
182 // Normalize coordinate space. 77 layout.width != frame->width || layout.height != frame->height) {
183 left /= screen_width; 78 resource_update_required = true;
184 top /= screen_height; 79 presentation_recreate_required = true;
185 right /= screen_width;
186 bottom /= screen_height;
187
188 return Common::Rectangle<f32>(left, top, right, bottom);
189}
190
191void BlitScreen::Recreate() {
192 present_manager.WaitPresent();
193 scheduler.Finish();
194 device.GetLogical().WaitIdle();
195 CreateDynamicResources();
196}
197
198void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
199 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout,
200 VkExtent2D render_area, bool use_accelerated) {
201 RefreshResources(framebuffer);
202
203 // Finish any pending renderpass
204 scheduler.RequestOutsideRenderPassOperationContext();
205
206 scheduler.Wait(resource_ticks[image_index]);
207 resource_ticks[image_index] = scheduler.CurrentTick();
208
209 VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index];
210 VkImageView source_image_view =
211 use_accelerated ? screen_info.image_view : *raw_image_views[image_index];
212
213 BufferData data;
214 SetUniformData(data, layout);
215 SetVertexData(data, framebuffer, layout);
216
217 const std::span<u8> mapped_span = buffer.Mapped();
218 std::memcpy(mapped_span.data(), &data, sizeof(data));
219
220 if (!use_accelerated) {
221 const u64 image_offset = GetRawImageOffset(framebuffer);
222
223 const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
224 const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr);
225
226 // TODO(Rodrigo): Read this from HLE
227 constexpr u32 block_height_log2 = 4;
228 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
229 const u64 linear_size{GetSizeInBytes(framebuffer)};
230 const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
231 framebuffer.stride, framebuffer.height,
232 1, block_height_log2, 0)};
233 Tegra::Texture::UnswizzleTexture(
234 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
235 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
236
237 const VkBufferImageCopy copy{
238 .bufferOffset = image_offset,
239 .bufferRowLength = 0,
240 .bufferImageHeight = 0,
241 .imageSubresource =
242 {
243 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
244 .mipLevel = 0,
245 .baseArrayLayer = 0,
246 .layerCount = 1,
247 },
248 .imageOffset = {.x = 0, .y = 0, .z = 0},
249 .imageExtent =
250 {
251 .width = framebuffer.width,
252 .height = framebuffer.height,
253 .depth = 1,
254 },
255 };
256 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
257 const VkImage image = *raw_images[index];
258 const VkImageMemoryBarrier base_barrier{
259 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
260 .pNext = nullptr,
261 .srcAccessMask = 0,
262 .dstAccessMask = 0,
263 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
264 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
265 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
266 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
267 .image = image,
268 .subresourceRange{
269 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
270 .baseMipLevel = 0,
271 .levelCount = 1,
272 .baseArrayLayer = 0,
273 .layerCount = 1,
274 },
275 };
276 VkImageMemoryBarrier read_barrier = base_barrier;
277 read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
278 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
279 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
280
281 VkImageMemoryBarrier write_barrier = base_barrier;
282 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
283 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
284
285 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
286 read_barrier);
287 cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy);
288 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
289 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
290 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
291 0, write_barrier);
292 });
293 } 80 }
294 81
295 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 82 // If we have a pending resource update, perform it
296 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { 83 if (resource_update_required) {
297 UpdateAADescriptorSet(source_image_view, false); 84 // Wait for idle to ensure no resources are in use
298 const u32 up_scale = Settings::values.resolution_info.up_scale; 85 WaitIdle();
299 const u32 down_shift = Settings::values.resolution_info.down_shift;
300 VkExtent2D size{
301 .width = (up_scale * framebuffer.width) >> down_shift,
302 .height = (up_scale * framebuffer.height) >> down_shift,
303 };
304 scheduler.Record([this, index = image_index, size,
305 anti_alias_pass](vk::CommandBuffer cmdbuf) {
306 const VkImageMemoryBarrier base_barrier{
307 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
308 .pNext = nullptr,
309 .srcAccessMask = 0,
310 .dstAccessMask = 0,
311 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
312 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
313 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
314 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
315 .image = {},
316 .subresourceRange =
317 {
318 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
319 .baseMipLevel = 0,
320 .levelCount = 1,
321 .baseArrayLayer = 0,
322 .layerCount = 1,
323 },
324 };
325
326 {
327 VkImageMemoryBarrier fsr_write_barrier = base_barrier;
328 fsr_write_barrier.image = *aa_image;
329 fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
330 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
331 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, fsr_write_barrier);
332 }
333 86
334 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 87 // Update window adapt pass
335 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 88 SetWindowAdaptPass();
336 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
337 const VkClearValue clear_color{
338 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
339 };
340 const VkRenderPassBeginInfo renderpass_bi{
341 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
342 .pNext = nullptr,
343 .renderPass = *aa_renderpass,
344 .framebuffer = *aa_framebuffer,
345 .renderArea =
346 {
347 .offset = {0, 0},
348 .extent = size,
349 },
350 .clearValueCount = 1,
351 .pClearValues = &clear_color,
352 };
353 const VkViewport viewport{
354 .x = 0.0f,
355 .y = 0.0f,
356 .width = static_cast<float>(size.width),
357 .height = static_cast<float>(size.height),
358 .minDepth = 0.0f,
359 .maxDepth = 1.0f,
360 };
361 const VkRect2D scissor{
362 .offset = {0, 0},
363 .extent = size,
364 };
365 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
366 switch (anti_alias_pass) {
367 case Settings::AntiAliasing::Fxaa:
368 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline);
369 break;
370 default:
371 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline);
372 break;
373 }
374 cmdbuf.SetViewport(0, viewport);
375 cmdbuf.SetScissor(0, scissor);
376 89
377 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 90 // Update frame format if needed
378 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, 91 if (presentation_recreate_required) {
379 aa_descriptor_sets[index], {}); 92 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
380 cmdbuf.Draw(4, 1, 0, 0); 93 window_adapt->GetRenderPass());
381 cmdbuf.EndRenderPass();
382
383 {
384 VkImageMemoryBarrier blit_read_barrier = base_barrier;
385 blit_read_barrier.image = *aa_image;
386 blit_read_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
387 blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
388
389 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
390 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, blit_read_barrier);
391 }
392 });
393 source_image_view = *aa_image_view;
394 }
395 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) {
396 if (!smaa) {
397 const u32 up_scale = Settings::values.resolution_info.up_scale;
398 const u32 down_shift = Settings::values.resolution_info.down_shift;
399 const VkExtent2D smaa_size{
400 .width = (up_scale * framebuffer.width) >> down_shift,
401 .height = (up_scale * framebuffer.height) >> down_shift,
402 };
403 CreateSMAA(smaa_size);
404 } 94 }
405 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
406 } 95 }
407 if (fsr) {
408 const auto crop_rect = NormalizeCrop(framebuffer, screen_info);
409 const VkExtent2D fsr_input_size{
410 .width = Settings::values.resolution_info.ScaleUp(screen_info.width),
411 .height = Settings::values.resolution_info.ScaleUp(screen_info.height),
412 };
413 VkImageView fsr_image_view =
414 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
415 UpdateDescriptorSet(fsr_image_view, true);
416 } else {
417 const bool is_nn =
418 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
419 UpdateDescriptorSet(source_image_view, is_nn);
420 }
421
422 scheduler.Record([this, host_framebuffer, index = image_index,
423 size = render_area](vk::CommandBuffer cmdbuf) {
424 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
425 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
426 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
427 const VkClearValue clear_color{
428 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
429 };
430 const VkRenderPassBeginInfo renderpass_bi{
431 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
432 .pNext = nullptr,
433 .renderPass = *renderpass,
434 .framebuffer = host_framebuffer,
435 .renderArea =
436 {
437 .offset = {0, 0},
438 .extent = size,
439 },
440 .clearValueCount = 1,
441 .pClearValues = &clear_color,
442 };
443 const VkViewport viewport{
444 .x = 0.0f,
445 .y = 0.0f,
446 .width = static_cast<float>(size.width),
447 .height = static_cast<float>(size.height),
448 .minDepth = 0.0f,
449 .maxDepth = 1.0f,
450 };
451 const VkRect2D scissor{
452 .offset = {0, 0},
453 .extent = size,
454 };
455 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
456 auto graphics_pipeline = [this]() {
457 switch (Settings::values.scaling_filter.GetValue()) {
458 case Settings::ScalingFilter::NearestNeighbor:
459 case Settings::ScalingFilter::Bilinear:
460 return *bilinear_pipeline;
461 case Settings::ScalingFilter::Bicubic:
462 return *bicubic_pipeline;
463 case Settings::ScalingFilter::Gaussian:
464 return *gaussian_pipeline;
465 case Settings::ScalingFilter::ScaleForce:
466 return *scaleforce_pipeline;
467 default:
468 return *bilinear_pipeline;
469 }
470 }();
471 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
472 cmdbuf.SetViewport(0, viewport);
473 cmdbuf.SetScissor(0, scissor);
474 96
475 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 97 // Add additional layers if needed
476 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 98 const VkExtent2D window_size{
477 descriptor_sets[index], {}); 99 .width = layout.screen.GetWidth(),
478 cmdbuf.Draw(4, 1, 0, 0); 100 .height = layout.screen.GetHeight(),
479 cmdbuf.EndRenderPass(); 101 };
480 });
481}
482 102
483void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, 103 while (layers.size() < framebuffers.size()) {
484 bool use_accelerated) { 104 layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
485 // Recreate dynamic resources if the the image count or input format changed 105 window_size, window_adapt->GetDescriptorSetLayout());
486 const VkFormat current_framebuffer_format =
487 std::exchange(framebuffer_view_format, GetFormat(framebuffer));
488 if (const std::size_t swapchain_images = swapchain.GetImageCount();
489 swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) {
490 image_count = swapchain_images;
491 Recreate();
492 } 106 }
493 107
494 // Recreate the presentation frame if the dimensions of the window changed 108 // Perform the draw
495 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 109 window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame);
496 if (layout.width != frame->width || layout.height != frame->height) {
497 Recreate();
498 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
499 *renderpass);
500 }
501 110
502 const VkExtent2D render_area{frame->width, frame->height}; 111 // Advance to next image
503 Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated);
504 if (++image_index >= image_count) { 112 if (++image_index >= image_count) {
505 image_index = 0; 113 image_index = 0;
506 } 114 }
507} 115}
508 116
509vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { 117vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout,
510 return CreateFramebuffer(image_view, extent, renderpass); 118 VkImageView image_view,
119 VkFormat current_view_format) {
120 const bool format_updated =
121 std::exchange(swapchain_view_format, current_view_format) != current_view_format;
122 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() ||
123 format_updated) {
124 WaitIdle();
125 SetWindowAdaptPass();
126 }
127 const VkExtent2D extent{
128 .width = layout.width,
129 .height = layout.height,
130 };
131 return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass());
511} 132}
512 133
513vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, 134vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
514 vk::RenderPass& rd) { 135 VkRenderPass render_pass) {
515 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ 136 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
516 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 137 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
517 .pNext = nullptr, 138 .pNext = nullptr,
518 .flags = 0, 139 .flags = 0,
519 .renderPass = *rd, 140 .renderPass = render_pass,
520 .attachmentCount = 1, 141 .attachmentCount = 1,
521 .pAttachments = &image_view, 142 .pAttachments = &image_view,
522 .width = extent.width, 143 .width = extent.width,
@@ -525,969 +146,4 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE
525 }); 146 });
526} 147}
527 148
528void BlitScreen::CreateStaticResources() {
529 CreateShaders();
530 CreateSampler();
531}
532
533void BlitScreen::CreateDynamicResources() {
534 CreateDescriptorPool();
535 CreateDescriptorSetLayout();
536 CreateDescriptorSets();
537 CreatePipelineLayout();
538 CreateRenderPass();
539 CreateGraphicsPipeline();
540 fsr.reset();
541 smaa.reset();
542 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
543 CreateFSR();
544 }
545}
546
547void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
548 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
549 if (!fsr) {
550 CreateFSR();
551 }
552 } else {
553 fsr.reset();
554 }
555
556 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
557 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
558 return;
559 }
560
561 raw_width = framebuffer.width;
562 raw_height = framebuffer.height;
563 pixel_format = framebuffer.pixel_format;
564
565 smaa.reset();
566 ReleaseRawImages();
567
568 CreateStagingBuffer(framebuffer);
569 CreateRawImages(framebuffer);
570}
571
572void BlitScreen::CreateShaders() {
573 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
574 fxaa_vertex_shader = BuildShader(device, FXAA_VERT_SPV);
575 fxaa_fragment_shader = BuildShader(device, FXAA_FRAG_SPV);
576 bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV);
577 bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV);
578 gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV);
579 if (device.IsFloat16Supported()) {
580 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV);
581 } else {
582 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV);
583 }
584}
585
586void BlitScreen::CreateDescriptorPool() {
587 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
588 {
589 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
590 .descriptorCount = static_cast<u32>(image_count),
591 },
592 {
593 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
594 .descriptorCount = static_cast<u32>(image_count),
595 },
596 }};
597
598 const std::array<VkDescriptorPoolSize, 1> pool_sizes_aa{{
599 {
600 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
601 .descriptorCount = static_cast<u32>(image_count * 2),
602 },
603 }};
604
605 const VkDescriptorPoolCreateInfo ci{
606 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
607 .pNext = nullptr,
608 .flags = 0,
609 .maxSets = static_cast<u32>(image_count),
610 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
611 .pPoolSizes = pool_sizes.data(),
612 };
613 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
614
615 const VkDescriptorPoolCreateInfo ci_aa{
616 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
617 .pNext = nullptr,
618 .flags = 0,
619 .maxSets = static_cast<u32>(image_count),
620 .poolSizeCount = static_cast<u32>(pool_sizes_aa.size()),
621 .pPoolSizes = pool_sizes_aa.data(),
622 };
623 aa_descriptor_pool = device.GetLogical().CreateDescriptorPool(ci_aa);
624}
625
626void BlitScreen::CreateRenderPass() {
627 renderpass = CreateRenderPassImpl(swapchain_view_format);
628}
629
630vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
631 const VkAttachmentDescription color_attachment{
632 .flags = 0,
633 .format = format,
634 .samples = VK_SAMPLE_COUNT_1_BIT,
635 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
636 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
637 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
638 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
639 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
640 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
641 };
642
643 const VkAttachmentReference color_attachment_ref{
644 .attachment = 0,
645 .layout = VK_IMAGE_LAYOUT_GENERAL,
646 };
647
648 const VkSubpassDescription subpass_description{
649 .flags = 0,
650 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
651 .inputAttachmentCount = 0,
652 .pInputAttachments = nullptr,
653 .colorAttachmentCount = 1,
654 .pColorAttachments = &color_attachment_ref,
655 .pResolveAttachments = nullptr,
656 .pDepthStencilAttachment = nullptr,
657 .preserveAttachmentCount = 0,
658 .pPreserveAttachments = nullptr,
659 };
660
661 const VkSubpassDependency dependency{
662 .srcSubpass = VK_SUBPASS_EXTERNAL,
663 .dstSubpass = 0,
664 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
665 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
666 .srcAccessMask = 0,
667 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
668 .dependencyFlags = 0,
669 };
670
671 const VkRenderPassCreateInfo renderpass_ci{
672 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
673 .pNext = nullptr,
674 .flags = 0,
675 .attachmentCount = 1,
676 .pAttachments = &color_attachment,
677 .subpassCount = 1,
678 .pSubpasses = &subpass_description,
679 .dependencyCount = 1,
680 .pDependencies = &dependency,
681 };
682
683 return device.GetLogical().CreateRenderPass(renderpass_ci);
684}
685
686void BlitScreen::CreateDescriptorSetLayout() {
687 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
688 {
689 .binding = 0,
690 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
691 .descriptorCount = 1,
692 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
693 .pImmutableSamplers = nullptr,
694 },
695 {
696 .binding = 1,
697 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
698 .descriptorCount = 1,
699 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
700 .pImmutableSamplers = nullptr,
701 },
702 }};
703
704 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings_aa{{
705 {
706 .binding = 0,
707 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
708 .descriptorCount = 1,
709 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
710 .pImmutableSamplers = nullptr,
711 },
712 {
713 .binding = 1,
714 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
715 .descriptorCount = 1,
716 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
717 .pImmutableSamplers = nullptr,
718 },
719 }};
720
721 const VkDescriptorSetLayoutCreateInfo ci{
722 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
723 .pNext = nullptr,
724 .flags = 0,
725 .bindingCount = static_cast<u32>(layout_bindings.size()),
726 .pBindings = layout_bindings.data(),
727 };
728
729 const VkDescriptorSetLayoutCreateInfo ci_aa{
730 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
731 .pNext = nullptr,
732 .flags = 0,
733 .bindingCount = static_cast<u32>(layout_bindings_aa.size()),
734 .pBindings = layout_bindings_aa.data(),
735 };
736
737 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
738 aa_descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci_aa);
739}
740
741void BlitScreen::CreateDescriptorSets() {
742 const std::vector layouts(image_count, *descriptor_set_layout);
743 const std::vector layouts_aa(image_count, *aa_descriptor_set_layout);
744
745 const VkDescriptorSetAllocateInfo ai{
746 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
747 .pNext = nullptr,
748 .descriptorPool = *descriptor_pool,
749 .descriptorSetCount = static_cast<u32>(image_count),
750 .pSetLayouts = layouts.data(),
751 };
752
753 const VkDescriptorSetAllocateInfo ai_aa{
754 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
755 .pNext = nullptr,
756 .descriptorPool = *aa_descriptor_pool,
757 .descriptorSetCount = static_cast<u32>(image_count),
758 .pSetLayouts = layouts_aa.data(),
759 };
760
761 descriptor_sets = descriptor_pool.Allocate(ai);
762 aa_descriptor_sets = aa_descriptor_pool.Allocate(ai_aa);
763}
764
765void BlitScreen::CreatePipelineLayout() {
766 const VkPipelineLayoutCreateInfo ci{
767 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
768 .pNext = nullptr,
769 .flags = 0,
770 .setLayoutCount = 1,
771 .pSetLayouts = descriptor_set_layout.address(),
772 .pushConstantRangeCount = 0,
773 .pPushConstantRanges = nullptr,
774 };
775 const VkPipelineLayoutCreateInfo ci_aa{
776 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
777 .pNext = nullptr,
778 .flags = 0,
779 .setLayoutCount = 1,
780 .pSetLayouts = aa_descriptor_set_layout.address(),
781 .pushConstantRangeCount = 0,
782 .pPushConstantRanges = nullptr,
783 };
784 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
785 aa_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci_aa);
786}
787
788void BlitScreen::CreateGraphicsPipeline() {
789 const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{
790 {
791 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
792 .pNext = nullptr,
793 .flags = 0,
794 .stage = VK_SHADER_STAGE_VERTEX_BIT,
795 .module = *vertex_shader,
796 .pName = "main",
797 .pSpecializationInfo = nullptr,
798 },
799 {
800 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
801 .pNext = nullptr,
802 .flags = 0,
803 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
804 .module = *bilinear_fragment_shader,
805 .pName = "main",
806 .pSpecializationInfo = nullptr,
807 },
808 }};
809
810 const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{
811 {
812 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
813 .pNext = nullptr,
814 .flags = 0,
815 .stage = VK_SHADER_STAGE_VERTEX_BIT,
816 .module = *vertex_shader,
817 .pName = "main",
818 .pSpecializationInfo = nullptr,
819 },
820 {
821 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
822 .pNext = nullptr,
823 .flags = 0,
824 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
825 .module = *bicubic_fragment_shader,
826 .pName = "main",
827 .pSpecializationInfo = nullptr,
828 },
829 }};
830
831 const std::array<VkPipelineShaderStageCreateInfo, 2> gaussian_shader_stages{{
832 {
833 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
834 .pNext = nullptr,
835 .flags = 0,
836 .stage = VK_SHADER_STAGE_VERTEX_BIT,
837 .module = *vertex_shader,
838 .pName = "main",
839 .pSpecializationInfo = nullptr,
840 },
841 {
842 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
843 .pNext = nullptr,
844 .flags = 0,
845 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
846 .module = *gaussian_fragment_shader,
847 .pName = "main",
848 .pSpecializationInfo = nullptr,
849 },
850 }};
851
852 const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{
853 {
854 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
855 .pNext = nullptr,
856 .flags = 0,
857 .stage = VK_SHADER_STAGE_VERTEX_BIT,
858 .module = *vertex_shader,
859 .pName = "main",
860 .pSpecializationInfo = nullptr,
861 },
862 {
863 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
864 .pNext = nullptr,
865 .flags = 0,
866 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
867 .module = *scaleforce_fragment_shader,
868 .pName = "main",
869 .pSpecializationInfo = nullptr,
870 },
871 }};
872
873 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
874 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
875
876 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
877 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
878 .pNext = nullptr,
879 .flags = 0,
880 .vertexBindingDescriptionCount = 1,
881 .pVertexBindingDescriptions = &vertex_binding_description,
882 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
883 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
884 };
885
886 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
887 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
888 .pNext = nullptr,
889 .flags = 0,
890 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
891 .primitiveRestartEnable = VK_FALSE,
892 };
893
894 const VkPipelineViewportStateCreateInfo viewport_state_ci{
895 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
896 .pNext = nullptr,
897 .flags = 0,
898 .viewportCount = 1,
899 .pViewports = nullptr,
900 .scissorCount = 1,
901 .pScissors = nullptr,
902 };
903
904 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
905 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
906 .pNext = nullptr,
907 .flags = 0,
908 .depthClampEnable = VK_FALSE,
909 .rasterizerDiscardEnable = VK_FALSE,
910 .polygonMode = VK_POLYGON_MODE_FILL,
911 .cullMode = VK_CULL_MODE_NONE,
912 .frontFace = VK_FRONT_FACE_CLOCKWISE,
913 .depthBiasEnable = VK_FALSE,
914 .depthBiasConstantFactor = 0.0f,
915 .depthBiasClamp = 0.0f,
916 .depthBiasSlopeFactor = 0.0f,
917 .lineWidth = 1.0f,
918 };
919
920 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
921 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
922 .pNext = nullptr,
923 .flags = 0,
924 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
925 .sampleShadingEnable = VK_FALSE,
926 .minSampleShading = 0.0f,
927 .pSampleMask = nullptr,
928 .alphaToCoverageEnable = VK_FALSE,
929 .alphaToOneEnable = VK_FALSE,
930 };
931
932 const VkPipelineColorBlendAttachmentState color_blend_attachment{
933 .blendEnable = VK_FALSE,
934 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
935 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
936 .colorBlendOp = VK_BLEND_OP_ADD,
937 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
938 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
939 .alphaBlendOp = VK_BLEND_OP_ADD,
940 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
941 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
942 };
943
944 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
945 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
946 .pNext = nullptr,
947 .flags = 0,
948 .logicOpEnable = VK_FALSE,
949 .logicOp = VK_LOGIC_OP_COPY,
950 .attachmentCount = 1,
951 .pAttachments = &color_blend_attachment,
952 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
953 };
954
955 static constexpr std::array dynamic_states{
956 VK_DYNAMIC_STATE_VIEWPORT,
957 VK_DYNAMIC_STATE_SCISSOR,
958 };
959 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
960 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
961 .pNext = nullptr,
962 .flags = 0,
963 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
964 .pDynamicStates = dynamic_states.data(),
965 };
966
967 const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{
968 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
969 .pNext = nullptr,
970 .flags = 0,
971 .stageCount = static_cast<u32>(bilinear_shader_stages.size()),
972 .pStages = bilinear_shader_stages.data(),
973 .pVertexInputState = &vertex_input_ci,
974 .pInputAssemblyState = &input_assembly_ci,
975 .pTessellationState = nullptr,
976 .pViewportState = &viewport_state_ci,
977 .pRasterizationState = &rasterization_ci,
978 .pMultisampleState = &multisampling_ci,
979 .pDepthStencilState = nullptr,
980 .pColorBlendState = &color_blend_ci,
981 .pDynamicState = &dynamic_state_ci,
982 .layout = *pipeline_layout,
983 .renderPass = *renderpass,
984 .subpass = 0,
985 .basePipelineHandle = 0,
986 .basePipelineIndex = 0,
987 };
988
989 const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{
990 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
991 .pNext = nullptr,
992 .flags = 0,
993 .stageCount = static_cast<u32>(bicubic_shader_stages.size()),
994 .pStages = bicubic_shader_stages.data(),
995 .pVertexInputState = &vertex_input_ci,
996 .pInputAssemblyState = &input_assembly_ci,
997 .pTessellationState = nullptr,
998 .pViewportState = &viewport_state_ci,
999 .pRasterizationState = &rasterization_ci,
1000 .pMultisampleState = &multisampling_ci,
1001 .pDepthStencilState = nullptr,
1002 .pColorBlendState = &color_blend_ci,
1003 .pDynamicState = &dynamic_state_ci,
1004 .layout = *pipeline_layout,
1005 .renderPass = *renderpass,
1006 .subpass = 0,
1007 .basePipelineHandle = 0,
1008 .basePipelineIndex = 0,
1009 };
1010
1011 const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{
1012 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1013 .pNext = nullptr,
1014 .flags = 0,
1015 .stageCount = static_cast<u32>(gaussian_shader_stages.size()),
1016 .pStages = gaussian_shader_stages.data(),
1017 .pVertexInputState = &vertex_input_ci,
1018 .pInputAssemblyState = &input_assembly_ci,
1019 .pTessellationState = nullptr,
1020 .pViewportState = &viewport_state_ci,
1021 .pRasterizationState = &rasterization_ci,
1022 .pMultisampleState = &multisampling_ci,
1023 .pDepthStencilState = nullptr,
1024 .pColorBlendState = &color_blend_ci,
1025 .pDynamicState = &dynamic_state_ci,
1026 .layout = *pipeline_layout,
1027 .renderPass = *renderpass,
1028 .subpass = 0,
1029 .basePipelineHandle = 0,
1030 .basePipelineIndex = 0,
1031 };
1032
1033 const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{
1034 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1035 .pNext = nullptr,
1036 .flags = 0,
1037 .stageCount = static_cast<u32>(scaleforce_shader_stages.size()),
1038 .pStages = scaleforce_shader_stages.data(),
1039 .pVertexInputState = &vertex_input_ci,
1040 .pInputAssemblyState = &input_assembly_ci,
1041 .pTessellationState = nullptr,
1042 .pViewportState = &viewport_state_ci,
1043 .pRasterizationState = &rasterization_ci,
1044 .pMultisampleState = &multisampling_ci,
1045 .pDepthStencilState = nullptr,
1046 .pColorBlendState = &color_blend_ci,
1047 .pDynamicState = &dynamic_state_ci,
1048 .layout = *pipeline_layout,
1049 .renderPass = *renderpass,
1050 .subpass = 0,
1051 .basePipelineHandle = 0,
1052 .basePipelineIndex = 0,
1053 };
1054
1055 bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci);
1056 bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci);
1057 gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci);
1058 scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci);
1059}
1060
1061void BlitScreen::CreateSampler() {
1062 const VkSamplerCreateInfo ci{
1063 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1064 .pNext = nullptr,
1065 .flags = 0,
1066 .magFilter = VK_FILTER_LINEAR,
1067 .minFilter = VK_FILTER_LINEAR,
1068 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1069 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1070 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1071 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1072 .mipLodBias = 0.0f,
1073 .anisotropyEnable = VK_FALSE,
1074 .maxAnisotropy = 0.0f,
1075 .compareEnable = VK_FALSE,
1076 .compareOp = VK_COMPARE_OP_NEVER,
1077 .minLod = 0.0f,
1078 .maxLod = 0.0f,
1079 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
1080 .unnormalizedCoordinates = VK_FALSE,
1081 };
1082
1083 const VkSamplerCreateInfo ci_nn{
1084 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1085 .pNext = nullptr,
1086 .flags = 0,
1087 .magFilter = VK_FILTER_NEAREST,
1088 .minFilter = VK_FILTER_NEAREST,
1089 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1090 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1091 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1092 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1093 .mipLodBias = 0.0f,
1094 .anisotropyEnable = VK_FALSE,
1095 .maxAnisotropy = 0.0f,
1096 .compareEnable = VK_FALSE,
1097 .compareOp = VK_COMPARE_OP_NEVER,
1098 .minLod = 0.0f,
1099 .maxLod = 0.0f,
1100 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
1101 .unnormalizedCoordinates = VK_FALSE,
1102 };
1103
1104 sampler = device.GetLogical().CreateSampler(ci);
1105 nn_sampler = device.GetLogical().CreateSampler(ci_nn);
1106}
1107
1108void BlitScreen::ReleaseRawImages() {
1109 for (const u64 tick : resource_ticks) {
1110 scheduler.Wait(tick);
1111 }
1112 raw_images.clear();
1113 aa_image_view.reset();
1114 aa_image.reset();
1115 buffer.reset();
1116}
1117
1118void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
1119 const VkBufferCreateInfo ci{
1120 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1121 .pNext = nullptr,
1122 .flags = 0,
1123 .size = CalculateBufferSize(framebuffer),
1124 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1125 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1126 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1127 .queueFamilyIndexCount = 0,
1128 .pQueueFamilyIndices = nullptr,
1129 };
1130
1131 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
1132}
1133
1134void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1135 raw_images.resize(image_count);
1136 raw_image_views.resize(image_count);
1137
1138 const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1,
1139 u32 down_shift = 0) {
1140 u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
1141 : VK_IMAGE_USAGE_TRANSFER_DST_BIT;
1142 return memory_allocator.CreateImage(VkImageCreateInfo{
1143 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1144 .pNext = nullptr,
1145 .flags = 0,
1146 .imageType = VK_IMAGE_TYPE_2D,
1147 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1148 .extent =
1149 {
1150 .width = (up_scale * framebuffer.width) >> down_shift,
1151 .height = (up_scale * framebuffer.height) >> down_shift,
1152 .depth = 1,
1153 },
1154 .mipLevels = 1,
1155 .arrayLayers = 1,
1156 .samples = VK_SAMPLE_COUNT_1_BIT,
1157 .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR,
1158 .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages,
1159 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1160 .queueFamilyIndexCount = 0,
1161 .pQueueFamilyIndices = nullptr,
1162 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1163 });
1164 };
1165 const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
1166 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
1167 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1168 .pNext = nullptr,
1169 .flags = 0,
1170 .image = *image,
1171 .viewType = VK_IMAGE_VIEW_TYPE_2D,
1172 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1173 .components =
1174 {
1175 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
1176 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
1177 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
1178 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
1179 },
1180 .subresourceRange =
1181 {
1182 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1183 .baseMipLevel = 0,
1184 .levelCount = 1,
1185 .baseArrayLayer = 0,
1186 .layerCount = 1,
1187 },
1188 });
1189 };
1190
1191 for (size_t i = 0; i < image_count; ++i) {
1192 raw_images[i] = create_image();
1193 raw_image_views[i] = create_image_view(raw_images[i]);
1194 }
1195
1196 // AA Resources
1197 const u32 up_scale = Settings::values.resolution_info.up_scale;
1198 const u32 down_shift = Settings::values.resolution_info.down_shift;
1199 aa_image = create_image(true, up_scale, down_shift);
1200 aa_image_view = create_image_view(aa_image, true);
1201 VkExtent2D size{
1202 .width = (up_scale * framebuffer.width) >> down_shift,
1203 .height = (up_scale * framebuffer.height) >> down_shift,
1204 };
1205 if (aa_renderpass) {
1206 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1207 return;
1208 }
1209 aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT);
1210 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1211
1212 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
1213 {
1214 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1215 .pNext = nullptr,
1216 .flags = 0,
1217 .stage = VK_SHADER_STAGE_VERTEX_BIT,
1218 .module = *fxaa_vertex_shader,
1219 .pName = "main",
1220 .pSpecializationInfo = nullptr,
1221 },
1222 {
1223 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1224 .pNext = nullptr,
1225 .flags = 0,
1226 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
1227 .module = *fxaa_fragment_shader,
1228 .pName = "main",
1229 .pSpecializationInfo = nullptr,
1230 },
1231 }};
1232
1233 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
1234 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
1235
1236 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
1237 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
1238 .pNext = nullptr,
1239 .flags = 0,
1240 .vertexBindingDescriptionCount = 1,
1241 .pVertexBindingDescriptions = &vertex_binding_description,
1242 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
1243 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
1244 };
1245
1246 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
1247 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
1248 .pNext = nullptr,
1249 .flags = 0,
1250 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1251 .primitiveRestartEnable = VK_FALSE,
1252 };
1253
1254 const VkPipelineViewportStateCreateInfo viewport_state_ci{
1255 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
1256 .pNext = nullptr,
1257 .flags = 0,
1258 .viewportCount = 1,
1259 .pViewports = nullptr,
1260 .scissorCount = 1,
1261 .pScissors = nullptr,
1262 };
1263
1264 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
1265 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
1266 .pNext = nullptr,
1267 .flags = 0,
1268 .depthClampEnable = VK_FALSE,
1269 .rasterizerDiscardEnable = VK_FALSE,
1270 .polygonMode = VK_POLYGON_MODE_FILL,
1271 .cullMode = VK_CULL_MODE_NONE,
1272 .frontFace = VK_FRONT_FACE_CLOCKWISE,
1273 .depthBiasEnable = VK_FALSE,
1274 .depthBiasConstantFactor = 0.0f,
1275 .depthBiasClamp = 0.0f,
1276 .depthBiasSlopeFactor = 0.0f,
1277 .lineWidth = 1.0f,
1278 };
1279
1280 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
1281 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
1282 .pNext = nullptr,
1283 .flags = 0,
1284 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
1285 .sampleShadingEnable = VK_FALSE,
1286 .minSampleShading = 0.0f,
1287 .pSampleMask = nullptr,
1288 .alphaToCoverageEnable = VK_FALSE,
1289 .alphaToOneEnable = VK_FALSE,
1290 };
1291
1292 const VkPipelineColorBlendAttachmentState color_blend_attachment{
1293 .blendEnable = VK_FALSE,
1294 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1295 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1296 .colorBlendOp = VK_BLEND_OP_ADD,
1297 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1298 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1299 .alphaBlendOp = VK_BLEND_OP_ADD,
1300 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
1301 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
1302 };
1303
1304 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
1305 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
1306 .pNext = nullptr,
1307 .flags = 0,
1308 .logicOpEnable = VK_FALSE,
1309 .logicOp = VK_LOGIC_OP_COPY,
1310 .attachmentCount = 1,
1311 .pAttachments = &color_blend_attachment,
1312 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
1313 };
1314
1315 static constexpr std::array dynamic_states{
1316 VK_DYNAMIC_STATE_VIEWPORT,
1317 VK_DYNAMIC_STATE_SCISSOR,
1318 };
1319 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
1320 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
1321 .pNext = nullptr,
1322 .flags = 0,
1323 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
1324 .pDynamicStates = dynamic_states.data(),
1325 };
1326
1327 const VkGraphicsPipelineCreateInfo fxaa_pipeline_ci{
1328 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1329 .pNext = nullptr,
1330 .flags = 0,
1331 .stageCount = static_cast<u32>(fxaa_shader_stages.size()),
1332 .pStages = fxaa_shader_stages.data(),
1333 .pVertexInputState = &vertex_input_ci,
1334 .pInputAssemblyState = &input_assembly_ci,
1335 .pTessellationState = nullptr,
1336 .pViewportState = &viewport_state_ci,
1337 .pRasterizationState = &rasterization_ci,
1338 .pMultisampleState = &multisampling_ci,
1339 .pDepthStencilState = nullptr,
1340 .pColorBlendState = &color_blend_ci,
1341 .pDynamicState = &dynamic_state_ci,
1342 .layout = *aa_pipeline_layout,
1343 .renderPass = *aa_renderpass,
1344 .subpass = 0,
1345 .basePipelineHandle = 0,
1346 .basePipelineIndex = 0,
1347 };
1348
1349 // AA
1350 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
1351}
1352
1353void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
1354 const VkDescriptorImageInfo image_info{
1355 .sampler = nn ? *nn_sampler : *sampler,
1356 .imageView = image_view,
1357 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
1358 };
1359
1360 const VkWriteDescriptorSet sampler_write{
1361 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1362 .pNext = nullptr,
1363 .dstSet = aa_descriptor_sets[image_index],
1364 .dstBinding = 0,
1365 .dstArrayElement = 0,
1366 .descriptorCount = 1,
1367 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1368 .pImageInfo = &image_info,
1369 .pBufferInfo = nullptr,
1370 .pTexelBufferView = nullptr,
1371 };
1372
1373 const VkWriteDescriptorSet sampler_write_2{
1374 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1375 .pNext = nullptr,
1376 .dstSet = aa_descriptor_sets[image_index],
1377 .dstBinding = 1,
1378 .dstArrayElement = 0,
1379 .descriptorCount = 1,
1380 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1381 .pImageInfo = &image_info,
1382 .pBufferInfo = nullptr,
1383 .pTexelBufferView = nullptr,
1384 };
1385
1386 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
1387}
1388
1389void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
1390 const VkDescriptorBufferInfo buffer_info{
1391 .buffer = *buffer,
1392 .offset = offsetof(BufferData, uniform),
1393 .range = sizeof(BufferData::uniform),
1394 };
1395
1396 const VkWriteDescriptorSet ubo_write{
1397 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1398 .pNext = nullptr,
1399 .dstSet = descriptor_sets[image_index],
1400 .dstBinding = 0,
1401 .dstArrayElement = 0,
1402 .descriptorCount = 1,
1403 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1404 .pImageInfo = nullptr,
1405 .pBufferInfo = &buffer_info,
1406 .pTexelBufferView = nullptr,
1407 };
1408
1409 const VkDescriptorImageInfo image_info{
1410 .sampler = nn ? *nn_sampler : *sampler,
1411 .imageView = image_view,
1412 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
1413 };
1414
1415 const VkWriteDescriptorSet sampler_write{
1416 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1417 .pNext = nullptr,
1418 .dstSet = descriptor_sets[image_index],
1419 .dstBinding = 1,
1420 .dstArrayElement = 0,
1421 .descriptorCount = 1,
1422 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1423 .pImageInfo = &image_info,
1424 .pBufferInfo = nullptr,
1425 .pTexelBufferView = nullptr,
1426 };
1427
1428 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
1429}
1430
1431void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
1432 data.uniform.modelview_matrix =
1433 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
1434}
1435
1436void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
1437 const Layout::FramebufferLayout layout) const {
1438 f32 left, top, right, bottom;
1439
1440 if (fsr) {
1441 // FSR has already applied the crop, so we just want to render the image
1442 // it has produced.
1443 left = 0;
1444 top = 0;
1445 right = 1;
1446 bottom = 1;
1447 } else {
1448 // Get the normalized crop rectangle.
1449 const auto crop = NormalizeCrop(framebuffer, screen_info);
1450
1451 // Apply the crop.
1452 left = crop.left;
1453 top = crop.top;
1454 right = crop.right;
1455 bottom = crop.bottom;
1456 }
1457
1458 // Map the coordinates to the screen.
1459 const auto& screen = layout.screen;
1460 const auto x = static_cast<f32>(screen.left);
1461 const auto y = static_cast<f32>(screen.top);
1462 const auto w = static_cast<f32>(screen.GetWidth());
1463 const auto h = static_cast<f32>(screen.GetHeight());
1464
1465 data.vertices[0] = ScreenRectVertex(x, y, left, top);
1466 data.vertices[1] = ScreenRectVertex(x + w, y, right, top);
1467 data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
1468 data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
1469}
1470
1471void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {
1472 smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size);
1473}
1474
1475void BlitScreen::CreateFSR() {
1476 const auto& layout = render_window.GetFramebufferLayout();
1477 const VkExtent2D fsr_size{
1478 .width = layout.screen.GetWidth(),
1479 .height = layout.screen.GetHeight(),
1480 };
1481 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, fsr_size);
1482}
1483
1484u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const {
1485 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
1486}
1487
1488u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
1489 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
1490 return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
1491}
1492
1493} // namespace Vulkan 149} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 3eff76009..cbdf2d5d0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -3,10 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <list>
6#include <memory> 7#include <memory>
7 8
8#include "core/frontend/framebuffer_layout.h" 9#include "core/frontend/framebuffer_layout.h"
9#include "video_core/host1x/gpu_device_memory_manager.h" 10#include "video_core/host1x/gpu_device_memory_manager.h"
11#include "video_core/renderer_vulkan/present/layer.h"
10#include "video_core/vulkan_common/vulkan_memory_allocator.h" 12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
12 14
@@ -14,155 +16,67 @@ namespace Core {
14class System; 16class System;
15} 17}
16 18
17namespace Core::Frontend {
18class EmuWindow;
19}
20
21namespace Tegra { 19namespace Tegra {
22struct FramebufferConfig; 20struct FramebufferConfig;
23} 21}
24 22
25namespace VideoCore { 23namespace Settings {
26class RasterizerInterface; 24enum class ScalingFilter : u32;
27} 25} // namespace Settings
28
29namespace Service::android {
30enum class PixelFormat : u32;
31}
32 26
33namespace Vulkan { 27namespace Vulkan {
34 28
35struct ScreenInfo;
36
37class Device; 29class Device;
38class FSR;
39class RasterizerVulkan; 30class RasterizerVulkan;
40class Scheduler; 31class Scheduler;
41class SMAA;
42class Swapchain;
43class PresentManager; 32class PresentManager;
33class WindowAdaptPass;
44 34
45struct Frame; 35struct Frame;
46 36
47struct ScreenInfo { 37struct FramebufferTextureInfo {
48 VkImage image{}; 38 VkImage image{};
49 VkImageView image_view{}; 39 VkImageView image_view{};
50 u32 width{}; 40 u32 width{};
51 u32 height{}; 41 u32 height{};
42 u32 scaled_width{};
43 u32 scaled_height{};
52}; 44};
53 45
54class BlitScreen { 46class BlitScreen {
55public: 47public:
56 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, 48 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
57 Core::Frontend::EmuWindow& render_window, const Device& device, 49 MemoryAllocator& memory_allocator, PresentManager& present_manager,
58 MemoryAllocator& memory_manager, Swapchain& swapchain, 50 Scheduler& scheduler);
59 PresentManager& present_manager, Scheduler& scheduler,
60 const ScreenInfo& screen_info);
61 ~BlitScreen(); 51 ~BlitScreen();
62 52
63 void Recreate(); 53 void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
64 54 std::span<const Tegra::FramebufferConfig> framebuffers,
65 void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer, 55 const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count,
66 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); 56 VkFormat current_swapchain_view_format);
67
68 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
69 bool use_accelerated);
70 57
71 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 58 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout,
72 VkExtent2D extent); 59 VkImageView image_view,
73 60 VkFormat current_view_format);
74 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
75 VkExtent2D extent, vk::RenderPass& rd);
76 61
77private: 62private:
78 struct BufferData; 63 void WaitIdle();
79 64 void SetWindowAdaptPass();
80 void CreateStaticResources(); 65 vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
81 void CreateShaders(); 66 VkRenderPass render_pass);
82 void CreateDescriptorPool();
83 void CreateRenderPass();
84 vk::RenderPass CreateRenderPassImpl(VkFormat format);
85 void CreateDescriptorSetLayout();
86 void CreateDescriptorSets();
87 void CreatePipelineLayout();
88 void CreateGraphicsPipeline();
89 void CreateSampler();
90
91 void CreateDynamicResources();
92
93 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
94 void ReleaseRawImages();
95 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
96 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
97
98 void UpdateDescriptorSet(VkImageView image_view, bool nn) const;
99 void UpdateAADescriptorSet(VkImageView image_view, bool nn) const;
100 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
101 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
102 const Layout::FramebufferLayout layout) const;
103
104 void CreateSMAA(VkExtent2D smaa_size);
105 void CreateFSR();
106
107 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
108 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
109 67
110 Tegra::MaxwellDeviceMemoryManager& device_memory; 68 Tegra::MaxwellDeviceMemoryManager& device_memory;
111 Core::Frontend::EmuWindow& render_window;
112 const Device& device; 69 const Device& device;
113 MemoryAllocator& memory_allocator; 70 MemoryAllocator& memory_allocator;
114 Swapchain& swapchain;
115 PresentManager& present_manager; 71 PresentManager& present_manager;
116 Scheduler& scheduler; 72 Scheduler& scheduler;
117 std::size_t image_count; 73 std::size_t image_count{};
118 std::size_t image_index{}; 74 std::size_t image_index{};
119 const ScreenInfo& screen_info; 75 VkFormat swapchain_view_format{};
120 76
121 vk::ShaderModule vertex_shader; 77 Settings::ScalingFilter scaling_filter{};
122 vk::ShaderModule fxaa_vertex_shader; 78 std::unique_ptr<WindowAdaptPass> window_adapt{};
123 vk::ShaderModule fxaa_fragment_shader; 79 std::list<Layer> layers{};
124 vk::ShaderModule bilinear_fragment_shader;
125 vk::ShaderModule bicubic_fragment_shader;
126 vk::ShaderModule gaussian_fragment_shader;
127 vk::ShaderModule scaleforce_fragment_shader;
128 vk::DescriptorPool descriptor_pool;
129 vk::DescriptorSetLayout descriptor_set_layout;
130 vk::PipelineLayout pipeline_layout;
131 vk::Pipeline nearest_neighbor_pipeline;
132 vk::Pipeline bilinear_pipeline;
133 vk::Pipeline bicubic_pipeline;
134 vk::Pipeline gaussian_pipeline;
135 vk::Pipeline scaleforce_pipeline;
136 vk::RenderPass renderpass;
137 vk::DescriptorSets descriptor_sets;
138 vk::Sampler nn_sampler;
139 vk::Sampler sampler;
140
141 vk::Buffer buffer;
142
143 std::vector<u64> resource_ticks;
144
145 std::vector<vk::Image> raw_images;
146 std::vector<vk::ImageView> raw_image_views;
147
148 vk::DescriptorPool aa_descriptor_pool;
149 vk::DescriptorSetLayout aa_descriptor_set_layout;
150 vk::PipelineLayout aa_pipeline_layout;
151 vk::Pipeline aa_pipeline;
152 vk::RenderPass aa_renderpass;
153 vk::Framebuffer aa_framebuffer;
154 vk::DescriptorSets aa_descriptor_sets;
155 vk::Image aa_image;
156 vk::ImageView aa_image_view;
157
158 u32 raw_width = 0;
159 u32 raw_height = 0;
160 Service::android::PixelFormat pixel_format{};
161 VkFormat framebuffer_view_format;
162 VkFormat swapchain_view_format;
163
164 std::unique_ptr<FSR> fsr;
165 std::unique_ptr<SMAA> smaa;
166}; 80};
167 81
168} // namespace Vulkan 82} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
deleted file mode 100644
index f7a05fbc0..000000000
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5#include "common/div_ceil.h"
6#include "common/settings.h"
7
8#include "video_core/fsr.h"
9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_comp_spv.h"
13#include "video_core/renderer_vulkan/vk_fsr.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_shader_util.h"
16#include "video_core/vulkan_common/vulkan_device.h"
17
18namespace Vulkan {
19using namespace FSR;
20
21FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
22 VkExtent2D output_size_)
23 : device{device_}, memory_allocator{memory_allocator_}, image_count{image_count_},
24 output_size{output_size_} {
25
26 CreateImages();
27 CreateSampler();
28 CreateShaders();
29 CreateDescriptorPool();
30 CreateDescriptorSetLayout();
31 CreateDescriptorSets();
32 CreatePipelineLayout();
33 CreatePipeline();
34}
35
36VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
37 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) {
38
39 UpdateDescriptorSet(image_index, image_view);
40
41 scheduler.Record([this, image_index, input_image_extent, crop_rect](vk::CommandBuffer cmdbuf) {
42 const VkImageMemoryBarrier base_barrier{
43 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
44 .pNext = nullptr,
45 .srcAccessMask = 0,
46 .dstAccessMask = 0,
47 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
48 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
49 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
50 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
51 .image = {},
52 .subresourceRange =
53 {
54 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
55 .baseMipLevel = 0,
56 .levelCount = 1,
57 .baseArrayLayer = 0,
58 .layerCount = 1,
59 },
60 };
61
62 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline);
63
64 const f32 input_image_width = static_cast<f32>(input_image_extent.width);
65 const f32 input_image_height = static_cast<f32>(input_image_extent.height);
66 const f32 output_image_width = static_cast<f32>(output_size.width);
67 const f32 output_image_height = static_cast<f32>(output_size.height);
68 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
69 const f32 viewport_x = crop_rect.left * input_image_width;
70 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
71 const f32 viewport_y = crop_rect.top * input_image_height;
72
73 std::array<u32, 4 * 4> push_constants;
74 FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4,
75 push_constants.data() + 8, push_constants.data() + 12,
76
77 viewport_width, viewport_height, input_image_width, input_image_height,
78 output_image_width, output_image_height, viewport_x, viewport_y);
79 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
80
81 {
82 VkImageMemoryBarrier fsr_write_barrier = base_barrier;
83 fsr_write_barrier.image = *images[image_index];
84 fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
85
86 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
87 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, fsr_write_barrier);
88 }
89
90 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
91 descriptor_sets[image_index * 2], {});
92 cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u),
93 Common::DivCeil(output_size.height, 16u), 1);
94
95 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline);
96
97 const float sharpening =
98 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
99
100 FsrRcasCon(push_constants.data(), sharpening);
101 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
102
103 {
104 std::array<VkImageMemoryBarrier, 2> barriers;
105 auto& fsr_read_barrier = barriers[0];
106 auto& blit_write_barrier = barriers[1];
107
108 fsr_read_barrier = base_barrier;
109 fsr_read_barrier.image = *images[image_index];
110 fsr_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
111 fsr_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
112
113 blit_write_barrier = base_barrier;
114 blit_write_barrier.image = *images[image_count + image_index];
115 blit_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
116 blit_write_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
117
118 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
119 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers);
120 }
121
122 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
123 descriptor_sets[image_index * 2 + 1], {});
124 cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u),
125 Common::DivCeil(output_size.height, 16u), 1);
126
127 {
128 std::array<VkImageMemoryBarrier, 1> barriers;
129 auto& blit_read_barrier = barriers[0];
130
131 blit_read_barrier = base_barrier;
132 blit_read_barrier.image = *images[image_count + image_index];
133 blit_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
134 blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
135
136 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
137 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, {}, {}, barriers);
138 }
139 });
140
141 return *image_views[image_count + image_index];
142}
143
144void FSR::CreateDescriptorPool() {
145 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
146 {
147 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
148 .descriptorCount = static_cast<u32>(image_count * 2),
149 },
150 {
151 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
152 .descriptorCount = static_cast<u32>(image_count * 2),
153 },
154 }};
155
156 const VkDescriptorPoolCreateInfo ci{
157 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
158 .pNext = nullptr,
159 .flags = 0,
160 .maxSets = static_cast<u32>(image_count * 2),
161 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
162 .pPoolSizes = pool_sizes.data(),
163 };
164 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
165}
166
167void FSR::CreateDescriptorSetLayout() {
168 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
169 {
170 .binding = 0,
171 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
172 .descriptorCount = 1,
173 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
174 .pImmutableSamplers = sampler.address(),
175 },
176 {
177 .binding = 1,
178 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
179 .descriptorCount = 1,
180 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
181 .pImmutableSamplers = sampler.address(),
182 },
183 }};
184
185 const VkDescriptorSetLayoutCreateInfo ci{
186 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
187 .pNext = nullptr,
188 .flags = 0,
189 .bindingCount = static_cast<u32>(layout_bindings.size()),
190 .pBindings = layout_bindings.data(),
191 };
192
193 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
194}
195
196void FSR::CreateDescriptorSets() {
197 const u32 sets = static_cast<u32>(image_count * 2);
198 const std::vector layouts(sets, *descriptor_set_layout);
199
200 const VkDescriptorSetAllocateInfo ai{
201 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
202 .pNext = nullptr,
203 .descriptorPool = *descriptor_pool,
204 .descriptorSetCount = sets,
205 .pSetLayouts = layouts.data(),
206 };
207
208 descriptor_sets = descriptor_pool.Allocate(ai);
209}
210
211void FSR::CreateImages() {
212 images.resize(image_count * 2);
213 image_views.resize(image_count * 2);
214
215 for (size_t i = 0; i < image_count * 2; ++i) {
216 images[i] = memory_allocator.CreateImage(VkImageCreateInfo{
217 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
218 .pNext = nullptr,
219 .flags = 0,
220 .imageType = VK_IMAGE_TYPE_2D,
221 .format = VK_FORMAT_R16G16B16A16_SFLOAT,
222 .extent =
223 {
224 .width = output_size.width,
225 .height = output_size.height,
226 .depth = 1,
227 },
228 .mipLevels = 1,
229 .arrayLayers = 1,
230 .samples = VK_SAMPLE_COUNT_1_BIT,
231 .tiling = VK_IMAGE_TILING_OPTIMAL,
232 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
233 VK_IMAGE_USAGE_SAMPLED_BIT,
234 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
235 .queueFamilyIndexCount = 0,
236 .pQueueFamilyIndices = nullptr,
237 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
238 });
239 image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
240 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
241 .pNext = nullptr,
242 .flags = 0,
243 .image = *images[i],
244 .viewType = VK_IMAGE_VIEW_TYPE_2D,
245 .format = VK_FORMAT_R16G16B16A16_SFLOAT,
246 .components =
247 {
248 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
249 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
250 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
251 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
252 },
253 .subresourceRange =
254 {
255 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
256 .baseMipLevel = 0,
257 .levelCount = 1,
258 .baseArrayLayer = 0,
259 .layerCount = 1,
260 },
261 });
262 }
263}
264
265void FSR::CreatePipelineLayout() {
266 VkPushConstantRange push_const{
267 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
268 .offset = 0,
269 .size = sizeof(std::array<u32, 4 * 4>),
270 };
271 VkPipelineLayoutCreateInfo ci{
272 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
273 .pNext = nullptr,
274 .flags = 0,
275 .setLayoutCount = 1,
276 .pSetLayouts = descriptor_set_layout.address(),
277 .pushConstantRangeCount = 1,
278 .pPushConstantRanges = &push_const,
279 };
280
281 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
282}
283
284void FSR::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const {
285 const auto fsr_image_view = *image_views[image_index];
286 const auto blit_image_view = *image_views[image_count + image_index];
287
288 const VkDescriptorImageInfo image_info{
289 .sampler = VK_NULL_HANDLE,
290 .imageView = image_view,
291 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
292 };
293 const VkDescriptorImageInfo fsr_image_info{
294 .sampler = VK_NULL_HANDLE,
295 .imageView = fsr_image_view,
296 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
297 };
298 const VkDescriptorImageInfo blit_image_info{
299 .sampler = VK_NULL_HANDLE,
300 .imageView = blit_image_view,
301 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
302 };
303
304 VkWriteDescriptorSet sampler_write{
305 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
306 .pNext = nullptr,
307 .dstSet = descriptor_sets[image_index * 2],
308 .dstBinding = 0,
309 .dstArrayElement = 0,
310 .descriptorCount = 1,
311 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
312 .pImageInfo = &image_info,
313 .pBufferInfo = nullptr,
314 .pTexelBufferView = nullptr,
315 };
316
317 VkWriteDescriptorSet output_write{
318 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
319 .pNext = nullptr,
320 .dstSet = descriptor_sets[image_index * 2],
321 .dstBinding = 1,
322 .dstArrayElement = 0,
323 .descriptorCount = 1,
324 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
325 .pImageInfo = &fsr_image_info,
326 .pBufferInfo = nullptr,
327 .pTexelBufferView = nullptr,
328 };
329
330 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {});
331
332 sampler_write.dstSet = descriptor_sets[image_index * 2 + 1];
333 sampler_write.pImageInfo = &fsr_image_info;
334 output_write.dstSet = descriptor_sets[image_index * 2 + 1];
335 output_write.pImageInfo = &blit_image_info;
336
337 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {});
338}
339
340void FSR::CreateSampler() {
341 const VkSamplerCreateInfo ci{
342 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
343 .pNext = nullptr,
344 .flags = 0,
345 .magFilter = VK_FILTER_LINEAR,
346 .minFilter = VK_FILTER_LINEAR,
347 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
348 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
349 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
350 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
351 .mipLodBias = 0.0f,
352 .anisotropyEnable = VK_FALSE,
353 .maxAnisotropy = 0.0f,
354 .compareEnable = VK_FALSE,
355 .compareOp = VK_COMPARE_OP_NEVER,
356 .minLod = 0.0f,
357 .maxLod = 0.0f,
358 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
359 .unnormalizedCoordinates = VK_FALSE,
360 };
361
362 sampler = device.GetLogical().CreateSampler(ci);
363}
364
365void FSR::CreateShaders() {
366 if (device.IsFloat16Supported()) {
367 easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_COMP_SPV);
368 rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_COMP_SPV);
369 } else {
370 easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_COMP_SPV);
371 rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_COMP_SPV);
372 }
373}
374
375void FSR::CreatePipeline() {
376 VkPipelineShaderStageCreateInfo shader_stage_easu{
377 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
378 .pNext = nullptr,
379 .flags = 0,
380 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
381 .module = *easu_shader,
382 .pName = "main",
383 .pSpecializationInfo = nullptr,
384 };
385
386 VkPipelineShaderStageCreateInfo shader_stage_rcas{
387 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
388 .pNext = nullptr,
389 .flags = 0,
390 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
391 .module = *rcas_shader,
392 .pName = "main",
393 .pSpecializationInfo = nullptr,
394 };
395
396 VkComputePipelineCreateInfo pipeline_ci_easu{
397 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
398 .pNext = nullptr,
399 .flags = 0,
400 .stage = shader_stage_easu,
401 .layout = *pipeline_layout,
402 .basePipelineHandle = VK_NULL_HANDLE,
403 .basePipelineIndex = 0,
404 };
405
406 VkComputePipelineCreateInfo pipeline_ci_rcas{
407 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
408 .pNext = nullptr,
409 .flags = 0,
410 .stage = shader_stage_rcas,
411 .layout = *pipeline_layout,
412 .basePipelineHandle = VK_NULL_HANDLE,
413 .basePipelineIndex = 0,
414 };
415
416 easu_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_easu);
417 rcas_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_rcas);
418}
419
420} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h
deleted file mode 100644
index 3505c1416..000000000
--- a/src/video_core/renderer_vulkan/vk_fsr.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14
15class FSR {
16public:
17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
18 VkExtent2D output_size);
19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
20 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect);
21
22private:
23 void CreateDescriptorPool();
24 void CreateDescriptorSetLayout();
25 void CreateDescriptorSets();
26 void CreateImages();
27 void CreateSampler();
28 void CreateShaders();
29 void CreatePipeline();
30 void CreatePipelineLayout();
31
32 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
33
34 const Device& device;
35 MemoryAllocator& memory_allocator;
36 size_t image_count;
37 VkExtent2D output_size;
38
39 vk::DescriptorPool descriptor_pool;
40 vk::DescriptorSetLayout descriptor_set_layout;
41 vk::DescriptorSets descriptor_sets;
42 vk::PipelineLayout pipeline_layout;
43 vk::ShaderModule easu_shader;
44 vk::ShaderModule rcas_shader;
45 vk::Pipeline easu_pipeline;
46 vk::Pipeline rcas_pipeline;
47 vk::Sampler sampler;
48 std::vector<vk::Image> images;
49 std::vector<vk::ImageView> image_views;
50};
51
52} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 5bf41b81f..aa0a027bb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -165,10 +165,9 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
165 165
166RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 166RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
167 Tegra::MaxwellDeviceMemoryManager& device_memory_, 167 Tegra::MaxwellDeviceMemoryManager& device_memory_,
168 ScreenInfo& screen_info_, const Device& device_, 168 const Device& device_, MemoryAllocator& memory_allocator_,
169 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, 169 StateTracker& state_tracker_, Scheduler& scheduler_)
170 Scheduler& scheduler_) 170 : gpu{gpu_}, device_memory{device_memory_}, device{device_},
171 : gpu{gpu_}, device_memory{device_memory_}, screen_info{screen_info_}, device{device_},
172 memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, 171 memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_},
173 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), 172 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
174 guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler), 173 guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler),
@@ -783,23 +782,29 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
783 query_cache.InvalidateRegion(*cpu_addr, copy_size); 782 query_cache.InvalidateRegion(*cpu_addr, copy_size);
784} 783}
785 784
786bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, 785std::optional<FramebufferTextureInfo> RasterizerVulkan::AccelerateDisplay(
787 DAddr framebuffer_addr, u32 pixel_stride) { 786 const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) {
788 if (!framebuffer_addr) { 787 if (!framebuffer_addr) {
789 return false; 788 return {};
790 } 789 }
791 std::scoped_lock lock{texture_cache.mutex}; 790 std::scoped_lock lock{texture_cache.mutex};
792 ImageView* const image_view = 791 const auto [image_view, scaled] =
793 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); 792 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
794 if (!image_view) { 793 if (!image_view) {
795 return false; 794 return {};
796 } 795 }
797 query_cache.NotifySegment(false); 796 query_cache.NotifySegment(false);
798 screen_info.image = image_view->ImageHandle(); 797
799 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); 798 const auto& resolution = Settings::values.resolution_info;
800 screen_info.width = image_view->size.width; 799
801 screen_info.height = image_view->size.height; 800 FramebufferTextureInfo info{};
802 return true; 801 info.image = image_view->ImageHandle();
802 info.image_view = image_view->Handle(Shader::TextureType::Color2D);
803 info.width = image_view->size.width;
804 info.height = image_view->size.height;
805 info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width;
806 info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height;
807 return info;
803} 808}
804 809
805void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading, 810void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 881ee0993..0617b37f0 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -43,7 +43,7 @@ class Maxwell3D;
43 43
44namespace Vulkan { 44namespace Vulkan {
45 45
46struct ScreenInfo; 46struct FramebufferTextureInfo;
47 47
48class StateTracker; 48class StateTracker;
49 49
@@ -78,9 +78,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerInterface,
78public: 78public:
79 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 79 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
80 Tegra::MaxwellDeviceMemoryManager& device_memory_, 80 Tegra::MaxwellDeviceMemoryManager& device_memory_,
81 ScreenInfo& screen_info_, const Device& device_, 81 const Device& device_, MemoryAllocator& memory_allocator_,
82 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, 82 StateTracker& state_tracker_, Scheduler& scheduler_);
83 Scheduler& scheduler_);
84 ~RasterizerVulkan() override; 83 ~RasterizerVulkan() override;
85 84
86 void Draw(bool is_indexed, u32 instance_count) override; 85 void Draw(bool is_indexed, u32 instance_count) override;
@@ -126,8 +125,6 @@ public:
126 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 125 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
127 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 126 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
128 std::span<const u8> memory) override; 127 std::span<const u8> memory) override;
129 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
130 u32 pixel_stride) override;
131 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 128 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
132 const VideoCore::DiskResourceLoadCallback& callback) override; 129 const VideoCore::DiskResourceLoadCallback& callback) override;
133 130
@@ -137,6 +134,10 @@ public:
137 134
138 void ReleaseChannel(s32 channel_id) override; 135 void ReleaseChannel(s32 channel_id) override;
139 136
137 std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config,
138 VAddr framebuffer_addr,
139 u32 pixel_stride);
140
140private: 141private:
141 static constexpr size_t MAX_TEXTURES = 192; 142 static constexpr size_t MAX_TEXTURES = 192;
142 static constexpr size_t MAX_IMAGES = 48; 143 static constexpr size_t MAX_IMAGES = 48;
@@ -182,7 +183,6 @@ private:
182 Tegra::GPU& gpu; 183 Tegra::GPU& gpu;
183 Tegra::MaxwellDeviceMemoryManager& device_memory; 184 Tegra::MaxwellDeviceMemoryManager& device_memory;
184 185
185 ScreenInfo& screen_info;
186 const Device& device; 186 const Device& device;
187 MemoryAllocator& memory_allocator; 187 MemoryAllocator& memory_allocator;
188 StateTracker& state_tracker; 188 StateTracker& state_tracker;