summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/renderer_vulkan/present/filters.cpp70
-rw-r--r--src/video_core/renderer_vulkan/present/filters.h30
-rw-r--r--src/video_core/renderer_vulkan/present/util.cpp50
-rw-r--r--src/video_core/renderer_vulkan/present/util.h2
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.cpp512
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.h71
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp117
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp945
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h93
11 files changed, 985 insertions, 913 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 240b80c6e..825815ebd 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -158,6 +158,9 @@ add_library(video_core STATIC
158 renderer_opengl/renderer_opengl.h 158 renderer_opengl/renderer_opengl.h
159 renderer_opengl/util_shaders.cpp 159 renderer_opengl/util_shaders.cpp
160 renderer_opengl/util_shaders.h 160 renderer_opengl/util_shaders.h
161 renderer_vulkan/present/anti_alias_pass.h
162 renderer_vulkan/present/filters.cpp
163 renderer_vulkan/present/filters.h
161 renderer_vulkan/present/fsr.cpp 164 renderer_vulkan/present/fsr.cpp
162 renderer_vulkan/present/fsr.h 165 renderer_vulkan/present/fsr.h
163 renderer_vulkan/present/fxaa.cpp 166 renderer_vulkan/present/fxaa.cpp
@@ -166,6 +169,8 @@ add_library(video_core STATIC
166 renderer_vulkan/present/smaa.h 169 renderer_vulkan/present/smaa.h
167 renderer_vulkan/present/util.cpp 170 renderer_vulkan/present/util.cpp
168 renderer_vulkan/present/util.h 171 renderer_vulkan/present/util.h
172 renderer_vulkan/present/window_adapt_pass.cpp
173 renderer_vulkan/present/window_adapt_pass.h
169 renderer_vulkan/blit_image.cpp 174 renderer_vulkan/blit_image.cpp
170 renderer_vulkan/blit_image.h 175 renderer_vulkan/blit_image.h
171 renderer_vulkan/fixed_pipeline_state.cpp 176 renderer_vulkan/fixed_pipeline_state.cpp
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..ee6239cc4
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/filters.cpp
@@ -0,0 +1,70 @@
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,
31 const MemoryAllocator& memory_allocator,
32 size_t image_count, VkFormat frame_format) {
33 return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format,
34 CreateNearestNeighborSampler(device),
35 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
36}
37
38std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device,
39 const MemoryAllocator& memory_allocator,
40 size_t image_count, VkFormat frame_format) {
41 return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format,
42 CreateBilinearSampler(device),
43 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
44}
45
46std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device,
47 const MemoryAllocator& memory_allocator,
48 size_t image_count, VkFormat frame_format) {
49 return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format,
50 CreateBilinearSampler(device),
51 BuildShader(device, PRESENT_BICUBIC_FRAG_SPV));
52}
53
54std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device,
55 const MemoryAllocator& memory_allocator,
56 size_t image_count, VkFormat frame_format) {
57 return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format,
58 CreateBilinearSampler(device),
59 BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV));
60}
61
62std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device,
63 const MemoryAllocator& memory_allocator,
64 size_t image_count, VkFormat frame_format) {
65 return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format,
66 CreateBilinearSampler(device),
67 SelectScaleForceShader(device));
68}
69
70} // 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..42d7052da
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/filters.h
@@ -0,0 +1,30 @@
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
10std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device,
11 const MemoryAllocator& memory_allocator,
12 size_t image_count, VkFormat frame_format);
13
14std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device,
15 const MemoryAllocator& memory_allocator,
16 size_t image_count, VkFormat frame_format);
17
18std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device,
19 const MemoryAllocator& memory_allocator,
20 size_t image_count, VkFormat frame_format);
21
22std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device,
23 const MemoryAllocator& memory_allocator,
24 size_t image_count, VkFormat frame_format);
25
26std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device,
27 const MemoryAllocator& memory_allocator,
28 size_t image_count, VkFormat frame_format);
29
30} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index a445b213e..cd6061101 100644
--- a/src/video_core/renderer_vulkan/present/util.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -441,6 +441,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>
441 }; 441 };
442} 442}
443 443
444vk::Sampler CreateBilinearSampler(const Device& device) {
445 const VkSamplerCreateInfo ci{
446 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
447 .pNext = nullptr,
448 .flags = 0,
449 .magFilter = VK_FILTER_LINEAR,
450 .minFilter = VK_FILTER_LINEAR,
451 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
452 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
453 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
454 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
455 .mipLodBias = 0.0f,
456 .anisotropyEnable = VK_FALSE,
457 .maxAnisotropy = 0.0f,
458 .compareEnable = VK_FALSE,
459 .compareOp = VK_COMPARE_OP_NEVER,
460 .minLod = 0.0f,
461 .maxLod = 0.0f,
462 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
463 .unnormalizedCoordinates = VK_FALSE,
464 };
465
466 return device.GetLogical().CreateSampler(ci);
467}
468
469vk::Sampler CreateNearestNeighborSampler(const Device& device) {
470 const VkSamplerCreateInfo ci_nn{
471 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
472 .pNext = nullptr,
473 .flags = 0,
474 .magFilter = VK_FILTER_NEAREST,
475 .minFilter = VK_FILTER_NEAREST,
476 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
477 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
478 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
479 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
480 .mipLodBias = 0.0f,
481 .anisotropyEnable = VK_FALSE,
482 .maxAnisotropy = 0.0f,
483 .compareEnable = VK_FALSE,
484 .compareOp = VK_COMPARE_OP_NEVER,
485 .minLod = 0.0f,
486 .maxLod = 0.0f,
487 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
488 .unnormalizedCoordinates = VK_FALSE,
489 };
490
491 return device.GetLogical().CreateSampler(ci_nn);
492}
493
444void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { 494void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
445 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ 495 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
446 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 496 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h
index 93cfdd16b..ea9a26c3d 100644
--- a/src/video_core/renderer_vulkan/present/util.h
+++ b/src/video_core/renderer_vulkan/present/util.h
@@ -39,6 +39,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
39VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, 39VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
40 VkSampler sampler, VkImageView view, 40 VkSampler sampler, VkImageView view,
41 VkDescriptorSet set, u32 binding); 41 VkDescriptorSet set, u32 binding);
42vk::Sampler CreateBilinearSampler(const Device& device);
43vk::Sampler CreateNearestNeighborSampler(const Device& device);
42 44
43void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, 45void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
44 VkExtent2D extent); 46 VkExtent2D extent);
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..7fd9ecd22
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
@@ -0,0 +1,512 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "core/frontend/framebuffer_layout.h"
7#include "video_core/host_shaders/vulkan_present_vert_spv.h"
8#include "video_core/renderer_vulkan/present/util.h"
9#include "video_core/renderer_vulkan/present/window_adapt_pass.h"
10#include "video_core/renderer_vulkan/vk_present_manager.h"
11#include "video_core/renderer_vulkan/vk_shader_util.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14
15namespace Vulkan {
16
17namespace {
18
19struct ScreenRectVertex {
20 ScreenRectVertex() = default;
21 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {}
22
23 std::array<f32, 2> position;
24 std::array<f32, 2> tex_coord;
25
26 static VkVertexInputBindingDescription GetDescription() {
27 return {
28 .binding = 0,
29 .stride = sizeof(ScreenRectVertex),
30 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
31 };
32 }
33
34 static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
35 return {{
36 {
37 .location = 0,
38 .binding = 0,
39 .format = VK_FORMAT_R32G32_SFLOAT,
40 .offset = offsetof(ScreenRectVertex, position),
41 },
42 {
43 .location = 1,
44 .binding = 0,
45 .format = VK_FORMAT_R32G32_SFLOAT,
46 .offset = offsetof(ScreenRectVertex, tex_coord),
47 },
48 }};
49 }
50};
51
52std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
53 // clang-format off
54 return { 2.f / width, 0.f, 0.f, 0.f,
55 0.f, 2.f / height, 0.f, 0.f,
56 0.f, 0.f, 1.f, 0.f,
57 -1.f, -1.f, 0.f, 1.f};
58 // clang-format on
59}
60
61} // Anonymous namespace
62
63struct WindowAdaptPass::BufferData {
64 struct {
65 std::array<f32, 4 * 4> modelview_matrix;
66 } uniform;
67
68 std::array<ScreenRectVertex, 4> vertices;
69};
70
71WindowAdaptPass::WindowAdaptPass(const Device& device_, const MemoryAllocator& memory_allocator,
72 size_t num_images, VkFormat frame_format, vk::Sampler&& sampler_,
73 vk::ShaderModule&& fragment_shader_)
74 : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) {
75 CreateDescriptorPool(num_images);
76 CreateDescriptorSetLayout();
77 CreateDescriptorSets(num_images);
78 CreatePipelineLayout();
79 CreateVertexShader();
80 CreateRenderPass(frame_format);
81 CreatePipeline();
82 CreateBuffer(memory_allocator);
83}
84
85WindowAdaptPass::~WindowAdaptPass() = default;
86
87void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view,
88 VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect,
89 const Layout::FramebufferLayout& layout, Frame* dst) {
90 ConfigureLayout(image_index, src_image_view, layout, crop_rect);
91
92 const VkFramebuffer host_framebuffer{*dst->framebuffer};
93 const VkRenderPass renderpass{*render_pass};
94 const VkPipeline graphics_pipeline{*pipeline};
95 const VkDescriptorSet descriptor_set{descriptor_sets[image_index]};
96 const VkExtent2D render_area{
97 .width = dst->width,
98 .height = dst->height,
99 };
100
101 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
102 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
103 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
104 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
105 const VkClearValue clear_color{
106 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
107 };
108 const VkRenderPassBeginInfo renderpass_bi{
109 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
110 .pNext = nullptr,
111 .renderPass = renderpass,
112 .framebuffer = host_framebuffer,
113 .renderArea =
114 {
115 .offset = {0, 0},
116 .extent = render_area,
117 },
118 .clearValueCount = 1,
119 .pClearValues = &clear_color,
120 };
121 const VkViewport viewport{
122 .x = 0.0f,
123 .y = 0.0f,
124 .width = static_cast<float>(render_area.width),
125 .height = static_cast<float>(render_area.height),
126 .minDepth = 0.0f,
127 .maxDepth = 1.0f,
128 };
129 const VkRect2D scissor{
130 .offset = {0, 0},
131 .extent = render_area,
132 };
133 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
134 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
135 cmdbuf.SetViewport(0, viewport);
136 cmdbuf.SetScissor(0, scissor);
137 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
138 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
139 descriptor_set, {});
140 cmdbuf.Draw(4, 1, 0, 0);
141 cmdbuf.EndRenderPass();
142 });
143}
144
145VkRenderPass WindowAdaptPass::GetRenderPass() {
146 return *render_pass;
147}
148
149void WindowAdaptPass::CreateDescriptorPool(size_t num_images) {
150 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
151 {
152 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
153 .descriptorCount = static_cast<u32>(num_images),
154 },
155 {
156 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
157 .descriptorCount = static_cast<u32>(num_images),
158 },
159 }};
160
161 const VkDescriptorPoolCreateInfo ci{
162 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
163 .pNext = nullptr,
164 .flags = 0,
165 .maxSets = static_cast<u32>(num_images),
166 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
167 .pPoolSizes = pool_sizes.data(),
168 };
169 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
170}
171
172void WindowAdaptPass::CreateDescriptorSetLayout() {
173 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
174 {
175 .binding = 0,
176 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
177 .descriptorCount = 1,
178 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
179 .pImmutableSamplers = nullptr,
180 },
181 {
182 .binding = 1,
183 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
184 .descriptorCount = 1,
185 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
186 .pImmutableSamplers = nullptr,
187 },
188 }};
189
190 const VkDescriptorSetLayoutCreateInfo ci{
191 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
192 .pNext = nullptr,
193 .flags = 0,
194 .bindingCount = static_cast<u32>(layout_bindings.size()),
195 .pBindings = layout_bindings.data(),
196 };
197
198 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
199}
200
201void WindowAdaptPass::CreateDescriptorSets(size_t num_images) {
202 const std::vector layouts(num_images, *descriptor_set_layout);
203 descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts);
204}
205
206void WindowAdaptPass::CreateBuffer(const MemoryAllocator& memory_allocator) {
207 const VkBufferCreateInfo ci{
208 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
209 .pNext = nullptr,
210 .flags = 0,
211 .size = sizeof(BufferData),
212 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
213 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
214 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
215 .queueFamilyIndexCount = 0,
216 .pQueueFamilyIndices = nullptr,
217 };
218
219 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
220}
221
222void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
223 const VkAttachmentDescription color_attachment{
224 .flags = 0,
225 .format = frame_format,
226 .samples = VK_SAMPLE_COUNT_1_BIT,
227 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
228 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
229 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
230 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
231 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
232 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
233 };
234
235 const VkAttachmentReference color_attachment_ref{
236 .attachment = 0,
237 .layout = VK_IMAGE_LAYOUT_GENERAL,
238 };
239
240 const VkSubpassDescription subpass_description{
241 .flags = 0,
242 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
243 .inputAttachmentCount = 0,
244 .pInputAttachments = nullptr,
245 .colorAttachmentCount = 1,
246 .pColorAttachments = &color_attachment_ref,
247 .pResolveAttachments = nullptr,
248 .pDepthStencilAttachment = nullptr,
249 .preserveAttachmentCount = 0,
250 .pPreserveAttachments = nullptr,
251 };
252
253 const VkSubpassDependency dependency{
254 .srcSubpass = VK_SUBPASS_EXTERNAL,
255 .dstSubpass = 0,
256 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
257 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
258 .srcAccessMask = 0,
259 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
260 .dependencyFlags = 0,
261 };
262
263 const VkRenderPassCreateInfo renderpass_ci{
264 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
265 .pNext = nullptr,
266 .flags = 0,
267 .attachmentCount = 1,
268 .pAttachments = &color_attachment,
269 .subpassCount = 1,
270 .pSubpasses = &subpass_description,
271 .dependencyCount = 1,
272 .pDependencies = &dependency,
273 };
274
275 render_pass = device.GetLogical().CreateRenderPass(renderpass_ci);
276}
277
278void WindowAdaptPass::CreateVertexShader() {
279 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
280}
281
282void WindowAdaptPass::CreatePipelineLayout() {
283 const VkPipelineLayoutCreateInfo ci{
284 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
285 .pNext = nullptr,
286 .flags = 0,
287 .setLayoutCount = 1,
288 .pSetLayouts = descriptor_set_layout.address(),
289 .pushConstantRangeCount = 0,
290 .pPushConstantRanges = nullptr,
291 };
292 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
293}
294
295void WindowAdaptPass::SetUniformData(BufferData& data,
296 const Layout::FramebufferLayout& layout) const {
297 data.uniform.modelview_matrix =
298 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
299}
300
301void WindowAdaptPass::SetVertexData(BufferData& data, const Layout::FramebufferLayout& layout,
302 const Common::Rectangle<f32>& crop) const {
303 // Map the coordinates to the screen.
304 const auto& screen = layout.screen;
305 const auto x = static_cast<f32>(screen.left);
306 const auto y = static_cast<f32>(screen.top);
307 const auto w = static_cast<f32>(screen.GetWidth());
308 const auto h = static_cast<f32>(screen.GetHeight());
309
310 data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
311 data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
312 data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
313 data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
314}
315
316void WindowAdaptPass::UpdateDescriptorSet(size_t image_index, VkImageView image_view) {
317 const VkDescriptorBufferInfo buffer_info{
318 .buffer = *buffer,
319 .offset = offsetof(BufferData, uniform),
320 .range = sizeof(BufferData::uniform),
321 };
322
323 const VkWriteDescriptorSet ubo_write{
324 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
325 .pNext = nullptr,
326 .dstSet = descriptor_sets[image_index],
327 .dstBinding = 0,
328 .dstArrayElement = 0,
329 .descriptorCount = 1,
330 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
331 .pImageInfo = nullptr,
332 .pBufferInfo = &buffer_info,
333 .pTexelBufferView = nullptr,
334 };
335
336 const VkDescriptorImageInfo image_info{
337 .sampler = *sampler,
338 .imageView = image_view,
339 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
340 };
341
342 const VkWriteDescriptorSet sampler_write{
343 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
344 .pNext = nullptr,
345 .dstSet = descriptor_sets[image_index],
346 .dstBinding = 1,
347 .dstArrayElement = 0,
348 .descriptorCount = 1,
349 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
350 .pImageInfo = &image_info,
351 .pBufferInfo = nullptr,
352 .pTexelBufferView = nullptr,
353 };
354
355 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
356}
357
358void WindowAdaptPass::ConfigureLayout(size_t image_index, VkImageView image_view,
359 const Layout::FramebufferLayout& layout,
360 const Common::Rectangle<f32>& crop_rect) {
361 BufferData data;
362 SetUniformData(data, layout);
363 SetVertexData(data, layout, crop_rect);
364
365 const std::span<u8> mapped_span = buffer.Mapped();
366 std::memcpy(mapped_span.data(), &data, sizeof(data));
367
368 UpdateDescriptorSet(image_index, image_view);
369}
370
371void WindowAdaptPass::CreatePipeline() {
372 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
373 {
374 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
375 .pNext = nullptr,
376 .flags = 0,
377 .stage = VK_SHADER_STAGE_VERTEX_BIT,
378 .module = *vertex_shader,
379 .pName = "main",
380 .pSpecializationInfo = nullptr,
381 },
382 {
383 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
384 .pNext = nullptr,
385 .flags = 0,
386 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
387 .module = *fragment_shader,
388 .pName = "main",
389 .pSpecializationInfo = nullptr,
390 },
391 }};
392
393 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
394 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
395
396 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
397 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
398 .pNext = nullptr,
399 .flags = 0,
400 .vertexBindingDescriptionCount = 1,
401 .pVertexBindingDescriptions = &vertex_binding_description,
402 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
403 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
404 };
405
406 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
407 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
408 .pNext = nullptr,
409 .flags = 0,
410 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
411 .primitiveRestartEnable = VK_FALSE,
412 };
413
414 const VkPipelineViewportStateCreateInfo viewport_state_ci{
415 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
416 .pNext = nullptr,
417 .flags = 0,
418 .viewportCount = 1,
419 .pViewports = nullptr,
420 .scissorCount = 1,
421 .pScissors = nullptr,
422 };
423
424 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
425 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
426 .pNext = nullptr,
427 .flags = 0,
428 .depthClampEnable = VK_FALSE,
429 .rasterizerDiscardEnable = VK_FALSE,
430 .polygonMode = VK_POLYGON_MODE_FILL,
431 .cullMode = VK_CULL_MODE_NONE,
432 .frontFace = VK_FRONT_FACE_CLOCKWISE,
433 .depthBiasEnable = VK_FALSE,
434 .depthBiasConstantFactor = 0.0f,
435 .depthBiasClamp = 0.0f,
436 .depthBiasSlopeFactor = 0.0f,
437 .lineWidth = 1.0f,
438 };
439
440 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
441 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
442 .pNext = nullptr,
443 .flags = 0,
444 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
445 .sampleShadingEnable = VK_FALSE,
446 .minSampleShading = 0.0f,
447 .pSampleMask = nullptr,
448 .alphaToCoverageEnable = VK_FALSE,
449 .alphaToOneEnable = VK_FALSE,
450 };
451
452 const VkPipelineColorBlendAttachmentState color_blend_attachment{
453 .blendEnable = VK_FALSE,
454 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
455 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
456 .colorBlendOp = VK_BLEND_OP_ADD,
457 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
458 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
459 .alphaBlendOp = VK_BLEND_OP_ADD,
460 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
461 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
462 };
463
464 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
465 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
466 .pNext = nullptr,
467 .flags = 0,
468 .logicOpEnable = VK_FALSE,
469 .logicOp = VK_LOGIC_OP_COPY,
470 .attachmentCount = 1,
471 .pAttachments = &color_blend_attachment,
472 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
473 };
474
475 static constexpr std::array dynamic_states{
476 VK_DYNAMIC_STATE_VIEWPORT,
477 VK_DYNAMIC_STATE_SCISSOR,
478 };
479 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
480 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
481 .pNext = nullptr,
482 .flags = 0,
483 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
484 .pDynamicStates = dynamic_states.data(),
485 };
486
487 const VkGraphicsPipelineCreateInfo pipeline_ci{
488 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
489 .pNext = nullptr,
490 .flags = 0,
491 .stageCount = static_cast<u32>(shader_stages.size()),
492 .pStages = shader_stages.data(),
493 .pVertexInputState = &vertex_input_ci,
494 .pInputAssemblyState = &input_assembly_ci,
495 .pTessellationState = nullptr,
496 .pViewportState = &viewport_state_ci,
497 .pRasterizationState = &rasterization_ci,
498 .pMultisampleState = &multisampling_ci,
499 .pDepthStencilState = nullptr,
500 .pColorBlendState = &color_blend_ci,
501 .pDynamicState = &dynamic_state_ci,
502 .layout = *pipeline_layout,
503 .renderPass = *render_pass,
504 .subpass = 0,
505 .basePipelineHandle = 0,
506 .basePipelineIndex = 0,
507 };
508
509 pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci);
510}
511
512} // 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..5309233a2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
@@ -0,0 +1,71 @@
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/vulkan_common/vulkan_wrapper.h"
8
9namespace Layout {
10struct FramebufferLayout;
11}
12
13namespace Tegra {
14struct FramebufferConfig;
15}
16
17namespace Vulkan {
18
19class Device;
20struct Frame;
21class MemoryAllocator;
22class Scheduler;
23
24class WindowAdaptPass final {
25public:
26 explicit WindowAdaptPass(const Device& device, const MemoryAllocator& memory_allocator,
27 size_t num_images, VkFormat frame_format, vk::Sampler&& sampler,
28 vk::ShaderModule&& fragment_shader);
29 ~WindowAdaptPass();
30
31 void Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view,
32 VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect,
33 const Layout::FramebufferLayout& layout, Frame* dst);
34
35 VkRenderPass GetRenderPass();
36
37private:
38 struct BufferData;
39
40 void SetUniformData(BufferData& data, const Layout::FramebufferLayout& layout) const;
41 void SetVertexData(BufferData& data, const Layout::FramebufferLayout& layout,
42 const Common::Rectangle<f32>& crop_rect) const;
43 void UpdateDescriptorSet(size_t image_index, VkImageView image_view);
44 void ConfigureLayout(size_t image_index, VkImageView image_view,
45 const Layout::FramebufferLayout& layout,
46 const Common::Rectangle<f32>& crop_rect);
47
48 void CreateDescriptorPool(size_t num_images);
49 void CreateDescriptorSetLayout();
50 void CreateDescriptorSets(size_t num_images);
51 void CreatePipelineLayout();
52 void CreateVertexShader();
53 void CreateRenderPass(VkFormat frame_format);
54 void CreatePipeline();
55 void CreateBuffer(const MemoryAllocator& memory_allocator);
56
57private:
58 const Device& device;
59 vk::DescriptorPool descriptor_pool;
60 vk::DescriptorSetLayout descriptor_set_layout;
61 vk::DescriptorSets descriptor_sets;
62 vk::PipelineLayout pipeline_layout;
63 vk::Sampler sampler;
64 vk::ShaderModule vertex_shader;
65 vk::ShaderModule fragment_shader;
66 vk::RenderPass render_pass;
67 vk::Pipeline pipeline;
68 vk::Buffer buffer;
69};
70
71} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index e1fe53bbd..2912aaff6 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -97,8 +97,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
97 render_window.GetFramebufferLayout().height), 97 render_window.GetFramebufferLayout().height),
98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
99 surface), 99 surface),
100 blit_screen(device_memory, render_window, device, memory_allocator, swapchain, 100 blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler),
101 present_manager, scheduler), 101 blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler),
102 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, 102 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
103 scheduler) { 103 scheduler) {
104 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 104 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
@@ -127,7 +127,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
127 127
128 RenderScreenshot(*framebuffer); 128 RenderScreenshot(*framebuffer);
129 Frame* frame = present_manager.GetRenderFrame(); 129 Frame* frame = present_manager.GetRenderFrame();
130 blit_screen.DrawToSwapchain(rasterizer, frame, *framebuffer); 130 blit_swapchain.DrawToFrame(rasterizer, frame, *framebuffer,
131 render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
132 swapchain.GetImageViewFormat());
131 scheduler.Flush(*frame->render_ready); 133 scheduler.Flush(*frame->render_ready);
132 present_manager.Present(frame); 134 present_manager.Present(frame);
133 135
@@ -166,54 +168,65 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
166 return; 168 return;
167 } 169 }
168 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 170 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
169 vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{ 171 auto frame = [&]() {
170 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 172 vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{
171 .pNext = nullptr, 173 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
172 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, 174 .pNext = nullptr,
173 .imageType = VK_IMAGE_TYPE_2D, 175 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
174 .format = VK_FORMAT_B8G8R8A8_UNORM, 176 .imageType = VK_IMAGE_TYPE_2D,
175 .extent = 177 .format = VK_FORMAT_B8G8R8A8_UNORM,
176 { 178 .extent =
177 .width = layout.width, 179 {
178 .height = layout.height, 180 .width = layout.width,
179 .depth = 1, 181 .height = layout.height,
182 .depth = 1,
183 },
184 .mipLevels = 1,
185 .arrayLayers = 1,
186 .samples = VK_SAMPLE_COUNT_1_BIT,
187 .tiling = VK_IMAGE_TILING_OPTIMAL,
188 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
189 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
190 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
191 .queueFamilyIndexCount = 0,
192 .pQueueFamilyIndices = nullptr,
193 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
194 });
195
196 vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
197 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
198 .pNext = nullptr,
199 .flags = 0,
200 .image = *staging_image,
201 .viewType = VK_IMAGE_VIEW_TYPE_2D,
202 .format = VK_FORMAT_B8G8R8A8_UNORM,
203 .components{
204 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
205 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
206 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
207 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
180 }, 208 },
181 .mipLevels = 1, 209 .subresourceRange{
182 .arrayLayers = 1, 210 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
183 .samples = VK_SAMPLE_COUNT_1_BIT, 211 .baseMipLevel = 0,
184 .tiling = VK_IMAGE_TILING_OPTIMAL, 212 .levelCount = 1,
185 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | 213 .baseArrayLayer = 0,
186 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 214 .layerCount = VK_REMAINING_ARRAY_LAYERS,
187 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 215 },
188 .queueFamilyIndexCount = 0, 216 });
189 .pQueueFamilyIndices = nullptr, 217 vk::Framebuffer screenshot_fb =
190 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 218 blit_screenshot.CreateFramebuffer(layout, *dst_view, VK_FORMAT_B8G8R8A8_UNORM);
191 }); 219 return Frame{
220 .width = layout.width,
221 .height = layout.height,
222 .image = std::move(staging_image),
223 .image_view = std::move(dst_view),
224 .framebuffer = std::move(screenshot_fb),
225 };
226 }();
192 227
193 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 228 blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffer, layout, 1,
194 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 229 VK_FORMAT_B8G8R8A8_UNORM);
195 .pNext = nullptr,
196 .flags = 0,
197 .image = *staging_image,
198 .viewType = VK_IMAGE_VIEW_TYPE_2D,
199 .format = VK_FORMAT_B8G8R8A8_UNORM,
200 .components{
201 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
202 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
203 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
204 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
205 },
206 .subresourceRange{
207 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
208 .baseMipLevel = 0,
209 .levelCount = 1,
210 .baseArrayLayer = 0,
211 .layerCount = VK_REMAINING_ARRAY_LAYERS,
212 },
213 });
214 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
215 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
216 blit_screen.Draw(rasterizer, framebuffer, *screenshot_fb, layout, render_area);
217 230
218 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); 231 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
219 const VkBufferCreateInfo dst_buffer_info{ 232 const VkBufferCreateInfo dst_buffer_info{
@@ -240,7 +253,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
240 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 253 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
241 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 254 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
242 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 255 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
243 .image = *staging_image, 256 .image = *frame.image,
244 .subresourceRange{ 257 .subresourceRange{
245 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 258 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
246 .baseMipLevel = 0, 259 .baseMipLevel = 0,
@@ -258,7 +271,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
258 .newLayout = VK_IMAGE_LAYOUT_GENERAL, 271 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
259 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 272 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
260 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 273 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
261 .image = *staging_image, 274 .image = *frame.image,
262 .subresourceRange{ 275 .subresourceRange{
263 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 276 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
264 .baseMipLevel = 0, 277 .baseMipLevel = 0,
@@ -292,7 +305,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
292 }; 305 };
293 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 306 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
294 0, read_barrier); 307 0, read_barrier);
295 cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer, 308 cmdbuf.CopyImageToBuffer(*frame.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
296 copy); 309 copy);
297 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 310 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
298 0, memory_write_barrier, nullptr, image_write_barrier); 311 0, memory_write_barrier, nullptr, image_write_barrier);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index d7d006b20..5b0560e68 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -78,7 +78,8 @@ private:
78 Scheduler scheduler; 78 Scheduler scheduler;
79 Swapchain swapchain; 79 Swapchain swapchain;
80 PresentManager present_manager; 80 PresentManager present_manager;
81 BlitScreen blit_screen; 81 BlitScreen blit_swapchain;
82 BlitScreen blit_screenshot;
82 RasterizerVulkan rasterizer; 83 RasterizerVulkan rasterizer;
83 std::optional<TurboMode> turbo_mode; 84 std::optional<TurboMode> turbo_mode;
84}; 85};
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index fe1a7b0cd..fd7c28779 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -9,19 +9,12 @@
9 9
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "common/polyfill_ranges.h"
14#include "common/settings.h" 12#include "common/settings.h"
15#include "core/core.h" 13#include "core/core.h"
16#include "core/frontend/emu_window.h" 14#include "core/frontend/emu_window.h"
17#include "video_core/gpu.h" 15#include "video_core/gpu.h"
18#include "video_core/host1x/gpu_device_memory_manager.h" 16#include "video_core/host1x/gpu_device_memory_manager.h"
19#include "video_core/host_shaders/present_bicubic_frag_spv.h" 17#include "video_core/renderer_vulkan/present/filters.h"
20#include "video_core/host_shaders/present_gaussian_frag_spv.h"
21#include "video_core/host_shaders/vulkan_present_frag_spv.h"
22#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
23#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
24#include "video_core/host_shaders/vulkan_present_vert_spv.h"
25#include "video_core/renderer_vulkan/present/fsr.h" 18#include "video_core/renderer_vulkan/present/fsr.h"
26#include "video_core/renderer_vulkan/present/fxaa.h" 19#include "video_core/renderer_vulkan/present/fxaa.h"
27#include "video_core/renderer_vulkan/present/smaa.h" 20#include "video_core/renderer_vulkan/present/smaa.h"
@@ -29,7 +22,6 @@
29#include "video_core/renderer_vulkan/vk_blit_screen.h" 22#include "video_core/renderer_vulkan/vk_blit_screen.h"
30#include "video_core/renderer_vulkan/vk_scheduler.h" 23#include "video_core/renderer_vulkan/vk_scheduler.h"
31#include "video_core/renderer_vulkan/vk_shader_util.h" 24#include "video_core/renderer_vulkan/vk_shader_util.h"
32#include "video_core/renderer_vulkan/vk_swapchain.h"
33#include "video_core/surface.h" 25#include "video_core/surface.h"
34#include "video_core/textures/decoders.h" 26#include "video_core/textures/decoders.h"
35#include "video_core/vulkan_common/vulkan_device.h" 27#include "video_core/vulkan_common/vulkan_device.h"
@@ -40,48 +32,6 @@ namespace Vulkan {
40 32
41namespace { 33namespace {
42 34
43struct ScreenRectVertex {
44 ScreenRectVertex() = default;
45 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {}
46
47 std::array<f32, 2> position;
48 std::array<f32, 2> tex_coord;
49
50 static VkVertexInputBindingDescription GetDescription() {
51 return {
52 .binding = 0,
53 .stride = sizeof(ScreenRectVertex),
54 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
55 };
56 }
57
58 static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
59 return {{
60 {
61 .location = 0,
62 .binding = 0,
63 .format = VK_FORMAT_R32G32_SFLOAT,
64 .offset = offsetof(ScreenRectVertex, position),
65 },
66 {
67 .location = 1,
68 .binding = 0,
69 .format = VK_FORMAT_R32G32_SFLOAT,
70 .offset = offsetof(ScreenRectVertex, tex_coord),
71 },
72 }};
73 }
74};
75
76std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
77 // clang-format off
78 return { 2.f / width, 0.f, 0.f, 0.f,
79 0.f, 2.f / height, 0.f, 0.f,
80 0.f, 0.f, 1.f, 0.f,
81 -1.f, -1.f, 0.f, 1.f};
82 // clang-format on
83}
84
85u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { 35u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
86 using namespace VideoCore::Surface; 36 using namespace VideoCore::Surface;
87 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); 37 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
@@ -110,43 +60,82 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
110 60
111} // Anonymous namespace 61} // Anonymous namespace
112 62
113struct BlitScreen::BufferData { 63BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
114 struct { 64 MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
115 std::array<f32, 4 * 4> modelview_matrix; 65 Scheduler& scheduler_)
116 } uniform; 66 : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
67 present_manager{present_manager_}, scheduler{scheduler_}, image_count{1},
68 swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
69
70BlitScreen::~BlitScreen() = default;
71
72void BlitScreen::WaitIdle() {
73 present_manager.WaitPresent();
74 scheduler.Finish();
75 device.GetLogical().WaitIdle();
76}
117 77
118 std::array<ScreenRectVertex, 4> vertices; 78void BlitScreen::SetWindowAdaptPass(const Layout::FramebufferLayout& layout) {
79 scaling_filter = Settings::values.scaling_filter.GetValue();
119 80
120 // Unaligned image data goes here 81 const VkExtent2D adapt_size{
121}; 82 .width = layout.screen.GetWidth(),
83 .height = layout.screen.GetHeight(),
84 };
122 85
123BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, 86 fsr.reset();
124 Core::Frontend::EmuWindow& render_window_, const Device& device_,
125 MemoryAllocator& memory_allocator_, Swapchain& swapchain_,
126 PresentManager& present_manager_, Scheduler& scheduler_)
127 : device_memory{device_memory_}, render_window{render_window_}, device{device_},
128 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
129 scheduler{scheduler_}, image_count{swapchain.GetImageCount()} {
130 resource_ticks.resize(image_count);
131 swapchain_view_format = swapchain.GetImageViewFormat();
132 87
133 CreateStaticResources(); 88 switch (scaling_filter) {
134 CreateDynamicResources(); 89 case Settings::ScalingFilter::NearestNeighbor:
90 window_adapt =
91 MakeNearestNeighbor(device, memory_allocator, image_count, swapchain_view_format);
92 break;
93 case Settings::ScalingFilter::Bicubic:
94 window_adapt = MakeBicubic(device, memory_allocator, image_count, swapchain_view_format);
95 break;
96 case Settings::ScalingFilter::Gaussian:
97 window_adapt = MakeGaussian(device, memory_allocator, image_count, swapchain_view_format);
98 break;
99 case Settings::ScalingFilter::ScaleForce:
100 window_adapt = MakeScaleForce(device, memory_allocator, image_count, swapchain_view_format);
101 break;
102 case Settings::ScalingFilter::Fsr:
103 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, adapt_size);
104 [[fallthrough]];
105 case Settings::ScalingFilter::Bilinear:
106 default:
107 window_adapt = MakeBilinear(device, memory_allocator, image_count, swapchain_view_format);
108 break;
109 }
135} 110}
136 111
137BlitScreen::~BlitScreen() = default; 112void BlitScreen::SetAntiAliasPass() {
113 if (anti_alias && anti_aliasing == Settings::values.anti_aliasing.GetValue()) {
114 return;
115 }
138 116
139void BlitScreen::Recreate() { 117 anti_aliasing = Settings::values.anti_aliasing.GetValue();
140 present_manager.WaitPresent(); 118
141 scheduler.Finish(); 119 const VkExtent2D render_area{
142 device.GetLogical().WaitIdle(); 120 .width = Settings::values.resolution_info.ScaleUp(raw_width),
143 CreateDynamicResources(); 121 .height = Settings::values.resolution_info.ScaleUp(raw_height),
122 };
123
124 switch (anti_aliasing) {
125 case Settings::AntiAliasing::Fxaa:
126 anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area);
127 break;
128 case Settings::AntiAliasing::Smaa:
129 anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area);
130 break;
131 default:
132 anti_alias = std::make_unique<NoAA>();
133 break;
134 }
144} 135}
145 136
146void BlitScreen::Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer, 137void BlitScreen::Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer,
147 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout, 138 const Layout::FramebufferLayout& layout, Frame* dst) {
148 VkExtent2D render_area) {
149
150 const auto texture_info = rasterizer.AccelerateDisplay( 139 const auto texture_info = rasterizer.AccelerateDisplay(
151 framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); 140 framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride);
152 const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; 141 const u32 texture_width = texture_info ? texture_info->width : framebuffer.width;
@@ -156,23 +145,19 @@ void BlitScreen::Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConf
156 const bool use_accelerated = texture_info.has_value(); 145 const bool use_accelerated = texture_info.has_value();
157 146
158 RefreshResources(framebuffer); 147 RefreshResources(framebuffer);
148 SetAntiAliasPass();
159 149
160 // Finish any pending renderpass 150 // Finish any pending renderpass
161 scheduler.RequestOutsideRenderPassOperationContext(); 151 scheduler.RequestOutsideRenderPassOperationContext();
162 152
163 scheduler.Wait(resource_ticks[image_index]); 153 scheduler.Wait(resource_ticks[image_index]);
164 resource_ticks[image_index] = scheduler.CurrentTick(); 154 SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); });
165 155
166 VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; 156 VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index];
167 VkImageView source_image_view = 157 VkImageView source_image_view =
168 texture_info ? texture_info->image_view : *raw_image_views[image_index]; 158 texture_info ? texture_info->image_view : *raw_image_views[image_index];
169 159
170 BufferData data;
171 SetUniformData(data, layout);
172 SetVertexData(data, framebuffer, layout, texture_width, texture_height);
173
174 const std::span<u8> mapped_span = buffer.Mapped(); 160 const std::span<u8> mapped_span = buffer.Mapped();
175 std::memcpy(mapped_span.data(), &data, sizeof(data));
176 161
177 if (!use_accelerated) { 162 if (!use_accelerated) {
178 const u64 image_offset = GetRawImageOffset(framebuffer); 163 const u64 image_offset = GetRawImageOffset(framebuffer);
@@ -249,145 +234,109 @@ void BlitScreen::Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConf
249 }); 234 });
250 } 235 }
251 236
252 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 237 source_image_view = anti_alias->Draw(scheduler, image_index, source_image, source_image_view);
253 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { 238
254 if (!fxaa) { 239 const auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
255 const u32 up_scale = Settings::values.resolution_info.up_scale; 240 const VkExtent2D render_extent{
256 const u32 down_shift = Settings::values.resolution_info.down_shift; 241 .width = scaled_width,
257 const VkExtent2D fxaa_size{ 242 .height = scaled_height,
258 .width = (up_scale * framebuffer.width) >> down_shift, 243 };
259 .height = (up_scale * framebuffer.height) >> down_shift,
260 };
261 fxaa = std::make_unique<FXAA>(device, memory_allocator, image_count, fxaa_size);
262 }
263 244
264 source_image_view = fxaa->Draw(scheduler, image_index, source_image, source_image_view);
265 }
266 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) {
267 if (!smaa) {
268 const u32 up_scale = Settings::values.resolution_info.up_scale;
269 const u32 down_shift = Settings::values.resolution_info.down_shift;
270 const VkExtent2D smaa_size{
271 .width = (up_scale * framebuffer.width) >> down_shift,
272 .height = (up_scale * framebuffer.height) >> down_shift,
273 };
274 CreateSMAA(smaa_size);
275 }
276 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
277 }
278 if (fsr) { 245 if (fsr) {
279 const auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); 246 const VkExtent2D adapt_size{
280 const VkExtent2D fsr_input_size{ 247 .width = layout.screen.GetWidth(),
281 .width = scaled_width, 248 .height = layout.screen.GetHeight(),
282 .height = scaled_height,
283 }; 249 };
284 VkImageView fsr_image_view = 250
285 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); 251 source_image_view =
286 UpdateDescriptorSet(fsr_image_view, true); 252 fsr->Draw(scheduler, image_index, source_image_view, render_extent, crop_rect);
253
254 const Common::Rectangle<f32> output_crop{0, 0, 1, 1};
255 window_adapt->Draw(scheduler, image_index, source_image_view, adapt_size, output_crop,
256 layout, dst);
287 } else { 257 } else {
288 const bool is_nn = 258 window_adapt->Draw(scheduler, image_index, source_image_view, render_extent, crop_rect,
289 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; 259 layout, dst);
290 UpdateDescriptorSet(source_image_view, is_nn);
291 } 260 }
261}
292 262
293 scheduler.Record([this, host_framebuffer, index = image_index, 263void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
294 size = render_area](vk::CommandBuffer cmdbuf) { 264 const Tegra::FramebufferConfig& framebuffer,
295 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 265 const Layout::FramebufferLayout& layout, size_t swapchain_images,
296 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 266 VkFormat current_swapchain_view_format) {
297 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 267 bool resource_update_required = false;
298 const VkClearValue clear_color{ 268 bool presentation_recreate_required = false;
299 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
300 };
301 const VkRenderPassBeginInfo renderpass_bi{
302 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
303 .pNext = nullptr,
304 .renderPass = *renderpass,
305 .framebuffer = host_framebuffer,
306 .renderArea =
307 {
308 .offset = {0, 0},
309 .extent = size,
310 },
311 .clearValueCount = 1,
312 .pClearValues = &clear_color,
313 };
314 const VkViewport viewport{
315 .x = 0.0f,
316 .y = 0.0f,
317 .width = static_cast<float>(size.width),
318 .height = static_cast<float>(size.height),
319 .minDepth = 0.0f,
320 .maxDepth = 1.0f,
321 };
322 const VkRect2D scissor{
323 .offset = {0, 0},
324 .extent = size,
325 };
326 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
327 auto graphics_pipeline = [this]() {
328 switch (Settings::values.scaling_filter.GetValue()) {
329 case Settings::ScalingFilter::NearestNeighbor:
330 case Settings::ScalingFilter::Bilinear:
331 return *bilinear_pipeline;
332 case Settings::ScalingFilter::Bicubic:
333 return *bicubic_pipeline;
334 case Settings::ScalingFilter::Gaussian:
335 return *gaussian_pipeline;
336 case Settings::ScalingFilter::ScaleForce:
337 return *scaleforce_pipeline;
338 default:
339 return *bilinear_pipeline;
340 }
341 }();
342 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
343 cmdbuf.SetViewport(0, viewport);
344 cmdbuf.SetScissor(0, scissor);
345 269
346 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 270 // Recreate dynamic resources if the adapting filter changed
347 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 271 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) {
348 descriptor_sets[index], {}); 272 resource_update_required = true;
349 cmdbuf.Draw(4, 1, 0, 0); 273 }
350 cmdbuf.EndRenderPass();
351 });
352}
353 274
354void BlitScreen::DrawToSwapchain(RasterizerVulkan& rasterizer, Frame* frame,
355 const Tegra::FramebufferConfig& framebuffer) {
356 // Recreate dynamic resources if the the image count or input format changed 275 // Recreate dynamic resources if the the image count or input format changed
357 const VkFormat current_framebuffer_format = 276 const VkFormat old_framebuffer_format =
358 std::exchange(framebuffer_view_format, GetFormat(framebuffer)); 277 std::exchange(framebuffer_view_format, GetFormat(framebuffer));
359 if (const std::size_t swapchain_images = swapchain.GetImageCount(); 278 if (swapchain_images != image_count || old_framebuffer_format != framebuffer_view_format) {
360 swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) {
361 image_count = swapchain_images; 279 image_count = swapchain_images;
362 Recreate(); 280 resource_update_required = true;
363 } 281 }
364 282
365 // Recreate the presentation frame if the dimensions of the window changed 283 // Recreate the presentation frame if the format or dimensions of the window changed
366 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 284 const VkFormat old_swapchain_view_format =
367 if (layout.width != frame->width || layout.height != frame->height) { 285 std::exchange(swapchain_view_format, current_swapchain_view_format);
368 Recreate(); 286 if (old_swapchain_view_format != current_swapchain_view_format ||
369 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, 287 layout.width != frame->width || layout.height != frame->height) {
370 *renderpass); 288 resource_update_required = true;
289 presentation_recreate_required = true;
371 } 290 }
372 291
373 const VkExtent2D render_area{frame->width, frame->height}; 292 // If we have a pending resource update, perform it
374 Draw(rasterizer, framebuffer, *frame->framebuffer, layout, render_area); 293 if (resource_update_required) {
294 // Wait for idle to ensure no resources are in use
295 WaitIdle();
296
297 // Set new number of resource ticks
298 resource_ticks.resize(swapchain_images);
299
300 // Update window adapt pass
301 SetWindowAdaptPass(layout);
302
303 // Update frame format if needed
304 if (presentation_recreate_required) {
305 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
306 window_adapt->GetRenderPass());
307 }
308 }
309
310 Draw(rasterizer, framebuffer, layout, frame);
375 if (++image_index >= image_count) { 311 if (++image_index >= image_count) {
376 image_index = 0; 312 image_index = 0;
377 } 313 }
378} 314}
379 315
380vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { 316vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout,
381 return CreateFramebuffer(image_view, extent, renderpass); 317 const VkImageView& image_view,
318 VkFormat current_view_format) {
319 const bool format_updated =
320 std::exchange(swapchain_view_format, current_view_format) != current_view_format;
321 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() ||
322 format_updated) {
323 WaitIdle();
324 SetWindowAdaptPass(layout);
325 }
326 const VkExtent2D extent{
327 .width = layout.width,
328 .height = layout.height,
329 };
330 return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass());
382} 331}
383 332
384vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, 333vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
385 vk::RenderPass& rd) { 334 VkRenderPass render_pass) {
386 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ 335 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
387 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 336 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
388 .pNext = nullptr, 337 .pNext = nullptr,
389 .flags = 0, 338 .flags = 0,
390 .renderPass = *rd, 339 .renderPass = render_pass,
391 .attachmentCount = 1, 340 .attachmentCount = 1,
392 .pAttachments = &image_view, 341 .pAttachments = &image_view,
393 .width = extent.width, 342 .width = extent.width,
@@ -396,35 +345,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE
396 }); 345 });
397} 346}
398 347
399void BlitScreen::CreateStaticResources() {
400 CreateShaders();
401 CreateSampler();
402}
403
404void BlitScreen::CreateDynamicResources() {
405 CreateDescriptorPool();
406 CreateDescriptorSetLayout();
407 CreateDescriptorSets();
408 CreatePipelineLayout();
409 CreateRenderPass();
410 CreateGraphicsPipeline();
411 fsr.reset();
412 fxaa.reset();
413 smaa.reset();
414 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
415 CreateFSR();
416 }
417}
418
419void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { 348void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
420 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
421 if (!fsr) {
422 CreateFSR();
423 }
424 } else {
425 fsr.reset();
426 }
427
428 if (framebuffer.width == raw_width && framebuffer.height == raw_height && 349 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
429 framebuffer.pixel_format == pixel_format && !raw_images.empty()) { 350 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
430 return; 351 return;
@@ -433,486 +354,13 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
433 raw_width = framebuffer.width; 354 raw_width = framebuffer.width;
434 raw_height = framebuffer.height; 355 raw_height = framebuffer.height;
435 pixel_format = framebuffer.pixel_format; 356 pixel_format = framebuffer.pixel_format;
357 anti_alias.reset();
436 358
437 fxaa.reset();
438 smaa.reset();
439 ReleaseRawImages(); 359 ReleaseRawImages();
440
441 CreateStagingBuffer(framebuffer); 360 CreateStagingBuffer(framebuffer);
442 CreateRawImages(framebuffer); 361 CreateRawImages(framebuffer);
443} 362}
444 363
445void BlitScreen::CreateShaders() {
446 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
447 bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV);
448 bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV);
449 gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV);
450 if (device.IsFloat16Supported()) {
451 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV);
452 } else {
453 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV);
454 }
455}
456
457void BlitScreen::CreateDescriptorPool() {
458 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
459 {
460 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
461 .descriptorCount = static_cast<u32>(image_count),
462 },
463 {
464 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
465 .descriptorCount = static_cast<u32>(image_count),
466 },
467 }};
468
469 const VkDescriptorPoolCreateInfo ci{
470 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
471 .pNext = nullptr,
472 .flags = 0,
473 .maxSets = static_cast<u32>(image_count),
474 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
475 .pPoolSizes = pool_sizes.data(),
476 };
477 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
478}
479
480void BlitScreen::CreateRenderPass() {
481 renderpass = CreateRenderPassImpl(swapchain_view_format);
482}
483
484vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
485 const VkAttachmentDescription color_attachment{
486 .flags = 0,
487 .format = format,
488 .samples = VK_SAMPLE_COUNT_1_BIT,
489 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
490 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
491 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
492 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
493 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
494 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
495 };
496
497 const VkAttachmentReference color_attachment_ref{
498 .attachment = 0,
499 .layout = VK_IMAGE_LAYOUT_GENERAL,
500 };
501
502 const VkSubpassDescription subpass_description{
503 .flags = 0,
504 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
505 .inputAttachmentCount = 0,
506 .pInputAttachments = nullptr,
507 .colorAttachmentCount = 1,
508 .pColorAttachments = &color_attachment_ref,
509 .pResolveAttachments = nullptr,
510 .pDepthStencilAttachment = nullptr,
511 .preserveAttachmentCount = 0,
512 .pPreserveAttachments = nullptr,
513 };
514
515 const VkSubpassDependency dependency{
516 .srcSubpass = VK_SUBPASS_EXTERNAL,
517 .dstSubpass = 0,
518 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
519 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
520 .srcAccessMask = 0,
521 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
522 .dependencyFlags = 0,
523 };
524
525 const VkRenderPassCreateInfo renderpass_ci{
526 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
527 .pNext = nullptr,
528 .flags = 0,
529 .attachmentCount = 1,
530 .pAttachments = &color_attachment,
531 .subpassCount = 1,
532 .pSubpasses = &subpass_description,
533 .dependencyCount = 1,
534 .pDependencies = &dependency,
535 };
536
537 return device.GetLogical().CreateRenderPass(renderpass_ci);
538}
539
540void BlitScreen::CreateDescriptorSetLayout() {
541 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
542 {
543 .binding = 0,
544 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
545 .descriptorCount = 1,
546 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
547 .pImmutableSamplers = nullptr,
548 },
549 {
550 .binding = 1,
551 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
552 .descriptorCount = 1,
553 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
554 .pImmutableSamplers = nullptr,
555 },
556 }};
557
558 const VkDescriptorSetLayoutCreateInfo ci{
559 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
560 .pNext = nullptr,
561 .flags = 0,
562 .bindingCount = static_cast<u32>(layout_bindings.size()),
563 .pBindings = layout_bindings.data(),
564 };
565
566 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
567}
568
569void BlitScreen::CreateDescriptorSets() {
570 const std::vector layouts(image_count, *descriptor_set_layout);
571
572 const VkDescriptorSetAllocateInfo ai{
573 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
574 .pNext = nullptr,
575 .descriptorPool = *descriptor_pool,
576 .descriptorSetCount = static_cast<u32>(image_count),
577 .pSetLayouts = layouts.data(),
578 };
579
580 descriptor_sets = descriptor_pool.Allocate(ai);
581}
582
583void BlitScreen::CreatePipelineLayout() {
584 const VkPipelineLayoutCreateInfo ci{
585 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
586 .pNext = nullptr,
587 .flags = 0,
588 .setLayoutCount = 1,
589 .pSetLayouts = descriptor_set_layout.address(),
590 .pushConstantRangeCount = 0,
591 .pPushConstantRanges = nullptr,
592 };
593 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
594}
595
596void BlitScreen::CreateGraphicsPipeline() {
597 const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{
598 {
599 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
600 .pNext = nullptr,
601 .flags = 0,
602 .stage = VK_SHADER_STAGE_VERTEX_BIT,
603 .module = *vertex_shader,
604 .pName = "main",
605 .pSpecializationInfo = nullptr,
606 },
607 {
608 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
609 .pNext = nullptr,
610 .flags = 0,
611 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
612 .module = *bilinear_fragment_shader,
613 .pName = "main",
614 .pSpecializationInfo = nullptr,
615 },
616 }};
617
618 const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{
619 {
620 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
621 .pNext = nullptr,
622 .flags = 0,
623 .stage = VK_SHADER_STAGE_VERTEX_BIT,
624 .module = *vertex_shader,
625 .pName = "main",
626 .pSpecializationInfo = nullptr,
627 },
628 {
629 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
630 .pNext = nullptr,
631 .flags = 0,
632 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
633 .module = *bicubic_fragment_shader,
634 .pName = "main",
635 .pSpecializationInfo = nullptr,
636 },
637 }};
638
639 const std::array<VkPipelineShaderStageCreateInfo, 2> gaussian_shader_stages{{
640 {
641 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
642 .pNext = nullptr,
643 .flags = 0,
644 .stage = VK_SHADER_STAGE_VERTEX_BIT,
645 .module = *vertex_shader,
646 .pName = "main",
647 .pSpecializationInfo = nullptr,
648 },
649 {
650 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
651 .pNext = nullptr,
652 .flags = 0,
653 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
654 .module = *gaussian_fragment_shader,
655 .pName = "main",
656 .pSpecializationInfo = nullptr,
657 },
658 }};
659
660 const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{
661 {
662 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
663 .pNext = nullptr,
664 .flags = 0,
665 .stage = VK_SHADER_STAGE_VERTEX_BIT,
666 .module = *vertex_shader,
667 .pName = "main",
668 .pSpecializationInfo = nullptr,
669 },
670 {
671 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
672 .pNext = nullptr,
673 .flags = 0,
674 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
675 .module = *scaleforce_fragment_shader,
676 .pName = "main",
677 .pSpecializationInfo = nullptr,
678 },
679 }};
680
681 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
682 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
683
684 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
685 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
686 .pNext = nullptr,
687 .flags = 0,
688 .vertexBindingDescriptionCount = 1,
689 .pVertexBindingDescriptions = &vertex_binding_description,
690 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
691 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
692 };
693
694 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
695 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
696 .pNext = nullptr,
697 .flags = 0,
698 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
699 .primitiveRestartEnable = VK_FALSE,
700 };
701
702 const VkPipelineViewportStateCreateInfo viewport_state_ci{
703 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
704 .pNext = nullptr,
705 .flags = 0,
706 .viewportCount = 1,
707 .pViewports = nullptr,
708 .scissorCount = 1,
709 .pScissors = nullptr,
710 };
711
712 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
713 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
714 .pNext = nullptr,
715 .flags = 0,
716 .depthClampEnable = VK_FALSE,
717 .rasterizerDiscardEnable = VK_FALSE,
718 .polygonMode = VK_POLYGON_MODE_FILL,
719 .cullMode = VK_CULL_MODE_NONE,
720 .frontFace = VK_FRONT_FACE_CLOCKWISE,
721 .depthBiasEnable = VK_FALSE,
722 .depthBiasConstantFactor = 0.0f,
723 .depthBiasClamp = 0.0f,
724 .depthBiasSlopeFactor = 0.0f,
725 .lineWidth = 1.0f,
726 };
727
728 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
729 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
730 .pNext = nullptr,
731 .flags = 0,
732 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
733 .sampleShadingEnable = VK_FALSE,
734 .minSampleShading = 0.0f,
735 .pSampleMask = nullptr,
736 .alphaToCoverageEnable = VK_FALSE,
737 .alphaToOneEnable = VK_FALSE,
738 };
739
740 const VkPipelineColorBlendAttachmentState color_blend_attachment{
741 .blendEnable = VK_FALSE,
742 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
743 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
744 .colorBlendOp = VK_BLEND_OP_ADD,
745 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
746 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
747 .alphaBlendOp = VK_BLEND_OP_ADD,
748 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
749 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
750 };
751
752 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
753 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
754 .pNext = nullptr,
755 .flags = 0,
756 .logicOpEnable = VK_FALSE,
757 .logicOp = VK_LOGIC_OP_COPY,
758 .attachmentCount = 1,
759 .pAttachments = &color_blend_attachment,
760 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
761 };
762
763 static constexpr std::array dynamic_states{
764 VK_DYNAMIC_STATE_VIEWPORT,
765 VK_DYNAMIC_STATE_SCISSOR,
766 };
767 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
768 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
769 .pNext = nullptr,
770 .flags = 0,
771 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
772 .pDynamicStates = dynamic_states.data(),
773 };
774
775 const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{
776 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
777 .pNext = nullptr,
778 .flags = 0,
779 .stageCount = static_cast<u32>(bilinear_shader_stages.size()),
780 .pStages = bilinear_shader_stages.data(),
781 .pVertexInputState = &vertex_input_ci,
782 .pInputAssemblyState = &input_assembly_ci,
783 .pTessellationState = nullptr,
784 .pViewportState = &viewport_state_ci,
785 .pRasterizationState = &rasterization_ci,
786 .pMultisampleState = &multisampling_ci,
787 .pDepthStencilState = nullptr,
788 .pColorBlendState = &color_blend_ci,
789 .pDynamicState = &dynamic_state_ci,
790 .layout = *pipeline_layout,
791 .renderPass = *renderpass,
792 .subpass = 0,
793 .basePipelineHandle = 0,
794 .basePipelineIndex = 0,
795 };
796
797 const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{
798 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
799 .pNext = nullptr,
800 .flags = 0,
801 .stageCount = static_cast<u32>(bicubic_shader_stages.size()),
802 .pStages = bicubic_shader_stages.data(),
803 .pVertexInputState = &vertex_input_ci,
804 .pInputAssemblyState = &input_assembly_ci,
805 .pTessellationState = nullptr,
806 .pViewportState = &viewport_state_ci,
807 .pRasterizationState = &rasterization_ci,
808 .pMultisampleState = &multisampling_ci,
809 .pDepthStencilState = nullptr,
810 .pColorBlendState = &color_blend_ci,
811 .pDynamicState = &dynamic_state_ci,
812 .layout = *pipeline_layout,
813 .renderPass = *renderpass,
814 .subpass = 0,
815 .basePipelineHandle = 0,
816 .basePipelineIndex = 0,
817 };
818
819 const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{
820 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
821 .pNext = nullptr,
822 .flags = 0,
823 .stageCount = static_cast<u32>(gaussian_shader_stages.size()),
824 .pStages = gaussian_shader_stages.data(),
825 .pVertexInputState = &vertex_input_ci,
826 .pInputAssemblyState = &input_assembly_ci,
827 .pTessellationState = nullptr,
828 .pViewportState = &viewport_state_ci,
829 .pRasterizationState = &rasterization_ci,
830 .pMultisampleState = &multisampling_ci,
831 .pDepthStencilState = nullptr,
832 .pColorBlendState = &color_blend_ci,
833 .pDynamicState = &dynamic_state_ci,
834 .layout = *pipeline_layout,
835 .renderPass = *renderpass,
836 .subpass = 0,
837 .basePipelineHandle = 0,
838 .basePipelineIndex = 0,
839 };
840
841 const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{
842 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
843 .pNext = nullptr,
844 .flags = 0,
845 .stageCount = static_cast<u32>(scaleforce_shader_stages.size()),
846 .pStages = scaleforce_shader_stages.data(),
847 .pVertexInputState = &vertex_input_ci,
848 .pInputAssemblyState = &input_assembly_ci,
849 .pTessellationState = nullptr,
850 .pViewportState = &viewport_state_ci,
851 .pRasterizationState = &rasterization_ci,
852 .pMultisampleState = &multisampling_ci,
853 .pDepthStencilState = nullptr,
854 .pColorBlendState = &color_blend_ci,
855 .pDynamicState = &dynamic_state_ci,
856 .layout = *pipeline_layout,
857 .renderPass = *renderpass,
858 .subpass = 0,
859 .basePipelineHandle = 0,
860 .basePipelineIndex = 0,
861 };
862
863 bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci);
864 bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci);
865 gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci);
866 scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci);
867}
868
869void BlitScreen::CreateSampler() {
870 const VkSamplerCreateInfo ci{
871 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
872 .pNext = nullptr,
873 .flags = 0,
874 .magFilter = VK_FILTER_LINEAR,
875 .minFilter = VK_FILTER_LINEAR,
876 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
877 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
878 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
879 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
880 .mipLodBias = 0.0f,
881 .anisotropyEnable = VK_FALSE,
882 .maxAnisotropy = 0.0f,
883 .compareEnable = VK_FALSE,
884 .compareOp = VK_COMPARE_OP_NEVER,
885 .minLod = 0.0f,
886 .maxLod = 0.0f,
887 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
888 .unnormalizedCoordinates = VK_FALSE,
889 };
890
891 const VkSamplerCreateInfo ci_nn{
892 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
893 .pNext = nullptr,
894 .flags = 0,
895 .magFilter = VK_FILTER_NEAREST,
896 .minFilter = VK_FILTER_NEAREST,
897 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
898 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
899 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
900 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
901 .mipLodBias = 0.0f,
902 .anisotropyEnable = VK_FALSE,
903 .maxAnisotropy = 0.0f,
904 .compareEnable = VK_FALSE,
905 .compareOp = VK_COMPARE_OP_NEVER,
906 .minLod = 0.0f,
907 .maxLod = 0.0f,
908 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
909 .unnormalizedCoordinates = VK_FALSE,
910 };
911
912 sampler = device.GetLogical().CreateSampler(ci);
913 nn_sampler = device.GetLogical().CreateSampler(ci_nn);
914}
915
916void BlitScreen::ReleaseRawImages() { 364void BlitScreen::ReleaseRawImages() {
917 for (const u64 tick : resource_ticks) { 365 for (const u64 tick : resource_ticks) {
918 scheduler.Wait(tick); 366 scheduler.Wait(tick);
@@ -1000,109 +448,12 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1000 } 448 }
1001} 449}
1002 450
1003void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
1004 const VkDescriptorBufferInfo buffer_info{
1005 .buffer = *buffer,
1006 .offset = offsetof(BufferData, uniform),
1007 .range = sizeof(BufferData::uniform),
1008 };
1009
1010 const VkWriteDescriptorSet ubo_write{
1011 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1012 .pNext = nullptr,
1013 .dstSet = descriptor_sets[image_index],
1014 .dstBinding = 0,
1015 .dstArrayElement = 0,
1016 .descriptorCount = 1,
1017 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1018 .pImageInfo = nullptr,
1019 .pBufferInfo = &buffer_info,
1020 .pTexelBufferView = nullptr,
1021 };
1022
1023 const VkDescriptorImageInfo image_info{
1024 .sampler = nn ? *nn_sampler : *sampler,
1025 .imageView = image_view,
1026 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
1027 };
1028
1029 const VkWriteDescriptorSet sampler_write{
1030 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1031 .pNext = nullptr,
1032 .dstSet = descriptor_sets[image_index],
1033 .dstBinding = 1,
1034 .dstArrayElement = 0,
1035 .descriptorCount = 1,
1036 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1037 .pImageInfo = &image_info,
1038 .pBufferInfo = nullptr,
1039 .pTexelBufferView = nullptr,
1040 };
1041
1042 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
1043}
1044
1045void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
1046 data.uniform.modelview_matrix =
1047 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
1048}
1049
1050void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
1051 const Layout::FramebufferLayout layout, u32 texture_width,
1052 u32 texture_height) const {
1053 f32 left, top, right, bottom;
1054
1055 if (fsr) {
1056 // FSR has already applied the crop, so we just want to render the image
1057 // it has produced.
1058 left = 0;
1059 top = 0;
1060 right = 1;
1061 bottom = 1;
1062 } else {
1063 // Get the normalized crop rectangle.
1064 const auto crop = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
1065
1066 // Apply the crop.
1067 left = crop.left;
1068 top = crop.top;
1069 right = crop.right;
1070 bottom = crop.bottom;
1071 }
1072
1073 // Map the coordinates to the screen.
1074 const auto& screen = layout.screen;
1075 const auto x = static_cast<f32>(screen.left);
1076 const auto y = static_cast<f32>(screen.top);
1077 const auto w = static_cast<f32>(screen.GetWidth());
1078 const auto h = static_cast<f32>(screen.GetHeight());
1079
1080 data.vertices[0] = ScreenRectVertex(x, y, left, top);
1081 data.vertices[1] = ScreenRectVertex(x + w, y, right, top);
1082 data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
1083 data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
1084}
1085
1086void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {
1087 smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size);
1088}
1089
1090void BlitScreen::CreateFSR() {
1091 const auto& layout = render_window.GetFramebufferLayout();
1092 const VkExtent2D fsr_size{
1093 .width = layout.screen.GetWidth(),
1094 .height = layout.screen.GetHeight(),
1095 };
1096 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, fsr_size);
1097}
1098
1099u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { 451u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const {
1100 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; 452 return GetSizeInBytes(framebuffer) * image_count;
1101} 453}
1102 454
1103u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const { 455u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
1104 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); 456 return GetSizeInBytes(framebuffer) * image_index;
1105 return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
1106} 457}
1107 458
1108} // namespace Vulkan 459} // 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 d7f8effa2..555b3d82e 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -30,16 +30,20 @@ namespace Service::android {
30enum class PixelFormat : u32; 30enum class PixelFormat : u32;
31} 31}
32 32
33namespace Settings {
34enum class AntiAliasing : u32;
35enum class ScalingFilter : u32;
36} // namespace Settings
37
33namespace Vulkan { 38namespace Vulkan {
34 39
40class AntiAliasPass;
35class Device; 41class Device;
36class FSR; 42class FSR;
37class FXAA;
38class RasterizerVulkan; 43class RasterizerVulkan;
39class Scheduler; 44class Scheduler;
40class SMAA;
41class Swapchain;
42class PresentManager; 45class PresentManager;
46class WindowAdaptPass;
43 47
44struct Frame; 48struct Frame;
45 49
@@ -54,103 +58,66 @@ struct FramebufferTextureInfo {
54 58
55class BlitScreen { 59class BlitScreen {
56public: 60public:
57 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, 61 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
58 Core::Frontend::EmuWindow& render_window, const Device& device, 62 MemoryAllocator& memory_allocator, PresentManager& present_manager,
59 MemoryAllocator& memory_manager, Swapchain& swapchain, 63 Scheduler& scheduler);
60 PresentManager& present_manager, Scheduler& scheduler);
61 ~BlitScreen(); 64 ~BlitScreen();
62 65
63 void Recreate(); 66 void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
64 67 const Tegra::FramebufferConfig& framebuffer,
65 void Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer, 68 const Layout::FramebufferLayout& layout, size_t swapchain_images,
66 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout, 69 VkFormat current_swapchain_view_format);
67 VkExtent2D render_area);
68
69 void DrawToSwapchain(RasterizerVulkan& rasterizer, Frame* frame,
70 const Tegra::FramebufferConfig& framebuffer);
71
72 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
73 VkExtent2D extent);
74 70
75 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 71 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout,
76 VkExtent2D extent, vk::RenderPass& rd); 72 const VkImageView& image_view,
73 VkFormat current_view_format);
77 74
78private: 75private:
79 struct BufferData; 76 void WaitIdle();
77 void SetWindowAdaptPass(const Layout::FramebufferLayout& layout);
78 void SetAntiAliasPass();
80 79
81 void CreateStaticResources(); 80 void Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer,
82 void CreateShaders(); 81 const Layout::FramebufferLayout& layout, Frame* dst);
83 void CreateDescriptorPool();
84 void CreateRenderPass();
85 vk::RenderPass CreateRenderPassImpl(VkFormat format);
86 void CreateDescriptorSetLayout();
87 void CreateDescriptorSets();
88 void CreatePipelineLayout();
89 void CreateGraphicsPipeline();
90 void CreateSampler();
91 82
92 void CreateDynamicResources(); 83 vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
84 VkRenderPass render_pass);
93 85
94 void RefreshResources(const Tegra::FramebufferConfig& framebuffer); 86 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
95 void ReleaseRawImages(); 87 void ReleaseRawImages();
96 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); 88 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
97 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); 89 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
98 90
99 void UpdateDescriptorSet(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, u32 texture_width,
103 u32 texture_height) const;
104
105 void CreateSMAA(VkExtent2D smaa_size);
106 void CreateFSR();
107
108 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 91 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
109 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const; 92 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
110 93
111 Tegra::MaxwellDeviceMemoryManager& device_memory; 94 Tegra::MaxwellDeviceMemoryManager& device_memory;
112 Core::Frontend::EmuWindow& render_window;
113 const Device& device; 95 const Device& device;
114 MemoryAllocator& memory_allocator; 96 MemoryAllocator& memory_allocator;
115 Swapchain& swapchain;
116 PresentManager& present_manager; 97 PresentManager& present_manager;
117 Scheduler& scheduler; 98 Scheduler& scheduler;
118 std::size_t image_count; 99 std::size_t image_count;
119 std::size_t image_index{}; 100 std::size_t image_index{};
120 101
121 vk::ShaderModule vertex_shader;
122 vk::ShaderModule bilinear_fragment_shader;
123 vk::ShaderModule bicubic_fragment_shader;
124 vk::ShaderModule gaussian_fragment_shader;
125 vk::ShaderModule scaleforce_fragment_shader;
126 vk::DescriptorPool descriptor_pool;
127 vk::DescriptorSetLayout descriptor_set_layout;
128 vk::PipelineLayout pipeline_layout;
129 vk::Pipeline bilinear_pipeline;
130 vk::Pipeline bicubic_pipeline;
131 vk::Pipeline gaussian_pipeline;
132 vk::Pipeline scaleforce_pipeline;
133 vk::RenderPass renderpass;
134 vk::DescriptorSets descriptor_sets;
135 vk::Sampler nn_sampler;
136 vk::Sampler sampler;
137
138 vk::Buffer buffer; 102 vk::Buffer buffer;
139 103
140 std::vector<u64> resource_ticks; 104 std::vector<u64> resource_ticks;
141 105
142 std::vector<vk::Image> raw_images; 106 std::vector<vk::Image> raw_images;
143 std::vector<vk::ImageView> raw_image_views; 107 std::vector<vk::ImageView> raw_image_views;
144
145 u32 raw_width = 0; 108 u32 raw_width = 0;
146 u32 raw_height = 0; 109 u32 raw_height = 0;
110
147 Service::android::PixelFormat pixel_format{}; 111 Service::android::PixelFormat pixel_format{};
148 VkFormat framebuffer_view_format; 112 VkFormat framebuffer_view_format;
149 VkFormat swapchain_view_format; 113 VkFormat swapchain_view_format;
150 114
115 Settings::AntiAliasing anti_aliasing{};
116 Settings::ScalingFilter scaling_filter{};
117
151 std::unique_ptr<FSR> fsr; 118 std::unique_ptr<FSR> fsr;
152 std::unique_ptr<SMAA> smaa; 119 std::unique_ptr<AntiAliasPass> anti_alias;
153 std::unique_ptr<FXAA> fxaa; 120 std::unique_ptr<WindowAdaptPass> window_adapt;
154}; 121};
155 122
156} // namespace Vulkan 123} // namespace Vulkan