summaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/CMakeLists.txt39
-rw-r--r--src/video_core/engines/maxwell_dma.h1
-rw-r--r--src/video_core/engines/sw_blitter/blitter.cpp37
-rw-r--r--src/video_core/framebuffer_config.cpp55
-rw-r--r--src/video_core/framebuffer_config.h6
-rw-r--r--src/video_core/gpu.cpp29
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/gpu_thread.cpp6
-rw-r--r--src/video_core/gpu_thread.h15
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt11
-rw-r--r--src/video_core/host_shaders/fidelityfx_fsr.frag (renamed from src/video_core/host_shaders/fidelityfx_fsr.comp)33
-rw-r--r--src/video_core/host_shaders/fxaa.vert4
-rw-r--r--src/video_core/host_shaders/opengl_present_scaleforce.frag12
-rw-r--r--src/video_core/host_shaders/present_bicubic.frag12
-rw-r--r--src/video_core/host_shaders/present_gaussian.frag12
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert13
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_present.frag2
-rw-r--r--src/video_core/host_shaders/vulkan_present.vert33
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag2
-rw-r--r--src/video_core/rasterizer_interface.h6
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h2
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp4
-rw-r--r--src/video_core/renderer_null/renderer_null.h2
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.cpp96
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.h71
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h14
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/present/filters.cpp39
-rw-r--r--src/video_core/renderer_opengl/present/filters.h17
-rw-r--r--src/video_core/renderer_opengl/present/fsr.cpp98
-rw-r--r--src/video_core/renderer_opengl/present/fsr.h39
-rw-r--r--src/video_core/renderer_opengl/present/fxaa.cpp41
-rw-r--r--src/video_core/renderer_opengl/present/fxaa.h27
-rw-r--r--src/video_core/renderer_opengl/present/layer.cpp215
-rw-r--r--src/video_core/renderer_opengl/present/layer.h80
-rw-r--r--src/video_core/renderer_opengl/present/present_uniforms.h43
-rw-r--r--src/video_core/renderer_opengl/present/smaa.cpp102
-rw-r--r--src/video_core/renderer_opengl/present/smaa.h35
-rw-r--r--src/video_core/renderer_opengl/present/util.h43
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.cpp103
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.h47
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp568
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h93
-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_pipeline_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h14
-rw-r--r--src/video_core/texture_cache/texture_cache.h9
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp2
82 files changed, 3331 insertions, 3460 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 0755ba772..16c905db9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -55,6 +55,7 @@ add_library(video_core STATIC
55 engines/maxwell_dma.h 55 engines/maxwell_dma.h
56 engines/puller.cpp 56 engines/puller.cpp
57 engines/puller.h 57 engines/puller.h
58 framebuffer_config.cpp
58 framebuffer_config.h 59 framebuffer_config.h
59 fsr.cpp 60 fsr.cpp
60 fsr.h 61 fsr.h
@@ -115,8 +116,24 @@ add_library(video_core STATIC
115 renderer_null/null_rasterizer.h 116 renderer_null/null_rasterizer.h
116 renderer_null/renderer_null.cpp 117 renderer_null/renderer_null.cpp
117 renderer_null/renderer_null.h 118 renderer_null/renderer_null.h
119 renderer_opengl/present/filters.cpp
120 renderer_opengl/present/filters.h
121 renderer_opengl/present/fsr.cpp
122 renderer_opengl/present/fsr.h
123 renderer_opengl/present/fxaa.cpp
124 renderer_opengl/present/fxaa.h
125 renderer_opengl/present/layer.cpp
126 renderer_opengl/present/layer.h
127 renderer_opengl/present/present_uniforms.h
128 renderer_opengl/present/smaa.cpp
129 renderer_opengl/present/smaa.h
130 renderer_opengl/present/util.h
131 renderer_opengl/present/window_adapt_pass.cpp
132 renderer_opengl/present/window_adapt_pass.h
118 renderer_opengl/blit_image.cpp 133 renderer_opengl/blit_image.cpp
119 renderer_opengl/blit_image.h 134 renderer_opengl/blit_image.h
135 renderer_opengl/gl_blit_screen.cpp
136 renderer_opengl/gl_blit_screen.h
120 renderer_opengl/gl_buffer_cache_base.cpp 137 renderer_opengl/gl_buffer_cache_base.cpp
121 renderer_opengl/gl_buffer_cache.cpp 138 renderer_opengl/gl_buffer_cache.cpp
122 renderer_opengl/gl_buffer_cache.h 139 renderer_opengl/gl_buffer_cache.h
@@ -126,8 +143,6 @@ add_library(video_core STATIC
126 renderer_opengl/gl_device.h 143 renderer_opengl/gl_device.h
127 renderer_opengl/gl_fence_manager.cpp 144 renderer_opengl/gl_fence_manager.cpp
128 renderer_opengl/gl_fence_manager.h 145 renderer_opengl/gl_fence_manager.h
129 renderer_opengl/gl_fsr.cpp
130 renderer_opengl/gl_fsr.h
131 renderer_opengl/gl_graphics_pipeline.cpp 146 renderer_opengl/gl_graphics_pipeline.cpp
132 renderer_opengl/gl_graphics_pipeline.h 147 renderer_opengl/gl_graphics_pipeline.h
133 renderer_opengl/gl_rasterizer.cpp 148 renderer_opengl/gl_rasterizer.cpp
@@ -155,6 +170,22 @@ add_library(video_core STATIC
155 renderer_opengl/renderer_opengl.h 170 renderer_opengl/renderer_opengl.h
156 renderer_opengl/util_shaders.cpp 171 renderer_opengl/util_shaders.cpp
157 renderer_opengl/util_shaders.h 172 renderer_opengl/util_shaders.h
173 renderer_vulkan/present/anti_alias_pass.h
174 renderer_vulkan/present/filters.cpp
175 renderer_vulkan/present/filters.h
176 renderer_vulkan/present/fsr.cpp
177 renderer_vulkan/present/fsr.h
178 renderer_vulkan/present/fxaa.cpp
179 renderer_vulkan/present/fxaa.h
180 renderer_vulkan/present/layer.cpp
181 renderer_vulkan/present/layer.h
182 renderer_vulkan/present/present_push_constants.h
183 renderer_vulkan/present/smaa.cpp
184 renderer_vulkan/present/smaa.h
185 renderer_vulkan/present/util.cpp
186 renderer_vulkan/present/util.h
187 renderer_vulkan/present/window_adapt_pass.cpp
188 renderer_vulkan/present/window_adapt_pass.h
158 renderer_vulkan/blit_image.cpp 189 renderer_vulkan/blit_image.cpp
159 renderer_vulkan/blit_image.h 190 renderer_vulkan/blit_image.h
160 renderer_vulkan/fixed_pipeline_state.cpp 191 renderer_vulkan/fixed_pipeline_state.cpp
@@ -181,8 +212,6 @@ add_library(video_core STATIC
181 renderer_vulkan/vk_descriptor_pool.h 212 renderer_vulkan/vk_descriptor_pool.h
182 renderer_vulkan/vk_fence_manager.cpp 213 renderer_vulkan/vk_fence_manager.cpp
183 renderer_vulkan/vk_fence_manager.h 214 renderer_vulkan/vk_fence_manager.h
184 renderer_vulkan/vk_fsr.cpp
185 renderer_vulkan/vk_fsr.h
186 renderer_vulkan/vk_graphics_pipeline.cpp 215 renderer_vulkan/vk_graphics_pipeline.cpp
187 renderer_vulkan/vk_graphics_pipeline.h 216 renderer_vulkan/vk_graphics_pipeline.h
188 renderer_vulkan/vk_master_semaphore.cpp 217 renderer_vulkan/vk_master_semaphore.cpp
@@ -203,8 +232,6 @@ add_library(video_core STATIC
203 renderer_vulkan/vk_scheduler.h 232 renderer_vulkan/vk_scheduler.h
204 renderer_vulkan/vk_shader_util.cpp 233 renderer_vulkan/vk_shader_util.cpp
205 renderer_vulkan/vk_shader_util.h 234 renderer_vulkan/vk_shader_util.h
206 renderer_vulkan/vk_smaa.cpp
207 renderer_vulkan/vk_smaa.h
208 renderer_vulkan/vk_staging_buffer_pool.cpp 235 renderer_vulkan/vk_staging_buffer_pool.cpp
209 renderer_vulkan/vk_staging_buffer_pool.h 236 renderer_vulkan/vk_staging_buffer_pool.h
210 renderer_vulkan/vk_state_tracker.cpp 237 renderer_vulkan/vk_state_tracker.cpp
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 1a43e24b6..99341e431 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "common/scratch_buffer.h" 13#include "common/scratch_buffer.h"
13#include "video_core/engines/engine_interface.h" 14#include "video_core/engines/engine_interface.h"
diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp
index 4bc079024..8bcc2f7a7 100644
--- a/src/video_core/engines/sw_blitter/blitter.cpp
+++ b/src/video_core/engines/sw_blitter/blitter.cpp
@@ -111,6 +111,20 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt
111 } 111 }
112} 112}
113 113
114template <bool unpack>
115void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t extent_x,
116 size_t extent_y, u32 pitch, u32 x0, u32 y0, size_t bpp) {
117 const size_t base_offset = x0 * bpp;
118 const size_t copy_size = extent_x * bpp;
119 for (size_t y = 0; y < extent_y; y++) {
120 const size_t first_offset = (y + y0) * pitch + base_offset;
121 const size_t second_offset = y * extent_x * bpp;
122 u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
123 const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
124 std::memcpy(write_to, read_from, copy_size);
125 }
126}
127
114} // namespace 128} // namespace
115 129
116struct SoftwareBlitEngine::BlitEngineImpl { 130struct SoftwareBlitEngine::BlitEngineImpl {
@@ -138,19 +152,6 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
138 } 152 }
139 return static_cast<size_t>(surface.pitch * surface.height); 153 return static_cast<size_t>(surface.pitch * surface.height);
140 }; 154 };
141 const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
142 std::span<u8> output, u32 extent_x, u32 extent_y,
143 u32 pitch, u32 x0, u32 y0, size_t bpp) {
144 const size_t base_offset = x0 * bpp;
145 const size_t copy_size = extent_x * bpp;
146 for (u32 y = y0; y < extent_y; y++) {
147 const size_t first_offset = y * pitch + base_offset;
148 const size_t second_offset = y * extent_x * bpp;
149 u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
150 const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
151 std::memcpy(write_to, read_from, copy_size);
152 }
153 };
154 155
155 const u32 src_extent_x = config.src_x1 - config.src_x0; 156 const u32 src_extent_x = config.src_x1 - config.src_x0;
156 const u32 src_extent_y = config.src_y1 - config.src_y0; 157 const u32 src_extent_y = config.src_y1 - config.src_y0;
@@ -205,8 +206,8 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
205 src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, 206 src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y,
206 src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); 207 src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel);
207 } else { 208 } else {
208 process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, 209 ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
209 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); 210 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
210 } 211 }
211 212
212 // Conversion Phase 213 // Conversion Phase
@@ -229,9 +230,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
229 dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, 230 dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y,
230 dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); 231 dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel);
231 } else { 232 } else {
232 process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, 233 ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
233 dst.pitch, config.dst_x0, config.dst_y0, 234 dst.pitch, config.dst_x0, config.dst_y0,
234 static_cast<size_t>(dst_bytes_per_pixel)); 235 static_cast<size_t>(dst_bytes_per_pixel));
235 } 236 }
236 return true; 237 return true;
237} 238}
diff --git a/src/video_core/framebuffer_config.cpp b/src/video_core/framebuffer_config.cpp
new file mode 100644
index 000000000..e28d41f84
--- /dev/null
+++ b/src/video_core/framebuffer_config.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "video_core/framebuffer_config.h"
6
7namespace Tegra {
8
9Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
10 u32 texture_height) {
11 f32 left, top, right, bottom;
12
13 if (!framebuffer.crop_rect.IsEmpty()) {
14 // If crop rectangle is not empty, apply properties from rectangle.
15 left = static_cast<f32>(framebuffer.crop_rect.left);
16 top = static_cast<f32>(framebuffer.crop_rect.top);
17 right = static_cast<f32>(framebuffer.crop_rect.right);
18 bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
19 } else {
20 // Otherwise, fall back to framebuffer dimensions.
21 left = 0;
22 top = 0;
23 right = static_cast<f32>(framebuffer.width);
24 bottom = static_cast<f32>(framebuffer.height);
25 }
26
27 // Apply transformation flags.
28 auto framebuffer_transform_flags = framebuffer.transform_flags;
29
30 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) {
31 // Switch left and right.
32 std::swap(left, right);
33 }
34 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
35 // Switch top and bottom.
36 std::swap(top, bottom);
37 }
38
39 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH;
40 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV;
41 if (True(framebuffer_transform_flags)) {
42 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
43 static_cast<u32>(framebuffer_transform_flags));
44 }
45
46 // Normalize coordinate space.
47 left /= static_cast<f32>(texture_width);
48 top /= static_cast<f32>(texture_height);
49 right /= static_cast<f32>(texture_width);
50 bottom /= static_cast<f32>(texture_height);
51
52 return Common::Rectangle<f32>(left, top, right, bottom);
53}
54
55} // namespace Tegra
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index 856f4bd52..6a18b76fb 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -7,6 +7,7 @@
7#include "common/math_util.h" 7#include "common/math_util.h"
8#include "core/hle/service/nvnflinger/buffer_transform_flags.h" 8#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
9#include "core/hle/service/nvnflinger/pixel_format.h" 9#include "core/hle/service/nvnflinger/pixel_format.h"
10#include "core/hle/service/nvnflinger/ui/fence.h"
10 11
11namespace Tegra { 12namespace Tegra {
12 13
@@ -21,7 +22,10 @@ struct FramebufferConfig {
21 u32 stride{}; 22 u32 stride{};
22 Service::android::PixelFormat pixel_format{}; 23 Service::android::PixelFormat pixel_format{};
23 Service::android::BufferTransformFlags transform_flags{}; 24 Service::android::BufferTransformFlags transform_flags{};
24 Common::Rectangle<int> crop_rect; 25 Common::Rectangle<int> crop_rect{};
25}; 26};
26 27
28Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
29 u32 texture_height);
30
27} // namespace Tegra 31} // namespace Tegra
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 609704b33..f4a5d831c 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -274,11 +274,6 @@ struct GPU::Impl {
274 } 274 }
275 } 275 }
276 276
277 /// Swap buffers (render frame)
278 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
279 gpu_thread.SwapBuffers(framebuffer);
280 }
281
282 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 277 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
283 void FlushRegion(DAddr addr, u64 size) { 278 void FlushRegion(DAddr addr, u64 size) {
284 gpu_thread.FlushRegion(addr, size); 279 gpu_thread.FlushRegion(addr, size);
@@ -313,8 +308,9 @@ struct GPU::Impl {
313 gpu_thread.FlushAndInvalidateRegion(addr, size); 308 gpu_thread.FlushAndInvalidateRegion(addr, size);
314 } 309 }
315 310
316 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 311 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
317 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { 312 std::vector<Service::Nvidia::NvFence>&& fences) {
313 size_t num_fences{fences.size()};
318 size_t current_request_counter{}; 314 size_t current_request_counter{};
319 { 315 {
320 std::unique_lock<std::mutex> lk(request_swap_mutex); 316 std::unique_lock<std::mutex> lk(request_swap_mutex);
@@ -328,13 +324,12 @@ struct GPU::Impl {
328 } 324 }
329 } 325 }
330 const auto wait_fence = 326 const auto wait_fence =
331 RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { 327 RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
332 auto& syncpoint_manager = host1x.GetSyncpointManager(); 328 auto& syncpoint_manager = host1x.GetSyncpointManager();
333 if (num_fences == 0) { 329 if (num_fences == 0) {
334 renderer->SwapBuffers(framebuffer); 330 renderer->Composite(layers);
335 } 331 }
336 const auto executer = [this, current_request_counter, 332 const auto executer = [this, current_request_counter, layers_copy = layers]() {
337 framebuffer_copy = *framebuffer]() {
338 { 333 {
339 std::unique_lock<std::mutex> lk(request_swap_mutex); 334 std::unique_lock<std::mutex> lk(request_swap_mutex);
340 if (--request_swap_counters[current_request_counter] != 0) { 335 if (--request_swap_counters[current_request_counter] != 0) {
@@ -342,7 +337,7 @@ struct GPU::Impl {
342 } 337 }
343 free_swap_counters.push_back(current_request_counter); 338 free_swap_counters.push_back(current_request_counter);
344 } 339 }
345 renderer->SwapBuffers(&framebuffer_copy); 340 renderer->Composite(layers_copy);
346 }; 341 };
347 for (size_t i = 0; i < num_fences; i++) { 342 for (size_t i = 0; i < num_fences; i++) {
348 syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); 343 syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
@@ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
505 return impl->ShaderNotify(); 500 return impl->ShaderNotify();
506} 501}
507 502
508void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 503void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
509 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { 504 std::vector<Service::Nvidia::NvFence>&& fences) {
510 impl->RequestSwapBuffers(framebuffer, fences, num_fences); 505 impl->RequestComposite(std::move(layers), std::move(fences));
511} 506}
512 507
513u64 GPU::GetTicks() const { 508u64 GPU::GetTicks() const {
@@ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) {
554 impl->ClearCdmaInstance(id); 549 impl->ClearCdmaInstance(id);
555} 550}
556 551
557void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
558 impl->SwapBuffers(framebuffer);
559}
560
561VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { 552VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) {
562 return impl->OnCPURead(addr, size); 553 return impl->OnCPURead(addr, size);
563} 554}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b3c1d15bd..c4602ca37 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -212,8 +212,8 @@ public:
212 212
213 void RendererFrameEndNotify(); 213 void RendererFrameEndNotify();
214 214
215 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 215 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
216 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); 216 std::vector<Service::Nvidia::NvFence>&& fences);
217 217
218 /// Performs any additional setup necessary in order to begin GPU emulation. 218 /// Performs any additional setup necessary in order to begin GPU emulation.
219 /// This can be used to launch any necessary threads and register any necessary 219 /// This can be used to launch any necessary threads and register any necessary
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 788d4f61e..58d8110b8 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
40 } 40 }
41 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { 41 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
42 scheduler.Push(submit_list->channel, std::move(submit_list->entries)); 42 scheduler.Push(submit_list->channel, std::move(submit_list->entries));
43 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
44 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
45 } else if (std::holds_alternative<GPUTickCommand>(next.data)) { 43 } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
46 system.GPU().TickWork(); 44 system.GPU().TickWork();
47 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { 45 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@@ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
78 PushCommand(SubmitListCommand(channel, std::move(entries))); 76 PushCommand(SubmitListCommand(channel, std::move(entries)));
79} 77}
80 78
81void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
82 PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
83}
84
85void ThreadManager::FlushRegion(DAddr addr, u64 size) { 79void ThreadManager::FlushRegion(DAddr addr, u64 size) {
86 if (!is_async) { 80 if (!is_async) {
87 // Always flush with synchronous GPU mode 81 // Always flush with synchronous GPU mode
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 2de25e9ef..dc0fce9f8 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -44,14 +44,6 @@ struct SubmitListCommand final {
44 Tegra::CommandList entries; 44 Tegra::CommandList entries;
45}; 45};
46 46
47/// Command to signal to the GPU thread that a swap buffers is pending
48struct SwapBuffersCommand final {
49 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_)
50 : framebuffer{std::move(framebuffer_)} {}
51
52 std::optional<Tegra::FramebufferConfig> framebuffer;
53};
54
55/// Command to signal to the GPU thread to flush a region 47/// Command to signal to the GPU thread to flush a region
56struct FlushRegionCommand final { 48struct FlushRegionCommand final {
57 explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} 49 explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}
@@ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final {
81struct GPUTickCommand final {}; 73struct GPUTickCommand final {};
82 74
83using CommandData = 75using CommandData =
84 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 76 std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand,
85 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; 77 FlushAndInvalidateRegionCommand, GPUTickCommand>;
86 78
87struct CommandDataContainer { 79struct CommandDataContainer {
88 CommandDataContainer() = default; 80 CommandDataContainer() = default;
@@ -118,9 +110,6 @@ public:
118 /// Push GPU command entries to be processed 110 /// Push GPU command entries to be processed
119 void SubmitList(s32 channel, Tegra::CommandList&& entries); 111 void SubmitList(s32 channel, Tegra::CommandList&& entries);
120 112
121 /// Swap buffers (render frame)
122 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
123
124 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 113 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
125 void FlushRegion(DAddr addr, u64 size); 114 void FlushRegion(DAddr addr, u64 size);
126 115
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index cd2549232..969f21d50 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -9,7 +9,7 @@ set(FIDELITYFX_FILES
9) 9)
10 10
11set(GLSL_INCLUDES 11set(GLSL_INCLUDES
12 fidelityfx_fsr.comp 12 fidelityfx_fsr.frag
13 ${FIDELITYFX_FILES} 13 ${FIDELITYFX_FILES}
14) 14)
15 15
@@ -56,10 +56,11 @@ set(SHADER_FILES
56 vulkan_color_clear.frag 56 vulkan_color_clear.frag
57 vulkan_color_clear.vert 57 vulkan_color_clear.vert
58 vulkan_depthstencil_clear.frag 58 vulkan_depthstencil_clear.frag
59 vulkan_fidelityfx_fsr_easu_fp16.comp 59 vulkan_fidelityfx_fsr.vert
60 vulkan_fidelityfx_fsr_easu_fp32.comp 60 vulkan_fidelityfx_fsr_easu_fp16.frag
61 vulkan_fidelityfx_fsr_rcas_fp16.comp 61 vulkan_fidelityfx_fsr_easu_fp32.frag
62 vulkan_fidelityfx_fsr_rcas_fp32.comp 62 vulkan_fidelityfx_fsr_rcas_fp16.frag
63 vulkan_fidelityfx_fsr_rcas_fp32.frag
63 vulkan_present.frag 64 vulkan_present.frag
64 vulkan_present.vert 65 vulkan_present.vert
65 vulkan_present_scaleforce_fp16.frag 66 vulkan_present_scaleforce_fp16.frag
diff --git a/src/video_core/host_shaders/fidelityfx_fsr.comp b/src/video_core/host_shaders/fidelityfx_fsr.frag
index f91b1aa9f..a266e1c4e 100644
--- a/src/video_core/host_shaders/fidelityfx_fsr.comp
+++ b/src/video_core/host_shaders/fidelityfx_fsr.frag
@@ -34,7 +34,6 @@ layout( push_constant ) uniform constants {
34}; 34};
35 35
36layout(set=0,binding=0) uniform sampler2D InputTexture; 36layout(set=0,binding=0) uniform sampler2D InputTexture;
37layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture;
38 37
39#define A_GPU 1 38#define A_GPU 1
40#define A_GLSL 1 39#define A_GLSL 1
@@ -72,44 +71,40 @@ layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture;
72 71
73#include "ffx_fsr1.h" 72#include "ffx_fsr1.h"
74 73
75void CurrFilter(AU2 pos) { 74#if USE_RCAS
76#if USE_BILINEAR 75 layout(location = 0) in vec2 frag_texcoord;
77 AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw);
78 imageStore(OutputTexture, ASU2(pos), textureLod(InputTexture, pp, 0.0));
79#endif 76#endif
77layout (location = 0) out vec4 frag_color;
78
79void CurrFilter(AU2 pos) {
80#if USE_EASU 80#if USE_EASU
81 #ifndef YUZU_USE_FP16 81 #ifndef YUZU_USE_FP16
82 AF3 c; 82 AF3 c;
83 FsrEasuF(c, pos, Const0, Const1, Const2, Const3); 83 FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
84 imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); 84 frag_color = AF4(c, 1.0);
85 #else 85 #else
86 AH3 c; 86 AH3 c;
87 FsrEasuH(c, pos, Const0, Const1, Const2, Const3); 87 FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
88 imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); 88 frag_color = AH4(c, 1.0);
89 #endif 89 #endif
90#endif 90#endif
91#if USE_RCAS 91#if USE_RCAS
92 #ifndef YUZU_USE_FP16 92 #ifndef YUZU_USE_FP16
93 AF3 c; 93 AF3 c;
94 FsrRcasF(c.r, c.g, c.b, pos, Const0); 94 FsrRcasF(c.r, c.g, c.b, pos, Const0);
95 imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); 95 frag_color = AF4(c, 1.0);
96 #else 96 #else
97 AH3 c; 97 AH3 c;
98 FsrRcasH(c.r, c.g, c.b, pos, Const0); 98 FsrRcasH(c.r, c.g, c.b, pos, Const0);
99 imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); 99 frag_color = AH4(c, 1.0);
100 #endif 100 #endif
101#endif 101#endif
102} 102}
103 103
104layout(local_size_x=64) in;
105void main() { 104void main() {
106 // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. 105#if USE_RCAS
107 AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); 106 CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
108 CurrFilter(gxy); 107#else
109 gxy.x += 8u; 108 CurrFilter(AU2(gl_FragCoord.xy));
110 CurrFilter(gxy); 109#endif
111 gxy.y += 8u;
112 CurrFilter(gxy);
113 gxy.x -= 8u;
114 CurrFilter(gxy);
115} 110}
diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert
index c2717d90d..223ab785e 100644
--- a/src/video_core/host_shaders/fxaa.vert
+++ b/src/video_core/host_shaders/fxaa.vert
@@ -7,8 +7,8 @@ out gl_PerVertex {
7 vec4 gl_Position; 7 vec4 gl_Position;
8}; 8};
9 9
10const vec2 vertices[4] = 10const vec2 vertices[3] =
11 vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)); 11 vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3));
12 12
13layout (location = 0) out vec4 posPos; 13layout (location = 0) out vec4 posPos;
14 14
diff --git a/src/video_core/host_shaders/opengl_present_scaleforce.frag b/src/video_core/host_shaders/opengl_present_scaleforce.frag
index a780373e3..1598575a1 100644
--- a/src/video_core/host_shaders/opengl_present_scaleforce.frag
+++ b/src/video_core/host_shaders/opengl_present_scaleforce.frag
@@ -26,21 +26,11 @@
26 26
27#endif 27#endif
28 28
29#ifdef VULKAN
30
31#define BINDING_COLOR_TEXTURE 1
32
33#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
34
35#define BINDING_COLOR_TEXTURE 0
36
37#endif
38
39layout (location = 0) in vec2 tex_coord; 29layout (location = 0) in vec2 tex_coord;
40 30
41layout (location = 0) out vec4 frag_color; 31layout (location = 0) out vec4 frag_color;
42 32
43layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; 33layout (binding = 0) uniform sampler2D input_texture;
44 34
45const bool ignore_alpha = true; 35const bool ignore_alpha = true;
46 36
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag
index c57dd2851..c814629cf 100644
--- a/src/video_core/host_shaders/present_bicubic.frag
+++ b/src/video_core/host_shaders/present_bicubic.frag
@@ -3,22 +3,12 @@
3 3
4#version 460 core 4#version 460 core
5 5
6#ifdef VULKAN
7
8#define BINDING_COLOR_TEXTURE 1
9
10#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
11
12#define BINDING_COLOR_TEXTURE 0
13
14#endif
15
16 6
17layout (location = 0) in vec2 frag_tex_coord; 7layout (location = 0) in vec2 frag_tex_coord;
18 8
19layout (location = 0) out vec4 color; 9layout (location = 0) out vec4 color;
20 10
21layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; 11layout (binding = 0) uniform sampler2D color_texture;
22 12
23vec4 cubic(float v) { 13vec4 cubic(float v) {
24 vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; 14 vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag
index 5f54b71b6..ad9bb76a4 100644
--- a/src/video_core/host_shaders/present_gaussian.frag
+++ b/src/video_core/host_shaders/present_gaussian.frag
@@ -7,21 +7,11 @@
7 7
8#version 460 core 8#version 460 core
9 9
10#ifdef VULKAN
11
12#define BINDING_COLOR_TEXTURE 1
13
14#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
15
16#define BINDING_COLOR_TEXTURE 0
17
18#endif
19
20layout(location = 0) in vec2 frag_tex_coord; 10layout(location = 0) in vec2 frag_tex_coord;
21 11
22layout(location = 0) out vec4 color; 12layout(location = 0) out vec4 color;
23 13
24layout(binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; 14layout(binding = 0) uniform sampler2D color_texture;
25 15
26const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); 16const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308);
27const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); 17const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703);
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert
new file mode 100644
index 000000000..6a87a7cac
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 450
5
6layout(location = 0) out vec2 texcoord;
7
8void main() {
9 float x = float((gl_VertexIndex & 1) << 2);
10 float y = float((gl_VertexIndex & 2) << 1);
11 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
12 texcoord = vec2(x, y) / 2.0;
13}
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
index 00af13726..d369bef06 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
@@ -7,4 +7,4 @@
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_EASU 1 8#define USE_EASU 1
9 9
10#include "fidelityfx_fsr.comp" 10#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
index 13d783fa8..6f25ef00f 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
@@ -6,4 +6,4 @@
6 6
7#define USE_EASU 1 7#define USE_EASU 1
8 8
9#include "fidelityfx_fsr.comp" 9#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
index 331549d96..0c953a900 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
@@ -7,4 +7,4 @@
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_RCAS 1 8#define USE_RCAS 1
9 9
10#include "fidelityfx_fsr.comp" 10#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
index 013ca0014..02e9a27c6 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
@@ -6,4 +6,4 @@
6 6
7#define USE_RCAS 1 7#define USE_RCAS 1
8 8
9#include "fidelityfx_fsr.comp" 9#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_present.frag b/src/video_core/host_shaders/vulkan_present.frag
index 97e098ced..adada9411 100644
--- a/src/video_core/host_shaders/vulkan_present.frag
+++ b/src/video_core/host_shaders/vulkan_present.frag
@@ -7,7 +7,7 @@ layout (location = 0) in vec2 frag_tex_coord;
7 7
8layout (location = 0) out vec4 color; 8layout (location = 0) out vec4 color;
9 9
10layout (binding = 1) uniform sampler2D color_texture; 10layout (binding = 0) uniform sampler2D color_texture;
11 11
12void main() { 12void main() {
13 color = texture(color_texture, frag_tex_coord); 13 color = texture(color_texture, frag_tex_coord);
diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert
index 89dc80468..249c9675a 100644
--- a/src/video_core/host_shaders/vulkan_present.vert
+++ b/src/video_core/host_shaders/vulkan_present.vert
@@ -3,16 +3,37 @@
3 3
4#version 460 core 4#version 460 core
5 5
6layout (location = 0) in vec2 vert_position;
7layout (location = 1) in vec2 vert_tex_coord;
8
9layout (location = 0) out vec2 frag_tex_coord; 6layout (location = 0) out vec2 frag_tex_coord;
10 7
11layout (set = 0, binding = 0) uniform MatrixBlock { 8struct ScreenRectVertex {
9 vec2 position;
10 vec2 tex_coord;
11};
12
13layout (push_constant) uniform PushConstants {
12 mat4 modelview_matrix; 14 mat4 modelview_matrix;
15 ScreenRectVertex vertices[4];
13}; 16};
14 17
18// Vulkan spec 15.8.1:
19// Any member of a push constant block that is declared as an
20// array must only be accessed with dynamically uniform indices.
21ScreenRectVertex GetVertex(int index) {
22 switch (index) {
23 case 0:
24 default:
25 return vertices[0];
26 case 1:
27 return vertices[1];
28 case 2:
29 return vertices[2];
30 case 3:
31 return vertices[3];
32 }
33}
34
15void main() { 35void main() {
16 gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); 36 ScreenRectVertex vertex = GetVertex(gl_VertexIndex);
17 frag_tex_coord = vert_tex_coord; 37 gl_Position = modelview_matrix * vec4(vertex.position, 0.0, 1.0);
38 frag_tex_coord = vertex.tex_coord;
18} 39}
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
index 3dc9c0df5..79ea817c2 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
@@ -5,6 +5,7 @@
5 5
6#extension GL_GOOGLE_include_directive : enable 6#extension GL_GOOGLE_include_directive : enable
7 7
8#define VERSION 1
8#define YUZU_USE_FP16 9#define YUZU_USE_FP16
9 10
10#include "opengl_present_scaleforce.frag" 11#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
index 77ed07552..9605bb58b 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
@@ -5,4 +5,6 @@
5 5
6#extension GL_GOOGLE_include_directive : enable 6#extension GL_GOOGLE_include_directive : enable
7 7
8#define VERSION 1
9
8#include "opengl_present_scaleforce.frag" 10#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 8fa4e4d9a..6e2eccfbf 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -155,12 +155,6 @@ public:
155 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 155 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
156 std::span<const u8> memory) = 0; 156 std::span<const u8> memory) = 0;
157 157
158 /// Attempt to use a faster method to display the framebuffer to screen
159 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
160 DAddr framebuffer_addr, u32 pixel_stride) {
161 return false;
162 }
163
164 /// Initialize disk cached resources for the game being emulated 158 /// Initialize disk cached resources for the game being emulated
165 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 159 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
166 const DiskResourceLoadCallback& callback) {} 160 const DiskResourceLoadCallback& callback) {}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 78ea5208b..3ad180f67 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -38,7 +38,7 @@ public:
38 virtual ~RendererBase(); 38 virtual ~RendererBase();
39 39
40 /// Finalize rendering the guest frame and draw into the presentation texture 40 /// Finalize rendering the guest frame and draw into the presentation texture
41 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 41 virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
42 42
43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; 43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
44 44
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index abfabb65b..a5cda0f38 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -92,10 +92,6 @@ bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surfac
92} 92}
93void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 93void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
94 std::span<const u8> memory) {} 94 std::span<const u8> memory) {}
95bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
96 DAddr framebuffer_addr, u32 pixel_stride) {
97 return true;
98}
99void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, 95void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
100 const VideoCore::DiskResourceLoadCallback& callback) {} 96 const VideoCore::DiskResourceLoadCallback& callback) {}
101void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { 97void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index a5789604f..c7f5849c7 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -77,8 +77,6 @@ public:
77 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 77 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
78 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 78 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
79 std::span<const u8> memory) override; 79 std::span<const u8> memory) override;
80 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
81 u32 pixel_stride) override;
82 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 80 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
83 const VideoCore::DiskResourceLoadCallback& callback) override; 81 const VideoCore::DiskResourceLoadCallback& callback) override;
84 void InitializeChannel(Tegra::Control::ChannelState& channel) override; 82 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
index 078feb925..c89daff53 100644
--- a/src/video_core/renderer_null/renderer_null.cpp
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -13,8 +13,8 @@ RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gp
13 13
14RendererNull::~RendererNull() = default; 14RendererNull::~RendererNull() = default;
15 15
16void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 16void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
17 if (!framebuffer) { 17 if (framebuffers.empty()) {
18 return; 18 return;
19 } 19 }
20 20
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
index 9531b43f6..063b476bb 100644
--- a/src/video_core/renderer_null/renderer_null.h
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -17,7 +17,7 @@ public:
17 std::unique_ptr<Core::Frontend::GraphicsContext> context); 17 std::unique_ptr<Core::Frontend::GraphicsContext> context);
18 ~RendererNull() override; 18 ~RendererNull() override;
19 19
20 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 20 void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
21 21
22 VideoCore::RasterizerInterface* ReadRasterizer() override { 22 VideoCore::RasterizerInterface* ReadRasterizer() override {
23 return &m_rasterizer; 23 return &m_rasterizer;
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp
new file mode 100644
index 000000000..6ba8b214b
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp
@@ -0,0 +1,96 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h"
6#include "video_core/renderer_opengl/gl_state_tracker.h"
7#include "video_core/renderer_opengl/present/filters.h"
8#include "video_core/renderer_opengl/present/layer.h"
9#include "video_core/renderer_opengl/present/window_adapt_pass.h"
10
11namespace OpenGL {
12
13BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
14 Tegra::MaxwellDeviceMemoryManager& device_memory_,
15 StateTracker& state_tracker_, ProgramManager& program_manager_,
16 Device& device_)
17 : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
18 program_manager(program_manager_), device(device_) {}
19
20BlitScreen::~BlitScreen() = default;
21
22void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
23 const Layout::FramebufferLayout& layout) {
24 // TODO: Signal state tracker about these changes
25 state_tracker.NotifyScreenDrawVertexArray();
26 state_tracker.NotifyPolygonModes();
27 state_tracker.NotifyViewport0();
28 state_tracker.NotifyScissor0();
29 state_tracker.NotifyColorMask(0);
30 state_tracker.NotifyBlend0();
31 state_tracker.NotifyFramebuffer();
32 state_tracker.NotifyFrontFace();
33 state_tracker.NotifyCullTest();
34 state_tracker.NotifyDepthTest();
35 state_tracker.NotifyStencilTest();
36 state_tracker.NotifyPolygonOffset();
37 state_tracker.NotifyRasterizeEnable();
38 state_tracker.NotifyFramebufferSRGB();
39 state_tracker.NotifyLogicOp();
40 state_tracker.NotifyClipControl();
41 state_tracker.NotifyAlphaTest();
42 state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
43
44 glEnable(GL_CULL_FACE);
45 glDisable(GL_COLOR_LOGIC_OP);
46 glDisable(GL_DEPTH_TEST);
47 glDisable(GL_STENCIL_TEST);
48 glDisable(GL_POLYGON_OFFSET_FILL);
49 glDisable(GL_RASTERIZER_DISCARD);
50 glDisable(GL_ALPHA_TEST);
51 glDisablei(GL_BLEND, 0);
52 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
53 glCullFace(GL_BACK);
54 glFrontFace(GL_CW);
55 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
56 glDepthRangeIndexed(0, 0.0, 0.0);
57
58 while (layers.size() < framebuffers.size()) {
59 layers.emplace_back(rasterizer, device_memory);
60 }
61
62 CreateWindowAdapt();
63 window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout);
64
65 // TODO
66 // program_manager.RestoreGuestPipeline();
67}
68
69void BlitScreen::CreateWindowAdapt() {
70 if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) {
71 return;
72 }
73
74 current_window_adapt = Settings::values.scaling_filter.GetValue();
75 switch (current_window_adapt) {
76 case Settings::ScalingFilter::NearestNeighbor:
77 window_adapt = MakeNearestNeighbor(device);
78 break;
79 case Settings::ScalingFilter::Bicubic:
80 window_adapt = MakeBicubic(device);
81 break;
82 case Settings::ScalingFilter::Gaussian:
83 window_adapt = MakeGaussian(device);
84 break;
85 case Settings::ScalingFilter::ScaleForce:
86 window_adapt = MakeScaleForce(device);
87 break;
88 case Settings::ScalingFilter::Fsr:
89 case Settings::ScalingFilter::Bilinear:
90 default:
91 window_adapt = MakeBilinear(device);
92 break;
93 }
94}
95
96} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h
new file mode 100644
index 000000000..0c3d838f1
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_blit_screen.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 <list>
7#include <memory>
8#include <span>
9
10#include "core/hle/service/nvnflinger/pixel_format.h"
11#include "video_core/host1x/gpu_device_memory_manager.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h"
13
14namespace Layout {
15struct FramebufferLayout;
16}
17
18namespace Tegra {
19struct FramebufferConfig;
20}
21
22namespace Settings {
23enum class ScalingFilter : u32;
24}
25
26namespace OpenGL {
27
28class Device;
29class Layer;
30class ProgramManager;
31class RasterizerOpenGL;
32class StateTracker;
33class WindowAdaptPass;
34
35/// Structure used for storing information about the display target for the Switch screen
36struct FramebufferTextureInfo {
37 GLuint display_texture{};
38 u32 width;
39 u32 height;
40 u32 scaled_width;
41 u32 scaled_height;
42};
43
44class BlitScreen {
45public:
46 explicit BlitScreen(RasterizerOpenGL& rasterizer,
47 Tegra::MaxwellDeviceMemoryManager& device_memory,
48 StateTracker& state_tracker, ProgramManager& program_manager,
49 Device& device);
50 ~BlitScreen();
51
52 /// Draws the emulated screens to the emulator window.
53 void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
54 const Layout::FramebufferLayout& layout);
55
56private:
57 void CreateWindowAdapt();
58
59 RasterizerOpenGL& rasterizer;
60 Tegra::MaxwellDeviceMemoryManager& device_memory;
61 StateTracker& state_tracker;
62 ProgramManager& program_manager;
63 Device& device;
64
65 Settings::ScalingFilter current_window_adapt{};
66 std::unique_ptr<WindowAdaptPass> window_adapt;
67
68 std::list<Layer> layers;
69};
70
71} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp
deleted file mode 100644
index 77262dcf1..000000000
--- a/src/video_core/renderer_opengl/gl_fsr.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/renderer_opengl/gl_fsr.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
9
10namespace OpenGL {
11using namespace FSR;
12
13using FsrConstants = std::array<u32, 4 * 4>;
14
15FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
16 std::string_view fsr_rcas_source)
17 : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
18 fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
19 fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
20 glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
21 glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
22}
23
24FSR::~FSR() = default;
25
26void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
27 u32 input_image_width, u32 input_image_height,
28 const Common::Rectangle<int>& crop_rect) {
29
30 const auto output_image_width = screen.GetWidth();
31 const auto output_image_height = screen.GetHeight();
32
33 if (fsr_intermediate_tex.handle) {
34 GLint fsr_tex_width, fsr_tex_height;
35 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
36 &fsr_tex_width);
37 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
38 &fsr_tex_height);
39 if (static_cast<u32>(fsr_tex_width) != output_image_width ||
40 static_cast<u32>(fsr_tex_height) != output_image_height) {
41 fsr_intermediate_tex.Release();
42 }
43 }
44 if (!fsr_intermediate_tex.handle) {
45 fsr_intermediate_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
47 output_image_height);
48 glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
49 fsr_intermediate_tex.handle, 0);
50 }
51
52 GLint old_draw_fb;
53 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
54
55 glFrontFace(GL_CW);
56 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
57 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
58 static_cast<GLfloat>(output_image_height));
59
60 FsrConstants constants;
61 FsrEasuConOffset(
62 constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
63
64 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
65 static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
66 static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
67 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
68
69 glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
70
71 program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
72 glDrawArrays(GL_TRIANGLES, 0, 3);
73
74 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
75 glBindTextureUnit(0, fsr_intermediate_tex.handle);
76
77 const float sharpening =
78 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
79
80 FsrRcasCon(constants.data(), sharpening);
81 glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
82}
83
84void FSR::InitBuffers() {
85 fsr_framebuffer.Create();
86}
87
88void FSR::ReleaseBuffers() {
89 fsr_framebuffer.Release();
90 fsr_intermediate_tex.Release();
91}
92
93const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
94 return fsr_rcas_frag;
95}
96
97bool FSR::AreBuffersInitialized() const noexcept {
98 return fsr_framebuffer.handle;
99}
100
101} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h
deleted file mode 100644
index 1f6ae3115..000000000
--- a/src/video_core/renderer_opengl/gl_fsr.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
20 std::string_view fsr_rcas_source);
21 ~FSR();
22
23 void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
24 u32 input_image_width, u32 input_image_height,
25 const Common::Rectangle<int>& crop_rect);
26
27 void InitBuffers();
28
29 void ReleaseBuffers();
30
31 [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
32
33 [[nodiscard]] bool AreBuffersInitialized() const noexcept;
34
35private:
36 OGLFramebuffer fsr_framebuffer;
37 OGLProgram fsr_vertex;
38 OGLProgram fsr_easu_frag;
39 OGLProgram fsr_rcas_frag;
40 OGLTexture fsr_intermediate_tex;
41};
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d5354ef2d..b42fb110c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -71,10 +71,10 @@ std::optional<VideoCore::QueryType> MaxwellToVideoCoreQuery(VideoCommon::QueryTy
71 71
72RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 72RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
73 Tegra::MaxwellDeviceMemoryManager& device_memory_, 73 Tegra::MaxwellDeviceMemoryManager& device_memory_,
74 const Device& device_, ScreenInfo& screen_info_, 74 const Device& device_, ProgramManager& program_manager_,
75 ProgramManager& program_manager_, StateTracker& state_tracker_) 75 StateTracker& state_tracker_)
76 : gpu(gpu_), device_memory(device_memory_), device(device_), screen_info(screen_info_), 76 : gpu(gpu_), device_memory(device_memory_), device(device_), program_manager(program_manager_),
77 program_manager(program_manager_), state_tracker(state_tracker_), 77 state_tracker(state_tracker_),
78 texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool), 78 texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool),
79 texture_cache(texture_cache_runtime, device_memory_), 79 texture_cache(texture_cache_runtime, device_memory_),
80 buffer_cache_runtime(device, staging_buffer_pool), 80 buffer_cache_runtime(device, staging_buffer_pool),
@@ -739,27 +739,29 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
739 query_cache.InvalidateRegion(*cpu_addr, copy_size); 739 query_cache.InvalidateRegion(*cpu_addr, copy_size);
740} 740}
741 741
742bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, 742std::optional<FramebufferTextureInfo> RasterizerOpenGL::AccelerateDisplay(
743 DAddr framebuffer_addr, u32 pixel_stride) { 743 const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) {
744 if (framebuffer_addr == 0) { 744 if (framebuffer_addr == 0) {
745 return false; 745 return {};
746 } 746 }
747 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 747 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
748 748
749 std::scoped_lock lock{texture_cache.mutex}; 749 std::scoped_lock lock{texture_cache.mutex};
750 ImageView* const image_view{ 750 const auto [image_view, scaled] =
751 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr)}; 751 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
752 if (!image_view) { 752 if (!image_view) {
753 return false; 753 return {};
754 } 754 }
755 // Verify that the cached surface is the same size and format as the requested framebuffer
756 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different");
757 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different");
758 755
759 screen_info.texture.width = image_view->size.width; 756 const auto& resolution = Settings::values.resolution_info;
760 screen_info.texture.height = image_view->size.height; 757
761 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); 758 FramebufferTextureInfo info{};
762 return true; 759 info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
760 info.width = image_view->size.width;
761 info.height = image_view->size.height;
762 info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width;
763 info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height;
764 return info;
763} 765}
764 766
765void RasterizerOpenGL::SyncState() { 767void RasterizerOpenGL::SyncState() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 34aa73526..6eae51ff7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -16,6 +16,7 @@
16#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
17#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
18#include "video_core/renderer_opengl/blit_image.h" 18#include "video_core/renderer_opengl/blit_image.h"
19#include "video_core/renderer_opengl/gl_blit_screen.h"
19#include "video_core/renderer_opengl/gl_buffer_cache.h" 20#include "video_core/renderer_opengl/gl_buffer_cache.h"
20#include "video_core/renderer_opengl/gl_device.h" 21#include "video_core/renderer_opengl/gl_device.h"
21#include "video_core/renderer_opengl/gl_fence_manager.h" 22#include "video_core/renderer_opengl/gl_fence_manager.h"
@@ -37,7 +38,7 @@ class MemoryManager;
37 38
38namespace OpenGL { 39namespace OpenGL {
39 40
40struct ScreenInfo; 41struct FramebufferTextureInfo;
41struct ShaderEntries; 42struct ShaderEntries;
42 43
43struct BindlessSSBO { 44struct BindlessSSBO {
@@ -76,8 +77,8 @@ class RasterizerOpenGL : public VideoCore::RasterizerInterface,
76public: 77public:
77 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 78 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
78 Tegra::MaxwellDeviceMemoryManager& device_memory_, 79 Tegra::MaxwellDeviceMemoryManager& device_memory_,
79 const Device& device_, ScreenInfo& screen_info_, 80 const Device& device_, ProgramManager& program_manager_,
80 ProgramManager& program_manager_, StateTracker& state_tracker_); 81 StateTracker& state_tracker_);
81 ~RasterizerOpenGL() override; 82 ~RasterizerOpenGL() override;
82 83
83 void Draw(bool is_indexed, u32 instance_count) override; 84 void Draw(bool is_indexed, u32 instance_count) override;
@@ -122,8 +123,6 @@ public:
122 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 123 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
123 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 124 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
124 std::span<const u8> memory) override; 125 std::span<const u8> memory) override;
125 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
126 u32 pixel_stride) override;
127 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 126 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
128 const VideoCore::DiskResourceLoadCallback& callback) override; 127 const VideoCore::DiskResourceLoadCallback& callback) override;
129 128
@@ -144,6 +143,10 @@ public:
144 return true; 143 return true;
145 } 144 }
146 145
146 std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config,
147 VAddr framebuffer_addr,
148 u32 pixel_stride);
149
147private: 150private:
148 static constexpr size_t MAX_TEXTURES = 192; 151 static constexpr size_t MAX_TEXTURES = 192;
149 static constexpr size_t MAX_IMAGES = 48; 152 static constexpr size_t MAX_IMAGES = 48;
@@ -237,7 +240,6 @@ private:
237 Tegra::MaxwellDeviceMemoryManager& device_memory; 240 Tegra::MaxwellDeviceMemoryManager& device_memory;
238 241
239 const Device& device; 242 const Device& device;
240 ScreenInfo& screen_info;
241 ProgramManager& program_manager; 243 ProgramManager& program_manager;
242 StateTracker& state_tracker; 244 StateTracker& state_tracker;
243 245
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 66a5ca03e..be14494ca 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1051,6 +1051,10 @@ void Image::Scale(bool up_scale) {
1051 state_tracker.NotifyScissor0(); 1051 state_tracker.NotifyScissor0();
1052} 1052}
1053 1053
1054bool Image::IsRescaled() const {
1055 return True(flags & ImageFlagBits::Rescaled);
1056}
1057
1054bool Image::ScaleUp(bool ignore) { 1058bool Image::ScaleUp(bool ignore) {
1055 const auto& resolution = runtime->resolution; 1059 const auto& resolution = runtime->resolution;
1056 if (!resolution.active) { 1060 if (!resolution.active) {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 34870c81f..3e54edcc2 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -217,6 +217,8 @@ public:
217 return gl_type; 217 return gl_type;
218 } 218 }
219 219
220 bool IsRescaled() const;
221
220 bool ScaleUp(bool ignore = false); 222 bool ScaleUp(bool ignore = false);
221 223
222 bool ScaleDown(bool ignore = false); 224 bool ScaleDown(bool ignore = false);
diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp
new file mode 100644
index 000000000..819e5d77f
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/filters.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/opengl_present_frag.h"
5#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
6#include "video_core/host_shaders/present_bicubic_frag.h"
7#include "video_core/host_shaders/present_gaussian_frag.h"
8#include "video_core/renderer_opengl/present/filters.h"
9#include "video_core/renderer_opengl/present/util.h"
10
11namespace OpenGL {
12
13std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device) {
14 return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(),
15 HostShaders::OPENGL_PRESENT_FRAG);
16}
17
18std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) {
19 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
20 HostShaders::OPENGL_PRESENT_FRAG);
21}
22
23std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) {
24 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
25 HostShaders::PRESENT_BICUBIC_FRAG);
26}
27
28std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) {
29 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
30 HostShaders::PRESENT_GAUSSIAN_FRAG);
31}
32
33std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) {
34 return std::make_unique<WindowAdaptPass>(
35 device, CreateBilinearSampler(),
36 fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG));
37}
38
39} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h
new file mode 100644
index 000000000..122ab7436
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/filters.h
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include "video_core/renderer_opengl/present/window_adapt_pass.h"
8
9namespace OpenGL {
10
11std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device);
12std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device);
13std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device);
14std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device);
15std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device);
16
17} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fsr.cpp b/src/video_core/renderer_opengl/present/fsr.cpp
new file mode 100644
index 000000000..b764aadae
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fsr.cpp
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/host_shaders/ffx_a_h.h"
7#include "video_core/host_shaders/ffx_fsr1_h.h"
8#include "video_core/host_shaders/full_screen_triangle_vert.h"
9#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
10#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
11#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
12#include "video_core/renderer_opengl/gl_shader_manager.h"
13#include "video_core/renderer_opengl/gl_shader_util.h"
14#include "video_core/renderer_opengl/present/fsr.h"
15#include "video_core/renderer_opengl/present/util.h"
16
17namespace OpenGL {
18using namespace FSR;
19
20using FsrConstants = std::array<u32, 4 * 4>;
21
22FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) {
23 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
24 ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
25 ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
26
27 std::string fsr_easu_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
28 std::string fsr_rcas_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
29 ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source);
30 ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source);
31
32 vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER);
33 easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER);
34 rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER);
35
36 glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f);
37 glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f);
38
39 sampler = CreateBilinearSampler();
40 framebuffer.Create();
41
42 easu_tex.Create(GL_TEXTURE_2D);
43 glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height);
44
45 rcas_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height);
47}
48
49FSR::~FSR() = default;
50
51GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
52 u32 input_image_height, const Common::Rectangle<f32>& crop_rect) {
53 const f32 input_width = static_cast<f32>(input_image_width);
54 const f32 input_height = static_cast<f32>(input_image_height);
55 const f32 output_width = static_cast<f32>(width);
56 const f32 output_height = static_cast<f32>(height);
57 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width;
58 const f32 viewport_x = crop_rect.left * input_width;
59 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height;
60 const f32 viewport_y = crop_rect.top * input_height;
61
62 FsrConstants easu_con{};
63 FsrConstants rcas_con{};
64
65 FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
66 easu_con.data() + 12, viewport_width, viewport_height, input_width,
67 input_height, output_width, output_height, viewport_x, viewport_y);
68
69 const float sharpening =
70 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
71
72 FsrRcasCon(rcas_con.data(), sharpening);
73
74 glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data());
75 glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data());
76
77 glFrontFace(GL_CW);
78 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
79 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0);
80 glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height);
81 program_manager.BindPresentPrograms(vert.handle, easu_frag.handle);
82 glBindTextureUnit(0, texture);
83 glBindSampler(0, sampler.handle);
84 glDrawArrays(GL_TRIANGLES, 0, 3);
85
86 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0);
87 program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle);
88 glBindTextureUnit(0, easu_tex.handle);
89 glDrawArrays(GL_TRIANGLES, 0, 3);
90
91 return rcas_tex.handle;
92}
93
94bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) {
95 return screen.GetWidth() != width || screen.GetHeight() != height;
96}
97
98} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fsr.h b/src/video_core/renderer_opengl/present/fsr.h
new file mode 100644
index 000000000..606935a01
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fsr.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(u32 output_width, u32 output_height);
20 ~FSR();
21
22 GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
23 u32 input_image_height, const Common::Rectangle<f32>& crop_rect);
24
25 bool NeedsRecreation(const Common::Rectangle<u32>& screen);
26
27private:
28 const u32 width;
29 const u32 height;
30 OGLFramebuffer framebuffer;
31 OGLSampler sampler;
32 OGLProgram vert;
33 OGLProgram easu_frag;
34 OGLProgram rcas_frag;
35 OGLTexture easu_tex;
36 OGLTexture rcas_tex;
37};
38
39} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fxaa.cpp b/src/video_core/renderer_opengl/present/fxaa.cpp
new file mode 100644
index 000000000..d9b58512d
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fxaa.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/fxaa_frag.h"
5#include "video_core/host_shaders/fxaa_vert.h"
6#include "video_core/renderer_opengl/gl_shader_manager.h"
7#include "video_core/renderer_opengl/gl_shader_util.h"
8#include "video_core/renderer_opengl/present/fxaa.h"
9#include "video_core/renderer_opengl/present/util.h"
10
11namespace OpenGL {
12
13FXAA::FXAA(u32 width, u32 height) {
14 vert_shader = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
15 frag_shader = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
16
17 sampler = CreateBilinearSampler();
18
19 framebuffer.Create();
20
21 texture.Create(GL_TEXTURE_2D);
22 glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
23 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
24}
25
26FXAA::~FXAA() = default;
27
28GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
29 glFrontFace(GL_CCW);
30
31 program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle);
32 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
33 glBindTextureUnit(0, input_texture);
34 glBindSampler(0, sampler.handle);
35 glDrawArrays(GL_TRIANGLES, 0, 3);
36 glFrontFace(GL_CW);
37
38 return texture.handle;
39}
40
41} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fxaa.h b/src/video_core/renderer_opengl/present/fxaa.h
new file mode 100644
index 000000000..b898198f1
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fxaa.h
@@ -0,0 +1,27 @@
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_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10class ProgramManager;
11
12class FXAA {
13public:
14 explicit FXAA(u32 width, u32 height);
15 ~FXAA();
16
17 GLuint Draw(ProgramManager& program_manager, GLuint input_texture);
18
19private:
20 OGLProgram vert_shader;
21 OGLProgram frag_shader;
22 OGLSampler sampler;
23 OGLFramebuffer framebuffer;
24 OGLTexture texture;
25};
26
27} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp
new file mode 100644
index 000000000..8643e07c6
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/layer.cpp
@@ -0,0 +1,215 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/framebuffer_config.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h"
6#include "video_core/renderer_opengl/gl_rasterizer.h"
7#include "video_core/renderer_opengl/present/fsr.h"
8#include "video_core/renderer_opengl/present/fxaa.h"
9#include "video_core/renderer_opengl/present/layer.h"
10#include "video_core/renderer_opengl/present/present_uniforms.h"
11#include "video_core/renderer_opengl/present/smaa.h"
12#include "video_core/surface.h"
13#include "video_core/textures/decoders.h"
14
15namespace OpenGL {
16
17Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_)
18 : rasterizer(rasterizer_), device_memory(device_memory_) {
19 // Allocate textures for the screen
20 framebuffer_texture.resource.Create(GL_TEXTURE_2D);
21
22 const GLuint texture = framebuffer_texture.resource.handle;
23 glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);
24
25 // Clear screen to black
26 const u8 framebuffer_data[4] = {0, 0, 0, 0};
27 glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE,
28 framebuffer_data);
29}
30
31Layer::~Layer() = default;
32
33GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
34 std::array<ScreenRectVertex, 4>& out_vertices,
35 ProgramManager& program_manager,
36 const Tegra::FramebufferConfig& framebuffer,
37 const Layout::FramebufferLayout& layout) {
38 FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
39 auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
40 GLuint texture = info.display_texture;
41
42 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
43 if (anti_aliasing != Settings::AntiAliasing::None) {
44 glEnablei(GL_SCISSOR_TEST, 0);
45 auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
46 auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height);
47
48 glScissorIndexed(0, 0, 0, viewport_width, viewport_height);
49 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width),
50 static_cast<GLfloat>(viewport_height));
51
52 switch (anti_aliasing) {
53 case Settings::AntiAliasing::Fxaa:
54 CreateFXAA();
55 texture = fxaa->Draw(program_manager, info.display_texture);
56 break;
57 case Settings::AntiAliasing::Smaa:
58 default:
59 CreateSMAA();
60 texture = smaa->Draw(program_manager, info.display_texture);
61 break;
62 }
63 }
64
65 glDisablei(GL_SCISSOR_TEST, 0);
66
67 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
68 if (!fsr || fsr->NeedsRecreation(layout.screen)) {
69 fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
70 }
71
72 texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop);
73 crop = {0, 0, 1, 1};
74 }
75
76 out_matrix =
77 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
78
79 // Map the coordinates to the screen.
80 const auto& screen = layout.screen;
81 const auto x = screen.left;
82 const auto y = screen.top;
83 const auto w = screen.GetWidth();
84 const auto h = screen.GetHeight();
85
86 out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
87 out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
88 out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
89 out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
90
91 return texture;
92}
93
94FramebufferTextureInfo Layer::PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer) {
95 // If framebuffer is provided, reload it from memory to a texture
96 if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) ||
97 framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) ||
98 framebuffer_texture.pixel_format != framebuffer.pixel_format ||
99 gl_framebuffer_data.empty()) {
100 // Reallocate texture if the framebuffer size has changed.
101 // This is expected to not happen very often and hence should not be a
102 // performance problem.
103 ConfigureFramebufferTexture(framebuffer);
104 }
105
106 // Load the framebuffer from memory if needed
107 return LoadFBToScreenInfo(framebuffer);
108}
109
110FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
111 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
112 const auto accelerated_info =
113 rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride);
114 if (accelerated_info) {
115 return *accelerated_info;
116 }
117
118 // Reset the screen info's display texture to its own permanent texture
119 FramebufferTextureInfo info{};
120 info.display_texture = framebuffer_texture.resource.handle;
121 info.width = framebuffer.width;
122 info.height = framebuffer.height;
123 info.scaled_width = framebuffer.width;
124 info.scaled_height = framebuffer.height;
125
126 // TODO(Rodrigo): Read this from HLE
127 constexpr u32 block_height_log2 = 4;
128 const auto pixel_format{
129 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
130 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
131 const u64 size_in_bytes{Tegra::Texture::CalculateSize(
132 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
133 const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
134 const std::span<const u8> input_data(host_ptr, size_in_bytes);
135 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
136 framebuffer.width, framebuffer.height, 1, block_height_log2,
137 0);
138
139 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
140 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
141
142 // Update existing texture
143 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
144 // they differ from the LCD resolution.
145 // TODO: Applications could theoretically crash yuzu here by specifying too large
146 // framebuffer sizes. We should make sure that this cannot happen.
147 glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width,
148 framebuffer.height, framebuffer_texture.gl_format,
149 framebuffer_texture.gl_type, gl_framebuffer_data.data());
150
151 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
152
153 return info;
154}
155
156void Layer::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) {
157 framebuffer_texture.width = framebuffer.width;
158 framebuffer_texture.height = framebuffer.height;
159 framebuffer_texture.pixel_format = framebuffer.pixel_format;
160
161 const auto pixel_format{
162 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
163 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
164 gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height *
165 bytes_per_pixel);
166
167 GLint internal_format;
168 switch (framebuffer.pixel_format) {
169 case Service::android::PixelFormat::Rgba8888:
170 internal_format = GL_RGBA8;
171 framebuffer_texture.gl_format = GL_RGBA;
172 framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
173 break;
174 case Service::android::PixelFormat::Rgb565:
175 internal_format = GL_RGB565;
176 framebuffer_texture.gl_format = GL_RGB;
177 framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
178 break;
179 default:
180 internal_format = GL_RGBA8;
181 framebuffer_texture.gl_format = GL_RGBA;
182 framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
183 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
184 // static_cast<u32>(framebuffer.pixel_format));
185 break;
186 }
187
188 framebuffer_texture.resource.Release();
189 framebuffer_texture.resource.Create(GL_TEXTURE_2D);
190 glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format,
191 framebuffer_texture.width, framebuffer_texture.height);
192
193 fxaa.reset();
194 smaa.reset();
195}
196
197void Layer::CreateFXAA() {
198 smaa.reset();
199 if (!fxaa) {
200 fxaa = std::make_unique<FXAA>(
201 Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
202 Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
203 }
204}
205
206void Layer::CreateSMAA() {
207 fxaa.reset();
208 if (!smaa) {
209 smaa = std::make_unique<SMAA>(
210 Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
211 Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
212 }
213}
214
215} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h
new file mode 100644
index 000000000..ef1055abf
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/layer.h
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "video_core/host1x/gpu_device_memory_manager.h"
10#include "video_core/renderer_opengl/gl_resource_manager.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Service::android {
17enum class PixelFormat : u32;
18};
19
20namespace Tegra {
21struct FramebufferConfig;
22}
23
24namespace OpenGL {
25
26struct FramebufferTextureInfo;
27class FSR;
28class FXAA;
29class ProgramManager;
30class RasterizerOpenGL;
31class SMAA;
32
33/// Structure used for storing information about the textures for the Switch screen
34struct TextureInfo {
35 OGLTexture resource;
36 GLsizei width;
37 GLsizei height;
38 GLenum gl_format;
39 GLenum gl_type;
40 Service::android::PixelFormat pixel_format;
41};
42
43struct ScreenRectVertex;
44
45class Layer {
46public:
47 explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory);
48 ~Layer();
49
50 GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
51 std::array<ScreenRectVertex, 4>& out_vertices,
52 ProgramManager& program_manager,
53 const Tegra::FramebufferConfig& framebuffer,
54 const Layout::FramebufferLayout& layout);
55
56private:
57 /// Loads framebuffer from emulated memory into the active OpenGL texture.
58 FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
59 FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer);
60 void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer);
61
62 void CreateFXAA();
63 void CreateSMAA();
64
65private:
66 RasterizerOpenGL& rasterizer;
67 Tegra::MaxwellDeviceMemoryManager& device_memory;
68
69 /// OpenGL framebuffer data
70 std::vector<u8> gl_framebuffer_data;
71
72 /// Display information for Switch screen
73 TextureInfo framebuffer_texture;
74
75 std::unique_ptr<FSR> fsr;
76 std::unique_ptr<FXAA> fxaa;
77 std::unique_ptr<SMAA> smaa;
78};
79
80} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/present_uniforms.h b/src/video_core/renderer_opengl/present/present_uniforms.h
new file mode 100644
index 000000000..3a19f05c7
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/present_uniforms.h
@@ -0,0 +1,43 @@
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_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10constexpr GLint PositionLocation = 0;
11constexpr GLint TexCoordLocation = 1;
12constexpr GLint ModelViewMatrixLocation = 0;
13
14struct ScreenRectVertex {
15 constexpr ScreenRectVertex() = default;
16
17 constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
18 : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
19
20 std::array<GLfloat, 2> position{};
21 std::array<GLfloat, 2> tex_coord{};
22};
23
24/**
25 * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left
26 * corner and (width, height) on the lower-bottom.
27 *
28 * The projection part of the matrix is trivial, hence these operations are represented
29 * by a 3x2 matrix.
30 */
31static inline std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
32 std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
33
34 // clang-format off
35 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
36 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
37 // Last matrix row is implicitly assumed to be [0, 0, 1].
38 // clang-format on
39
40 return matrix;
41}
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/smaa.cpp b/src/video_core/renderer_opengl/present/smaa.cpp
new file mode 100644
index 000000000..de7f6e502
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/smaa.cpp
@@ -0,0 +1,102 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/opengl_smaa_glsl.h"
5#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h"
6#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h"
7#include "video_core/host_shaders/smaa_edge_detection_frag.h"
8#include "video_core/host_shaders/smaa_edge_detection_vert.h"
9#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
10#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
11#include "video_core/renderer_opengl/gl_shader_manager.h"
12#include "video_core/renderer_opengl/gl_shader_util.h"
13#include "video_core/renderer_opengl/present/smaa.h"
14#include "video_core/renderer_opengl/present/util.h"
15#include "video_core/smaa_area_tex.h"
16#include "video_core/smaa_search_tex.h"
17
18namespace OpenGL {
19
20SMAA::SMAA(u32 width, u32 height) {
21 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
22 std::string shader_source{specialized_source};
23 ReplaceInclude(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
24 return CreateProgram(shader_source, stage);
25 };
26
27 edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER);
28 edge_detection_frag = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER);
29 blending_weight_calculation_vert =
30 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER);
31 blending_weight_calculation_frag =
32 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER);
33 neighborhood_blending_vert =
34 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER);
35 neighborhood_blending_frag =
36 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);
37
38 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
39 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
40
41 area_tex.Create(GL_TEXTURE_2D);
42 glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);
43 glTextureSubImage2D(area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG,
44 GL_UNSIGNED_BYTE, areaTexBytes);
45 search_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT);
47 glTextureSubImage2D(search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED,
48 GL_UNSIGNED_BYTE, searchTexBytes);
49
50 edges_tex.Create(GL_TEXTURE_2D);
51 glTextureStorage2D(edges_tex.handle, 1, GL_RG16F, width, height);
52
53 blend_tex.Create(GL_TEXTURE_2D);
54 glTextureStorage2D(blend_tex.handle, 1, GL_RGBA16F, width, height);
55
56 sampler = CreateBilinearSampler();
57
58 framebuffer.Create();
59
60 texture.Create(GL_TEXTURE_2D);
61 glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
62 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
63}
64
65SMAA::~SMAA() = default;
66
67GLuint SMAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
68 glClearColor(0, 0, 0, 0);
69 glFrontFace(GL_CCW);
70 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
71 glBindSampler(0, sampler.handle);
72 glBindSampler(1, sampler.handle);
73 glBindSampler(2, sampler.handle);
74
75 glBindTextureUnit(0, input_texture);
76 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, edges_tex.handle, 0);
77 glClear(GL_COLOR_BUFFER_BIT);
78 program_manager.BindPresentPrograms(edge_detection_vert.handle, edge_detection_frag.handle);
79 glDrawArrays(GL_TRIANGLES, 0, 3);
80
81 glBindTextureUnit(0, edges_tex.handle);
82 glBindTextureUnit(1, area_tex.handle);
83 glBindTextureUnit(2, search_tex.handle);
84 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, blend_tex.handle, 0);
85 glClear(GL_COLOR_BUFFER_BIT);
86 program_manager.BindPresentPrograms(blending_weight_calculation_vert.handle,
87 blending_weight_calculation_frag.handle);
88 glDrawArrays(GL_TRIANGLES, 0, 3);
89
90 glBindTextureUnit(0, input_texture);
91 glBindTextureUnit(1, blend_tex.handle);
92 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
93 program_manager.BindPresentPrograms(neighborhood_blending_vert.handle,
94 neighborhood_blending_frag.handle);
95 glClear(GL_COLOR_BUFFER_BIT);
96 glDrawArrays(GL_TRIANGLES, 0, 3);
97 glFrontFace(GL_CW);
98
99 return texture.handle;
100}
101
102} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/smaa.h b/src/video_core/renderer_opengl/present/smaa.h
new file mode 100644
index 000000000..a48cb4fa9
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/smaa.h
@@ -0,0 +1,35 @@
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_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10class ProgramManager;
11
12class SMAA {
13public:
14 explicit SMAA(u32 width, u32 height);
15 ~SMAA();
16
17 GLuint Draw(ProgramManager& program_manager, GLuint input_texture);
18
19private:
20 OGLProgram edge_detection_vert;
21 OGLProgram blending_weight_calculation_vert;
22 OGLProgram neighborhood_blending_vert;
23 OGLProgram edge_detection_frag;
24 OGLProgram blending_weight_calculation_frag;
25 OGLProgram neighborhood_blending_frag;
26 OGLTexture area_tex;
27 OGLTexture search_tex;
28 OGLTexture edges_tex;
29 OGLTexture blend_tex;
30 OGLSampler sampler;
31 OGLFramebuffer framebuffer;
32 OGLTexture texture;
33};
34
35} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/util.h b/src/video_core/renderer_opengl/present/util.h
new file mode 100644
index 000000000..67f03aa27
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/util.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/assert.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10
11namespace OpenGL {
12
13static inline void ReplaceInclude(std::string& shader_source, std::string_view include_name,
14 std::string_view include_content) {
15 const std::string include_string = fmt::format("#include \"{}\"", include_name);
16 const std::size_t pos = shader_source.find(include_string);
17 ASSERT(pos != std::string::npos);
18 shader_source.replace(pos, include_string.size(), include_content);
19};
20
21static inline OGLSampler CreateBilinearSampler() {
22 OGLSampler sampler;
23 sampler.Create();
24 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
25 glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
26 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
27 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
28 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
29 return sampler;
30}
31
32static inline OGLSampler CreateNearestNeighborSampler() {
33 OGLSampler sampler;
34 sampler.Create();
35 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
36 glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
37 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
38 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
39 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
40 return sampler;
41}
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
new file mode 100644
index 000000000..4d681606b
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/framebuffer_config.h"
6#include "video_core/host_shaders/opengl_present_vert.h"
7#include "video_core/renderer_opengl/gl_device.h"
8#include "video_core/renderer_opengl/gl_shader_manager.h"
9#include "video_core/renderer_opengl/gl_shader_util.h"
10#include "video_core/renderer_opengl/present/layer.h"
11#include "video_core/renderer_opengl/present/present_uniforms.h"
12#include "video_core/renderer_opengl/present/window_adapt_pass.h"
13
14namespace OpenGL {
15
16WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_,
17 std::string_view frag_source)
18 : device(device_), sampler(std::move(sampler_)) {
19 vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
20 frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER);
21
22 // Generate VBO handle for drawing
23 vertex_buffer.Create();
24
25 // Attach vertex data to VAO
26 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
27
28 // Query vertex buffer address when the driver supports unified vertex attributes
29 if (device.HasVertexBufferUnifiedMemory()) {
30 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
31 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
32 &vertex_buffer_address);
33 }
34}
35
36WindowAdaptPass::~WindowAdaptPass() = default;
37
38void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
39 std::span<const Tegra::FramebufferConfig> framebuffers,
40 const Layout::FramebufferLayout& layout) {
41 GLint old_read_fb;
42 GLint old_draw_fb;
43 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
44 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
45
46 const size_t layer_count = framebuffers.size();
47 std::vector<GLuint> textures(layer_count);
48 std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count);
49 std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count);
50
51 auto layer_it = layers.begin();
52 for (size_t i = 0; i < layer_count; i++) {
53 textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
54 framebuffers[i], layout);
55 layer_it++;
56 }
57
58 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
59 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
60
61 program_manager.BindPresentPrograms(vert.handle, frag.handle);
62
63 glDisable(GL_FRAMEBUFFER_SRGB);
64 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
65 static_cast<GLfloat>(layout.height));
66
67 glEnableVertexAttribArray(PositionLocation);
68 glEnableVertexAttribArray(TexCoordLocation);
69 glVertexAttribDivisor(PositionLocation, 0);
70 glVertexAttribDivisor(TexCoordLocation, 0);
71 glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
72 offsetof(ScreenRectVertex, position));
73 glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
74 offsetof(ScreenRectVertex, tex_coord));
75 glVertexAttribBinding(PositionLocation, 0);
76 glVertexAttribBinding(TexCoordLocation, 0);
77 if (device.HasVertexBufferUnifiedMemory()) {
78 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
79 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
80 sizeof(decltype(vertices)::value_type));
81 } else {
82 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
83 }
84
85 glBindSampler(0, sampler.handle);
86
87 // Update background color before drawing
88 glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
89 Settings::values.bg_green.GetValue() / 255.0f,
90 Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
91
92 glClear(GL_COLOR_BUFFER_BIT);
93
94 for (size_t i = 0; i < layer_count; i++) {
95 glBindTextureUnit(0, textures[i]);
96 glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
97 matrices[i].data());
98 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i]));
99 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
100 }
101}
102
103} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h
new file mode 100644
index 000000000..00975a9c6
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h
@@ -0,0 +1,47 @@
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#include <span>
8
9#include "common/math_util.h"
10#include "video_core/renderer_opengl/gl_resource_manager.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Tegra {
17struct FramebufferConfig;
18}
19
20namespace OpenGL {
21
22class Device;
23class Layer;
24class ProgramManager;
25
26class WindowAdaptPass final {
27public:
28 explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler,
29 std::string_view frag_source);
30 ~WindowAdaptPass();
31
32 void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
33 std::span<const Tegra::FramebufferConfig> framebuffers,
34 const Layout::FramebufferLayout& layout);
35
36private:
37 const Device& device;
38 OGLSampler sampler;
39 OGLProgram vert;
40 OGLProgram frag;
41 OGLBuffer vertex_buffer;
42
43 // GPU address of the vertex buffer
44 GLuint64EXT vertex_buffer_address = 0;
45};
46
47} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index b75376fdb..e33a32592 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -16,68 +16,15 @@
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/telemetry_session.h" 18#include "core/telemetry_session.h"
19#include "video_core/host_shaders/ffx_a_h.h" 19#include "video_core/renderer_opengl/gl_blit_screen.h"
20#include "video_core/host_shaders/ffx_fsr1_h.h"
21#include "video_core/host_shaders/full_screen_triangle_vert.h"
22#include "video_core/host_shaders/fxaa_frag.h"
23#include "video_core/host_shaders/fxaa_vert.h"
24#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
25#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
26#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
27#include "video_core/host_shaders/opengl_present_frag.h"
28#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
29#include "video_core/host_shaders/opengl_present_vert.h"
30#include "video_core/host_shaders/opengl_smaa_glsl.h"
31#include "video_core/host_shaders/present_bicubic_frag.h"
32#include "video_core/host_shaders/present_gaussian_frag.h"
33#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h"
34#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h"
35#include "video_core/host_shaders/smaa_edge_detection_frag.h"
36#include "video_core/host_shaders/smaa_edge_detection_vert.h"
37#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
38#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
39#include "video_core/renderer_opengl/gl_fsr.h"
40#include "video_core/renderer_opengl/gl_rasterizer.h" 20#include "video_core/renderer_opengl/gl_rasterizer.h"
41#include "video_core/renderer_opengl/gl_shader_manager.h" 21#include "video_core/renderer_opengl/gl_shader_manager.h"
42#include "video_core/renderer_opengl/gl_shader_util.h" 22#include "video_core/renderer_opengl/gl_shader_util.h"
43#include "video_core/renderer_opengl/renderer_opengl.h" 23#include "video_core/renderer_opengl/renderer_opengl.h"
44#include "video_core/smaa_area_tex.h"
45#include "video_core/smaa_search_tex.h"
46#include "video_core/textures/decoders.h" 24#include "video_core/textures/decoders.h"
47 25
48namespace OpenGL { 26namespace OpenGL {
49namespace { 27namespace {
50constexpr GLint PositionLocation = 0;
51constexpr GLint TexCoordLocation = 1;
52constexpr GLint ModelViewMatrixLocation = 0;
53
54struct ScreenRectVertex {
55 constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
56 : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
57
58 std::array<GLfloat, 2> position;
59 std::array<GLfloat, 2> tex_coord;
60};
61
62/**
63 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
64 * corner and (width, height) on the lower-bottom.
65 *
66 * The projection part of the matrix is trivial, hence these operations are represented
67 * by a 3x2 matrix.
68 */
69std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
70 std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
71
72 // clang-format off
73 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
74 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
75 // Last matrix row is implicitly assumed to be [0, 0, 1].
76 // clang-format on
77
78 return matrix;
79}
80
81const char* GetSource(GLenum source) { 28const char* GetSource(GLenum source) {
82 switch (source) { 29 switch (source) {
83 case GL_DEBUG_SOURCE_API: 30 case GL_DEBUG_SOURCE_API:
@@ -148,15 +95,13 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
148 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, 95 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
149 emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, 96 emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_},
150 state_tracker{}, program_manager{device}, 97 state_tracker{}, program_manager{device},
151 rasterizer(emu_window, gpu, device_memory, device, screen_info, program_manager, 98 rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
152 state_tracker) {
153 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { 99 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
154 glEnable(GL_DEBUG_OUTPUT); 100 glEnable(GL_DEBUG_OUTPUT);
155 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 101 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
156 glDebugMessageCallback(DebugHandler, nullptr); 102 glDebugMessageCallback(DebugHandler, nullptr);
157 } 103 }
158 AddTelemetryFields(); 104 AddTelemetryFields();
159 InitOpenGLObjects();
160 105
161 // Initialize default attributes to match hardware's disabled attributes 106 // Initialize default attributes to match hardware's disabled attributes
162 GLint max_attribs{}; 107 GLint max_attribs{};
@@ -168,27 +113,27 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
168 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { 113 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
169 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 114 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
170 } 115 }
171 // Enable unified vertex attributes and query vertex buffer address when the driver supports it 116
117 // Enable unified vertex attributes when the driver supports it
172 if (device.HasVertexBufferUnifiedMemory()) { 118 if (device.HasVertexBufferUnifiedMemory()) {
173 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); 119 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
174 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); 120 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
175 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
176 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
177 &vertex_buffer_address);
178 } 121 }
122 blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
123 program_manager, device);
179} 124}
180 125
181RendererOpenGL::~RendererOpenGL() = default; 126RendererOpenGL::~RendererOpenGL() = default;
182 127
183void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 128void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
184 if (!framebuffer) { 129 if (framebuffers.empty()) {
185 return; 130 return;
186 } 131 }
187 PrepareRendertarget(framebuffer); 132
188 RenderScreenshot(); 133 RenderScreenshot(framebuffers);
189 134
190 state_tracker.BindFramebuffer(0); 135 state_tracker.BindFramebuffer(0);
191 DrawScreen(emu_window.GetFramebufferLayout()); 136 blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout());
192 137
193 ++m_current_frame; 138 ++m_current_frame;
194 139
@@ -199,172 +144,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
199 render_window.OnFrameDisplayed(); 144 render_window.OnFrameDisplayed();
200} 145}
201 146
202void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
203 if (!framebuffer) {
204 return;
205 }
206 // If framebuffer is provided, reload it from memory to a texture
207 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
208 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
209 screen_info.texture.pixel_format != framebuffer->pixel_format ||
210 gl_framebuffer_data.empty()) {
211 // Reallocate texture if the framebuffer size has changed.
212 // This is expected to not happen very often and hence should not be a
213 // performance problem.
214 ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
215 }
216
217 // Load the framebuffer from memory, draw it to the screen, and swap buffers
218 LoadFBToScreenInfo(*framebuffer);
219}
220
221void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
222 // Framebuffer orientation handling
223 framebuffer_transform_flags = framebuffer.transform_flags;
224 framebuffer_crop_rect = framebuffer.crop_rect;
225 framebuffer_width = framebuffer.width;
226 framebuffer_height = framebuffer.height;
227
228 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
229 screen_info.was_accelerated =
230 rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride);
231 if (screen_info.was_accelerated) {
232 return;
233 }
234
235 // Reset the screen info's display texture to its own permanent texture
236 screen_info.display_texture = screen_info.texture.resource.handle;
237
238 // TODO(Rodrigo): Read this from HLE
239 constexpr u32 block_height_log2 = 4;
240 const auto pixel_format{
241 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
242 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
243 const u64 size_in_bytes{Tegra::Texture::CalculateSize(
244 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
245 const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
246 const std::span<const u8> input_data(host_ptr, size_in_bytes);
247 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
248 framebuffer.width, framebuffer.height, 1, block_height_log2,
249 0);
250
251 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
252 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
253
254 // Update existing texture
255 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
256 // they differ from the LCD resolution.
257 // TODO: Applications could theoretically crash yuzu here by specifying too large
258 // framebuffer sizes. We should make sure that this cannot happen.
259 glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width,
260 framebuffer.height, screen_info.texture.gl_format,
261 screen_info.texture.gl_type, gl_framebuffer_data.data());
262
263 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
264}
265
266void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
267 const TextureInfo& texture) {
268 const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r};
269 glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
270}
271
272void RendererOpenGL::InitOpenGLObjects() {
273 // Create shader programs
274 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
275 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
276
277 const auto replace_include = [](std::string& shader_source, std::string_view include_name,
278 std::string_view include_content) {
279 const std::string include_string = fmt::format("#include \"{}\"", include_name);
280 const std::size_t pos = shader_source.find(include_string);
281 ASSERT(pos != std::string::npos);
282 shader_source.replace(pos, include_string.size(), include_content);
283 };
284
285 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
286 std::string shader_source{specialized_source};
287 replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
288 return CreateProgram(shader_source, stage);
289 };
290
291 smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER);
292 smaa_edge_detection_frag =
293 SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER);
294 smaa_blending_weight_calculation_vert =
295 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER);
296 smaa_blending_weight_calculation_frag =
297 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER);
298 smaa_neighborhood_blending_vert =
299 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER);
300 smaa_neighborhood_blending_frag =
301 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);
302
303 present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
304 present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
305 present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER);
306 present_gaussian_fragment =
307 CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER);
308 present_scaleforce_fragment =
309 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
310 GL_FRAGMENT_SHADER);
311
312 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
313 replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
314 replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
315
316 std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
317 std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
318 replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
319 replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
320
321 fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
322 fsr_rcas_frag_source);
323
324 // Generate presentation sampler
325 present_sampler.Create();
326 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
327 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
328 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
329 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
330 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
331
332 present_sampler_nn.Create();
333 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
334 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
335 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
336 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
337 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
338
339 // Generate VBO handle for drawing
340 vertex_buffer.Create();
341
342 // Attach vertex data to VAO
343 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
344
345 // Allocate textures for the screen
346 screen_info.texture.resource.Create(GL_TEXTURE_2D);
347
348 const GLuint texture = screen_info.texture.resource.handle;
349 glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);
350
351 screen_info.display_texture = screen_info.texture.resource.handle;
352
353 // Clear screen to black
354 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture);
355
356 aa_framebuffer.Create();
357
358 smaa_area_tex.Create(GL_TEXTURE_2D);
359 glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);
360 glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG,
361 GL_UNSIGNED_BYTE, areaTexBytes);
362 smaa_search_tex.Create(GL_TEXTURE_2D);
363 glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT);
364 glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED,
365 GL_UNSIGNED_BYTE, searchTexBytes);
366}
367
368void RendererOpenGL::AddTelemetryFields() { 147void RendererOpenGL::AddTelemetryFields() {
369 const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; 148 const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
370 const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; 149 const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
@@ -380,328 +159,7 @@ void RendererOpenGL::AddTelemetryFields() {
380 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); 159 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
381} 160}
382 161
383void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 162void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
384 const Tegra::FramebufferConfig& framebuffer) {
385 texture.width = framebuffer.width;
386 texture.height = framebuffer.height;
387 texture.pixel_format = framebuffer.pixel_format;
388
389 const auto pixel_format{
390 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
391 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
392 gl_framebuffer_data.resize(texture.width * texture.height * bytes_per_pixel);
393
394 GLint internal_format;
395 switch (framebuffer.pixel_format) {
396 case Service::android::PixelFormat::Rgba8888:
397 internal_format = GL_RGBA8;
398 texture.gl_format = GL_RGBA;
399 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
400 break;
401 case Service::android::PixelFormat::Rgb565:
402 internal_format = GL_RGB565;
403 texture.gl_format = GL_RGB;
404 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
405 break;
406 default:
407 internal_format = GL_RGBA8;
408 texture.gl_format = GL_RGBA;
409 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
410 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
411 // static_cast<u32>(framebuffer.pixel_format));
412 break;
413 }
414
415 texture.resource.Release();
416 texture.resource.Create(GL_TEXTURE_2D);
417 glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
418 aa_texture.Release();
419 aa_texture.Create(GL_TEXTURE_2D);
420 glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F,
421 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
422 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
423 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0);
424 smaa_edges_tex.Release();
425 smaa_edges_tex.Create(GL_TEXTURE_2D);
426 glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F,
427 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
428 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
429 smaa_blend_tex.Release();
430 smaa_blend_tex.Create(GL_TEXTURE_2D);
431 glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F,
432 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
433 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
434}
435
436void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
437 // TODO: Signal state tracker about these changes
438 state_tracker.NotifyScreenDrawVertexArray();
439 state_tracker.NotifyPolygonModes();
440 state_tracker.NotifyViewport0();
441 state_tracker.NotifyScissor0();
442 state_tracker.NotifyColorMask(0);
443 state_tracker.NotifyBlend0();
444 state_tracker.NotifyFramebuffer();
445 state_tracker.NotifyFrontFace();
446 state_tracker.NotifyCullTest();
447 state_tracker.NotifyDepthTest();
448 state_tracker.NotifyStencilTest();
449 state_tracker.NotifyPolygonOffset();
450 state_tracker.NotifyRasterizeEnable();
451 state_tracker.NotifyFramebufferSRGB();
452 state_tracker.NotifyLogicOp();
453 state_tracker.NotifyClipControl();
454 state_tracker.NotifyAlphaTest();
455
456 state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
457
458 glEnable(GL_CULL_FACE);
459 glDisable(GL_COLOR_LOGIC_OP);
460 glDisable(GL_DEPTH_TEST);
461 glDisable(GL_STENCIL_TEST);
462 glDisable(GL_POLYGON_OFFSET_FILL);
463 glDisable(GL_RASTERIZER_DISCARD);
464 glDisable(GL_ALPHA_TEST);
465 glDisablei(GL_BLEND, 0);
466 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
467 glCullFace(GL_BACK);
468 glFrontFace(GL_CW);
469 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
470 glDepthRangeIndexed(0, 0.0, 0.0);
471
472 glBindTextureUnit(0, screen_info.display_texture);
473
474 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
475 if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
476 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
477 anti_aliasing = Settings::AntiAliasing::None;
478 Settings::values.anti_aliasing.SetValue(anti_aliasing);
479 }
480
481 if (anti_aliasing != Settings::AntiAliasing::None) {
482 glEnablei(GL_SCISSOR_TEST, 0);
483 auto viewport_width = screen_info.texture.width;
484 auto scissor_width = framebuffer_crop_rect.GetWidth();
485 if (scissor_width <= 0) {
486 scissor_width = viewport_width;
487 }
488 auto viewport_height = screen_info.texture.height;
489 auto scissor_height = framebuffer_crop_rect.GetHeight();
490 if (scissor_height <= 0) {
491 scissor_height = viewport_height;
492 }
493 if (screen_info.was_accelerated) {
494 viewport_width = Settings::values.resolution_info.ScaleUp(viewport_width);
495 scissor_width = Settings::values.resolution_info.ScaleUp(scissor_width);
496 viewport_height = Settings::values.resolution_info.ScaleUp(viewport_height);
497 scissor_height = Settings::values.resolution_info.ScaleUp(scissor_height);
498 }
499 glScissorIndexed(0, 0, 0, scissor_width, scissor_height);
500 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width),
501 static_cast<GLfloat>(viewport_height));
502
503 glBindSampler(0, present_sampler.handle);
504 GLint old_read_fb;
505 GLint old_draw_fb;
506 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
507 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
508
509 switch (anti_aliasing) {
510 case Settings::AntiAliasing::Fxaa: {
511 program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle);
512 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle);
513 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
514 } break;
515 case Settings::AntiAliasing::Smaa: {
516 glClearColor(0, 0, 0, 0);
517 glFrontFace(GL_CCW);
518 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle);
519 glBindSampler(1, present_sampler.handle);
520 glBindSampler(2, present_sampler.handle);
521
522 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
523 smaa_edges_tex.handle, 0);
524 glClear(GL_COLOR_BUFFER_BIT);
525 program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle,
526 smaa_edge_detection_frag.handle);
527 glDrawArrays(GL_TRIANGLES, 0, 3);
528
529 glBindTextureUnit(0, smaa_edges_tex.handle);
530 glBindTextureUnit(1, smaa_area_tex.handle);
531 glBindTextureUnit(2, smaa_search_tex.handle);
532 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
533 smaa_blend_tex.handle, 0);
534 glClear(GL_COLOR_BUFFER_BIT);
535 program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle,
536 smaa_blending_weight_calculation_frag.handle);
537 glDrawArrays(GL_TRIANGLES, 0, 3);
538
539 glBindTextureUnit(0, screen_info.display_texture);
540 glBindTextureUnit(1, smaa_blend_tex.handle);
541 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
542 aa_texture.handle, 0);
543 program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle,
544 smaa_neighborhood_blending_frag.handle);
545 glDrawArrays(GL_TRIANGLES, 0, 3);
546 glFrontFace(GL_CW);
547 } break;
548 default:
549 UNREACHABLE();
550 }
551
552 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
553 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
554
555 glBindTextureUnit(0, aa_texture.handle);
556 }
557 glDisablei(GL_SCISSOR_TEST, 0);
558
559 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
560 if (!fsr->AreBuffersInitialized()) {
561 fsr->InitBuffers();
562 }
563
564 auto crop_rect = framebuffer_crop_rect;
565 if (crop_rect.GetWidth() == 0) {
566 crop_rect.right = framebuffer_width;
567 }
568 if (crop_rect.GetHeight() == 0) {
569 crop_rect.bottom = framebuffer_height;
570 }
571 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
572 const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
573 const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
574 glBindSampler(0, present_sampler.handle);
575 fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
576 } else {
577 if (fsr->AreBuffersInitialized()) {
578 fsr->ReleaseBuffers();
579 }
580 }
581
582 const std::array ortho_matrix =
583 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
584
585 const auto fragment_handle = [this]() {
586 switch (Settings::values.scaling_filter.GetValue()) {
587 case Settings::ScalingFilter::NearestNeighbor:
588 case Settings::ScalingFilter::Bilinear:
589 return present_bilinear_fragment.handle;
590 case Settings::ScalingFilter::Bicubic:
591 return present_bicubic_fragment.handle;
592 case Settings::ScalingFilter::Gaussian:
593 return present_gaussian_fragment.handle;
594 case Settings::ScalingFilter::ScaleForce:
595 return present_scaleforce_fragment.handle;
596 case Settings::ScalingFilter::Fsr:
597 return fsr->GetPresentFragmentProgram().handle;
598 default:
599 return present_bilinear_fragment.handle;
600 }
601 }();
602 program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
603 glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
604 ortho_matrix.data());
605
606 const auto& texcoords = screen_info.display_texcoords;
607 auto left = texcoords.left;
608 auto right = texcoords.right;
609 if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
610 if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
611 // Flip the framebuffer vertically
612 left = texcoords.right;
613 right = texcoords.left;
614 } else {
615 // Other transformations are unsupported
616 LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}",
617 framebuffer_transform_flags);
618 UNIMPLEMENTED();
619 }
620 }
621
622 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
623
624 f32 left_start{};
625 if (framebuffer_crop_rect.Top() > 0) {
626 left_start = static_cast<f32>(framebuffer_crop_rect.Top()) /
627 static_cast<f32>(framebuffer_crop_rect.Bottom());
628 }
629 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
630 f32 scale_v =
631 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
632
633 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
634 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
635 // (e.g. handheld mode) on a 1920x1080 framebuffer.
636 if (framebuffer_crop_rect.GetWidth() > 0) {
637 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
638 static_cast<f32>(screen_info.texture.width);
639 }
640 if (framebuffer_crop_rect.GetHeight() > 0) {
641 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
642 static_cast<f32>(screen_info.texture.height);
643 }
644 }
645 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
646 !screen_info.was_accelerated) {
647 scale_u /= Settings::values.resolution_info.up_factor;
648 scale_v /= Settings::values.resolution_info.up_factor;
649 }
650
651 const auto& screen = layout.screen;
652 const std::array vertices = {
653 ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u,
654 left_start + left * scale_v),
655 ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u,
656 left_start + left * scale_v),
657 ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u,
658 left_start + right * scale_v),
659 ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u,
660 left_start + right * scale_v),
661 };
662 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
663
664 glDisable(GL_FRAMEBUFFER_SRGB);
665 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
666 static_cast<GLfloat>(layout.height));
667
668 glEnableVertexAttribArray(PositionLocation);
669 glEnableVertexAttribArray(TexCoordLocation);
670 glVertexAttribDivisor(PositionLocation, 0);
671 glVertexAttribDivisor(TexCoordLocation, 0);
672 glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
673 offsetof(ScreenRectVertex, position));
674 glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
675 offsetof(ScreenRectVertex, tex_coord));
676 glVertexAttribBinding(PositionLocation, 0);
677 glVertexAttribBinding(TexCoordLocation, 0);
678 if (device.HasVertexBufferUnifiedMemory()) {
679 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
680 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
681 sizeof(vertices));
682 } else {
683 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
684 }
685
686 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
687 glBindSampler(0, present_sampler.handle);
688 } else {
689 glBindSampler(0, present_sampler_nn.handle);
690 }
691
692 // Update background color before drawing
693 glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
694 Settings::values.bg_green.GetValue() / 255.0f,
695 Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
696
697 glClear(GL_COLOR_BUFFER_BIT);
698 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
699
700 // TODO
701 // program_manager.RestoreGuestPipeline();
702}
703
704void RendererOpenGL::RenderScreenshot() {
705 if (!renderer_settings.screenshot_requested) { 163 if (!renderer_settings.screenshot_requested) {
706 return; 164 return;
707 } 165 }
@@ -723,7 +181,7 @@ void RendererOpenGL::RenderScreenshot() {
723 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); 181 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
724 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); 182 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
725 183
726 DrawScreen(layout); 184 blit_screen->DrawScreen(framebuffers, layout);
727 185
728 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 186 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
729 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 187 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 18699610a..c4625c96e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,7 +10,6 @@
10 10
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 12#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_fsr.h"
14#include "video_core/renderer_opengl/gl_rasterizer.h" 13#include "video_core/renderer_opengl/gl_rasterizer.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_manager.h" 15#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -25,37 +24,13 @@ namespace Core::Frontend {
25class EmuWindow; 24class EmuWindow;
26} 25}
27 26
28namespace Core::Memory {
29class Memory;
30}
31
32namespace Layout {
33struct FramebufferLayout;
34}
35
36namespace Tegra { 27namespace Tegra {
37class GPU; 28class GPU;
38} 29}
39 30
40namespace OpenGL { 31namespace OpenGL {
41 32
42/// Structure used for storing information about the textures for the Switch screen 33class BlitScreen;
43struct TextureInfo {
44 OGLTexture resource;
45 GLsizei width;
46 GLsizei height;
47 GLenum gl_format;
48 GLenum gl_type;
49 Service::android::PixelFormat pixel_format;
50};
51
52/// Structure used for storing information about the display target for the Switch screen
53struct ScreenInfo {
54 GLuint display_texture{};
55 bool was_accelerated = false;
56 const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
57 TextureInfo texture;
58};
59 34
60class RendererOpenGL final : public VideoCore::RendererBase { 35class RendererOpenGL final : public VideoCore::RendererBase {
61public: 36public:
@@ -65,7 +40,7 @@ public:
65 std::unique_ptr<Core::Frontend::GraphicsContext> context_); 40 std::unique_ptr<Core::Frontend::GraphicsContext> context_);
66 ~RendererOpenGL() override; 41 ~RendererOpenGL() override;
67 42
68 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 43 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
69 44
70 VideoCore::RasterizerInterface* ReadRasterizer() override { 45 VideoCore::RasterizerInterface* ReadRasterizer() override {
71 return &rasterizer; 46 return &rasterizer;
@@ -76,28 +51,8 @@ public:
76 } 51 }
77 52
78private: 53private:
79 /// Initializes the OpenGL state and creates persistent objects.
80 void InitOpenGLObjects();
81
82 void AddTelemetryFields(); 54 void AddTelemetryFields();
83 55 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
84 void ConfigureFramebufferTexture(TextureInfo& texture,
85 const Tegra::FramebufferConfig& framebuffer);
86
87 /// Draws the emulated screens to the emulator window.
88 void DrawScreen(const Layout::FramebufferLayout& layout);
89
90 void RenderScreenshot();
91
92 /// Loads framebuffer from emulated memory into the active OpenGL texture.
93 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
94
95 /// Fills active OpenGL texture with the given RGB color.Since the color is solid, the texture
96 /// can be 1x1 but will stretch across whatever it's rendered on.
97 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
98 const TextureInfo& texture);
99
100 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
101 56
102 Core::TelemetrySession& telemetry_session; 57 Core::TelemetrySession& telemetry_session;
103 Core::Frontend::EmuWindow& emu_window; 58 Core::Frontend::EmuWindow& emu_window;
@@ -108,49 +63,9 @@ private:
108 StateTracker state_tracker; 63 StateTracker state_tracker;
109 ProgramManager program_manager; 64 ProgramManager program_manager;
110 RasterizerOpenGL rasterizer; 65 RasterizerOpenGL rasterizer;
111
112 // OpenGL object IDs
113 OGLSampler present_sampler;
114 OGLSampler present_sampler_nn;
115 OGLBuffer vertex_buffer;
116 OGLProgram fxaa_vertex;
117 OGLProgram fxaa_fragment;
118 OGLProgram present_vertex;
119 OGLProgram present_bilinear_fragment;
120 OGLProgram present_bicubic_fragment;
121 OGLProgram present_gaussian_fragment;
122 OGLProgram present_scaleforce_fragment;
123 OGLFramebuffer screenshot_framebuffer; 66 OGLFramebuffer screenshot_framebuffer;
124 67
125 // GPU address of the vertex buffer 68 std::unique_ptr<BlitScreen> blit_screen;
126 GLuint64EXT vertex_buffer_address = 0;
127
128 /// Display information for Switch screen
129 ScreenInfo screen_info;
130 OGLTexture aa_texture;
131 OGLFramebuffer aa_framebuffer;
132
133 OGLProgram smaa_edge_detection_vert;
134 OGLProgram smaa_blending_weight_calculation_vert;
135 OGLProgram smaa_neighborhood_blending_vert;
136 OGLProgram smaa_edge_detection_frag;
137 OGLProgram smaa_blending_weight_calculation_frag;
138 OGLProgram smaa_neighborhood_blending_frag;
139 OGLTexture smaa_area_tex;
140 OGLTexture smaa_search_tex;
141 OGLTexture smaa_edges_tex;
142 OGLTexture smaa_blend_tex;
143
144 std::unique_ptr<FSR> fsr;
145
146 /// OpenGL framebuffer data
147 std::vector<u8> gl_framebuffer_data;
148
149 /// Used for transforming the framebuffer orientation
150 Service::android::BufferTransformFlags framebuffer_transform_flags{};
151 Common::Rectangle<int> framebuffer_crop_rect;
152 u32 framebuffer_width;
153 u32 framebuffer_height;
154}; 69};
155 70
156} // namespace OpenGL 71} // namespace OpenGL
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_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 1e1821b10..20f7a9702 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -381,8 +381,9 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
381 .support_float64 = device.IsFloat64Supported(), 381 .support_float64 = device.IsFloat64Supported(),
382 .support_float16 = device.IsFloat16Supported(), 382 .support_float16 = device.IsFloat16Supported(),
383 .support_int64 = device.IsShaderInt64Supported(), 383 .support_int64 = device.IsShaderInt64Supported(),
384 .needs_demote_reorder = 384 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
385 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, 385 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
386 driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY,
386 .support_snorm_render_buffer = true, 387 .support_snorm_render_buffer = true,
387 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 388 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
388 .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), 389 .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
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;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index a7400adfa..a20c956ff 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -713,12 +713,12 @@ bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
713} 713}
714 714
715template <class P> 715template <class P>
716typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( 716std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImageView(
717 const Tegra::FramebufferConfig& config, DAddr cpu_addr) { 717 const Tegra::FramebufferConfig& config, DAddr cpu_addr) {
718 // TODO: Properly implement this 718 // TODO: Properly implement this
719 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); 719 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS);
720 if (it == page_table.end()) { 720 if (it == page_table.end()) {
721 return nullptr; 721 return {};
722 } 722 }
723 const auto& image_map_ids = it->second; 723 const auto& image_map_ids = it->second;
724 boost::container::small_vector<ImageId, 4> valid_image_ids; 724 boost::container::small_vector<ImageId, 4> valid_image_ids;
@@ -747,7 +747,8 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(
747 747
748 const auto GetImageViewForFramebuffer = [&](ImageId image_id) { 748 const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
749 const ImageViewInfo info{ImageViewType::e2D, view_format}; 749 const ImageViewInfo info{ImageViewType::e2D, view_format};
750 return &slot_image_views[FindOrEmplaceImageView(image_id, info)]; 750 return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
751 slot_images[image_id].IsRescaled());
751 }; 752 };
752 753
753 if (valid_image_ids.size() == 1) [[likely]] { 754 if (valid_image_ids.size() == 1) [[likely]] {
@@ -761,7 +762,7 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(
761 return GetImageViewForFramebuffer(*most_recent); 762 return GetImageViewForFramebuffer(*most_recent);
762 } 763 }
763 764
764 return nullptr; 765 return {};
765} 766}
766 767
767template <class P> 768template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index f9aebb293..e7b910121 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -212,8 +212,8 @@ public:
212 const Tegra::Engines::Fermi2D::Config& copy); 212 const Tegra::Engines::Fermi2D::Config& copy);
213 213
214 /// Try to find a cached image view in the given CPU address 214 /// Try to find a cached image view in the given CPU address
215 [[nodiscard]] ImageView* TryFindFramebufferImageView(const Tegra::FramebufferConfig& config, 215 [[nodiscard]] std::pair<ImageView*, bool> TryFindFramebufferImageView(
216 DAddr cpu_addr); 216 const Tegra::FramebufferConfig& config, DAddr cpu_addr);
217 217
218 /// Return true when there are uncommitted images to be downloaded 218 /// Return true when there are uncommitted images to be downloaded
219 [[nodiscard]] bool HasUncommittedFlushes() const noexcept; 219 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 727bbd98d..d7216d349 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -868,6 +868,8 @@ std::string Device::GetDriverName() const {
868 return "Qualcomm"; 868 return "Qualcomm";
869 case VK_DRIVER_ID_ARM_PROPRIETARY: 869 case VK_DRIVER_ID_ARM_PROPRIETARY:
870 return "Mali"; 870 return "Mali";
871 case VK_DRIVER_ID_SAMSUNG_PROPRIETARY:
872 return "Xclipse";
871 case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: 873 case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
872 return "SwiftShader"; 874 return "SwiftShader";
873 case VK_DRIVER_ID_BROADCOM_PROPRIETARY: 875 case VK_DRIVER_ID_BROADCOM_PROPRIETARY: