summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/settings.cpp3
-rw-r--r--src/common/settings.h32
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/content_archive.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp7
-rw-r--r--src/core/hle/service/am/am_types.h6
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.cpp12
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.h2
-rw-r--r--src/core/hle/service/am/library_applet_creator.cpp81
-rw-r--r--src/core/hle/service/am/process.cpp23
-rw-r--r--src/core/hle/service/am/process.h2
-rw-r--r--src/core/hle/service/am/self_controller.cpp18
-rw-r--r--src/core/hle/service/am/self_controller.h2
-rw-r--r--src/core/hle/service/am/system_buffer_manager.cpp19
-rw-r--r--src/core/hle/service/am/system_buffer_manager.h3
-rw-r--r--src/core/hle/service/am/window_controller.cpp4
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp7
-rw-r--r--src/core/hle/service/nvdrv/core/container.h1
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp17
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp176
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.h24
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp1
-rw-r--r--src/core/hle/service/nvnflinger/hwc_layer.h13
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp7
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h13
-rw-r--r--src/frontend_common/config.cpp18
-rw-r--r--src/frontend_common/config.h2
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/capture.h36
-rw-r--r--src/video_core/framebuffer_config.h7
-rw-r--r--src/video_core/gpu.cpp15
-rw-r--r--src/video_core/gpu.h2
-rw-r--r--src/video_core/host_shaders/fidelityfx_fsr.frag21
-rw-r--r--src/video_core/host_shaders/fxaa.frag2
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag19
-rw-r--r--src/video_core/host_shaders/opengl_present.frag2
-rw-r--r--src/video_core/host_shaders/present_bicubic.frag2
-rw-r--r--src/video_core/host_shaders/present_gaussian.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag2
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag2
-rw-r--r--src/video_core/present.h37
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp5
-rw-r--r--src/video_core/renderer_null/renderer_null.h2
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.h7
-rw-r--r--src/video_core/renderer_opengl/present/layer.cpp35
-rw-r--r--src/video_core/renderer_opengl/present/layer.h8
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.cpp19
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp89
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h9
-rw-r--r--src/video_core/renderer_vulkan/present/layer.cpp19
-rw-r--r--src/video_core/renderer_vulkan/present/layer.h6
-rw-r--r--src/video_core/renderer_vulkan/present/util.cpp92
-rw-r--r--src/video_core/renderer_vulkan/present/util.h9
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.cpp29
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.h6
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp114
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h8
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/configure_applets.cpp86
-rw-r--r--src/yuzu/configuration/configure_applets.h48
-rw-r--r--src/yuzu/configuration/configure_applets.ui65
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h2
-rw-r--r--src/yuzu/configuration/shared_translation.cpp22
79 files changed, 1173 insertions, 255 deletions
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 07709d4e5..80d388fe8 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -30,6 +30,7 @@ namespace Settings {
30#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> 30#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
31#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> 31#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
32 32
33SETTING(AppletMode, false);
33SETTING(AudioEngine, false); 34SETTING(AudioEngine, false);
34SETTING(bool, false); 35SETTING(bool, false);
35SETTING(int, false); 36SETTING(int, false);
@@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
215 return "Debugging"; 216 return "Debugging";
216 case Category::GpuDriver: 217 case Category::GpuDriver:
217 return "GpuDriver"; 218 return "GpuDriver";
219 case Category::LibraryApplet:
220 return "LibraryApplet";
218 case Category::Miscellaneous: 221 case Category::Miscellaneous:
219 return "Miscellaneous"; 222 return "Miscellaneous";
220 case Category::Network: 223 case Category::Network:
diff --git a/src/common/settings.h b/src/common/settings.h
index f1b1add56..aa054dc24 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -133,6 +133,38 @@ struct TouchFromButtonMap {
133struct Values { 133struct Values {
134 Linkage linkage{}; 134 Linkage linkage{};
135 135
136 // Applet
137 Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
138 Category::LibraryApplet};
139 Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
140 Category::LibraryApplet};
141 Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
142 Category::LibraryApplet};
143 Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
144 Category::LibraryApplet};
145 Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
146 Category::LibraryApplet};
147 Setting<AppletMode> player_select_applet_mode{
148 linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
149 Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
150 Category::LibraryApplet};
151 Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
152 Category::LibraryApplet};
153 Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
154 Category::LibraryApplet};
155 Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
156 Category::LibraryApplet};
157 Setting<AppletMode> photo_viewer_applet_mode{
158 linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
159 Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
160 Category::LibraryApplet};
161 Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
162 Category::LibraryApplet};
163 Setting<AppletMode> wifi_web_auth_applet_mode{
164 linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
165 Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
166 Category::LibraryApplet};
167
136 // Audio 168 // Audio
137 SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", 169 SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
138 Category::Audio, Specialization::RuntimeList}; 170 Category::Audio, Specialization::RuntimeList};
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 987489e8a..2df3f0809 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -44,6 +44,7 @@ enum class Category : u32 {
44 Services, 44 Services,
45 Paths, 45 Paths,
46 Linux, 46 Linux,
47 LibraryApplet,
47 MaxEnum, 48 MaxEnum,
48}; 49};
49 50
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 617036588..f42367e67 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
151 151
152ENUM(ConsoleMode, Handheld, Docked); 152ENUM(ConsoleMode, Handheld, Docked);
153 153
154ENUM(AppletMode, HLE, LLE);
155
154template <typename Type> 156template <typename Type>
155inline std::string CanonicalizeEnum(Type id) { 157inline std::string CanonicalizeEnum(Type id) {
156 const auto group = EnumMetadata<Type>::Canonicalizations(); 158 const auto group = EnumMetadata<Type>::Canonicalizations();
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 285fe4db6..665252358 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const {
172 return reader->GetSdkAddonVersion(); 172 return reader->GetSdkAddonVersion();
173} 173}
174 174
175u8 NCA::GetKeyGeneration() const {
176 return reader->GetKeyGeneration();
177}
178
175bool NCA::IsUpdate() const { 179bool NCA::IsUpdate() const {
176 return is_update; 180 return is_update;
177} 181}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f68464eb0..8560617f5 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -77,6 +77,7 @@ public:
77 u64 GetTitleId() const; 77 u64 GetTitleId() const;
78 RightsId GetRightsId() const; 78 RightsId GetRightsId() const;
79 u32 GetSDKVersion() const; 79 u32 GetSDKVersion() const;
80 u8 GetKeyGeneration() const;
80 bool IsUpdate() const; 81 bool IsUpdate() const;
81 82
82 VirtualFile GetRomFS() const; 83 VirtualFile GetRomFS() const;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 0b08e877e..1bcc42890 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -4,8 +4,9 @@
4#include <random> 4#include <random>
5#include "common/scope_exit.h" 5#include "common/scope_exit.h"
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/arm/dynarmic/arm_dynarmic.h"
8#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/gpu_dirty_memory_manager.h"
9#include "core/hle/kernel/k_process.h" 10#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_scoped_resource_reservation.h" 11#include "core/hle/kernel/k_scoped_resource_reservation.h"
11#include "core/hle/kernel/k_shared_memory.h" 12#include "core/hle/kernel/k_shared_memory.h"
@@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() {
1258 1259
1259#ifdef HAS_NCE 1260#ifdef HAS_NCE
1260 if (this->IsApplication() && Settings::IsNceEnabled()) { 1261 if (this->IsApplication() && Settings::IsNceEnabled()) {
1262 // Register the scoped JIT handler before creating any NCE instances
1263 // so that its signal handler will appear first in the signal chain.
1264 Core::ScopedJitExecution::RegisterHandler();
1265
1261 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1266 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1262 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); 1267 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
1263 } 1268 }
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h
index a2b852b12..8c33feb15 100644
--- a/src/core/hle/service/am/am_types.h
+++ b/src/core/hle/service/am/am_types.h
@@ -130,9 +130,9 @@ enum class AppletProgramId : u64 {
130 130
131enum class LibraryAppletMode : u32 { 131enum class LibraryAppletMode : u32 {
132 AllForeground = 0, 132 AllForeground = 0,
133 Background = 1, 133 PartialForeground = 1,
134 NoUI = 2, 134 NoUi = 2,
135 BackgroundIndirectDisplay = 3, 135 PartialForegroundIndirectDisplay = 3,
136 AllForegroundInitiallyHidden = 4, 136 AllForegroundInitiallyHidden = 4,
137}; 137};
138 138
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
index fbf75d379..034c62f32 100644
--- a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
@@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
68 case LibraryAppletMode::AllForeground: 68 case LibraryAppletMode::AllForeground:
69 InitializeForeground(); 69 InitializeForeground();
70 break; 70 break;
71 case LibraryAppletMode::Background: 71 case LibraryAppletMode::PartialForeground:
72 case LibraryAppletMode::BackgroundIndirectDisplay: 72 case LibraryAppletMode::PartialForegroundIndirectDisplay:
73 InitializeBackground(applet_mode); 73 InitializePartialForeground(applet_mode);
74 break; 74 break;
75 default: 75 default:
76 ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); 76 ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
243 InitializeFrontendNormalKeyboard(); 243 InitializeFrontendNormalKeyboard();
244} 244}
245 245
246void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { 246void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
247 LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); 247 LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
248 248
249 is_background = true; 249 is_background = true;
@@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
258 swkbd_inline_initialize_arg.size()); 258 swkbd_inline_initialize_arg.size());
259 259
260 if (swkbd_initialize_arg.library_applet_mode_flag) { 260 if (swkbd_initialize_arg.library_applet_mode_flag) {
261 ASSERT(library_applet_mode == LibraryAppletMode::Background); 261 ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
262 } else { 262 } else {
263 ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); 263 ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
264 } 264 }
265} 265}
266 266
diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h
index f464b7e15..2a7d01b96 100644
--- a/src/core/hle/service/am/frontend/applet_software_keyboard.h
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h
@@ -62,7 +62,7 @@ private:
62 void InitializeForeground(); 62 void InitializeForeground();
63 63
64 /// Initializes the inline software keyboard. 64 /// Initializes the inline software keyboard.
65 void InitializeBackground(LibraryAppletMode library_applet_mode); 65 void InitializePartialForeground(LibraryAppletMode library_applet_mode);
66 66
67 /// Processes the text check sent by the application. 67 /// Processes the text check sent by the application.
68 void ProcessTextCheck(); 68 void ProcessTextCheck();
diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp
index 47bab7528..00d5a0705 100644
--- a/src/core/hle/service/am/library_applet_creator.cpp
+++ b/src/core/hle/service/am/library_applet_creator.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 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 "common/settings.h"
4#include "core/hle/kernel/k_transfer_memory.h" 5#include "core/hle/kernel/k_transfer_memory.h"
5#include "core/hle/service/am/applet_data_broker.h" 6#include "core/hle/service/am/applet_data_broker.h"
6#include "core/hle/service/am/applet_manager.h" 7#include "core/hle/service/am/applet_manager.h"
@@ -16,6 +17,34 @@ namespace Service::AM {
16 17
17namespace { 18namespace {
18 19
20bool ShouldCreateGuestApplet(AppletId applet_id) {
21#define X(Name, name) \
22 if (applet_id == AppletId::Name && \
23 Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \
24 return false; \
25 }
26
27 X(Cabinet, cabinet)
28 X(Controller, controller)
29 X(DataErase, data_erase)
30 X(Error, error)
31 X(NetConnect, net_connect)
32 X(ProfileSelect, player_select)
33 X(SoftwareKeyboard, swkbd)
34 X(MiiEdit, mii_edit)
35 X(Web, web)
36 X(Shop, shop)
37 X(PhotoViewer, photo_viewer)
38 X(OfflineWeb, offline_web)
39 X(LoginShare, login_share)
40 X(WebAuth, wifi_web_auth)
41 X(MyPage, my_page)
42
43#undef X
44
45 return true;
46}
47
19AppletProgramId AppletIdToProgramId(AppletId applet_id) { 48AppletProgramId AppletIdToProgramId(AppletId applet_id) {
20 switch (applet_id) { 49 switch (applet_id) {
21 case AppletId::OverlayDisplay: 50 case AppletId::OverlayDisplay:
@@ -63,17 +92,26 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
63 } 92 }
64} 93}
65 94
66[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet( 95std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
67 Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, 96 std::shared_ptr<Applet> caller_applet,
68 LibraryAppletMode mode) { 97 AppletId applet_id,
98 LibraryAppletMode mode) {
69 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); 99 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
70 if (program_id == 0) { 100 if (program_id == 0) {
71 // Unknown applet 101 // Unknown applet
72 return {}; 102 return {};
73 } 103 }
74 104
105 // TODO: enable other versions of applets
106 enum : u8 {
107 Firmware1400 = 14,
108 Firmware1500 = 15,
109 Firmware1600 = 16,
110 Firmware1700 = 17,
111 };
112
75 auto process = std::make_unique<Process>(system); 113 auto process = std::make_unique<Process>(system);
76 if (!process->Initialize(program_id)) { 114 if (!process->Initialize(program_id, Firmware1400, Firmware1700)) {
77 // Couldn't initialize the guest process 115 // Couldn't initialize the guest process
78 return {}; 116 return {};
79 } 117 }
@@ -87,24 +125,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
87 // Set focus state 125 // Set focus state
88 switch (mode) { 126 switch (mode) {
89 case LibraryAppletMode::AllForeground: 127 case LibraryAppletMode::AllForeground:
90 case LibraryAppletMode::NoUI: 128 case LibraryAppletMode::NoUi:
91 applet->focus_state = FocusState::InFocus; 129 case LibraryAppletMode::PartialForeground:
130 case LibraryAppletMode::PartialForegroundIndirectDisplay:
92 applet->hid_registration.EnableAppletToGetInput(true); 131 applet->hid_registration.EnableAppletToGetInput(true);
132 applet->focus_state = FocusState::InFocus;
93 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); 133 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
94 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
95 break; 134 break;
96 case LibraryAppletMode::AllForegroundInitiallyHidden: 135 case LibraryAppletMode::AllForegroundInitiallyHidden:
97 applet->system_buffer_manager.SetWindowVisibility(false);
98 applet->focus_state = FocusState::NotInFocus;
99 applet->hid_registration.EnableAppletToGetInput(false); 136 applet->hid_registration.EnableAppletToGetInput(false);
100 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); 137 applet->focus_state = FocusState::NotInFocus;
101 break; 138 applet->system_buffer_manager.SetWindowVisibility(false);
102 case LibraryAppletMode::Background: 139 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
103 case LibraryAppletMode::BackgroundIndirectDisplay:
104 default:
105 applet->focus_state = FocusState::Background;
106 applet->hid_registration.EnableAppletToGetInput(true);
107 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
108 break; 140 break;
109 } 141 }
110 142
@@ -117,9 +149,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
117 return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); 149 return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
118} 150}
119 151
120[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet( 152std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
121 Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, 153 std::shared_ptr<Applet> caller_applet,
122 LibraryAppletMode mode) { 154 AppletId applet_id,
155 LibraryAppletMode mode) {
123 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); 156 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
124 157
125 auto process = std::make_unique<Process>(system); 158 auto process = std::make_unique<Process>(system);
@@ -163,7 +196,13 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
163 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, 196 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
164 applet_mode); 197 applet_mode);
165 198
166 auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); 199 std::shared_ptr<ILibraryAppletAccessor> library_applet;
200 if (ShouldCreateGuestApplet(applet_id)) {
201 library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode);
202 }
203 if (!library_applet) {
204 library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
205 }
167 if (!library_applet) { 206 if (!library_applet) {
168 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); 207 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
169 208
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp
index 16b685f86..992c50713 100644
--- a/src/core/hle/service/am/process.cpp
+++ b/src/core/hle/service/am/process.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5 5
6#include "core/file_sys/content_archive.h"
6#include "core/file_sys/nca_metadata.h" 7#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/registered_cache.h" 8#include "core/file_sys/registered_cache.h"
8#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
@@ -20,7 +21,7 @@ Process::~Process() {
20 this->Finalize(); 21 this->Finalize();
21} 22}
22 23
23bool Process::Initialize(u64 program_id) { 24bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
24 // First, ensure we are not holding another process. 25 // First, ensure we are not holding another process.
25 this->Finalize(); 26 this->Finalize();
26 27
@@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) {
29 30
30 // Attempt to load program NCA. 31 // Attempt to load program NCA.
31 const FileSys::RegisteredCache* bis_system{}; 32 const FileSys::RegisteredCache* bis_system{};
32 FileSys::VirtualFile nca{}; 33 FileSys::VirtualFile nca_raw{};
33 34
34 // Get the program NCA from built-in storage. 35 // Get the program NCA from built-in storage.
35 bis_system = fsc.GetSystemNANDContents(); 36 bis_system = fsc.GetSystemNANDContents();
36 if (bis_system) { 37 if (bis_system) {
37 nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); 38 nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
38 } 39 }
39 40
40 // Ensure we retrieved a program NCA. 41 // Ensure we retrieved a program NCA.
41 if (!nca) { 42 if (!nca_raw) {
42 return false; 43 return false;
43 } 44 }
44 45
46 // Ensure we have a suitable version.
47 if (minimum_key_generation > 0) {
48 FileSys::NCA nca(nca_raw);
49 if (nca.GetStatus() == Loader::ResultStatus::Success &&
50 (nca.GetKeyGeneration() < minimum_key_generation ||
51 nca.GetKeyGeneration() > maximum_key_generation)) {
52 LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
53 nca.GetKeyGeneration());
54 return false;
55 }
56 }
57
45 // Get the appropriate loader to parse this NCA. 58 // Get the appropriate loader to parse this NCA.
46 auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0); 59 auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0);
47 60
48 // Ensure we have a loader which can parse the NCA. 61 // Ensure we have a loader which can parse the NCA.
49 if (!app_loader) { 62 if (!app_loader) {
diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h
index 4b908ade4..4b8102fb6 100644
--- a/src/core/hle/service/am/process.h
+++ b/src/core/hle/service/am/process.h
@@ -21,7 +21,7 @@ public:
21 explicit Process(Core::System& system); 21 explicit Process(Core::System& system);
22 ~Process(); 22 ~Process();
23 23
24 bool Initialize(u64 program_id); 24 bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
25 void Finalize(); 25 void Finalize();
26 26
27 bool Run(); 27 bool Run();
diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp
index 0289f5cf1..65e249c0c 100644
--- a/src/core/hle/service/am/self_controller.cpp
+++ b/src/core/hle/service/am/self_controller.cpp
@@ -1,10 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2024 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 "common/logging/log.h"
5#include "core/hle/result.h"
4#include "core/hle/service/am/am_results.h" 6#include "core/hle/service/am/am_results.h"
5#include "core/hle/service/am/frontend/applets.h" 7#include "core/hle/service/am/frontend/applets.h"
6#include "core/hle/service/am/self_controller.h" 8#include "core/hle/service/am/self_controller.h"
7#include "core/hle/service/caps/caps_su.h" 9#include "core/hle/service/caps/caps_su.h"
10#include "core/hle/service/hle_ipc.h"
8#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 12#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
10#include "core/hle/service/nvnflinger/nvnflinger.h" 13#include "core/hle/service/nvnflinger/nvnflinger.h"
@@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
47 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, 50 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
48 {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, 51 {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
49 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, 52 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
50 {61, nullptr, "SetMediaPlaybackState"}, 53 {61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
51 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, 54 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
52 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, 55 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
53 {64, nullptr, "SetInputDetectionSourceSet"}, 56 {64, nullptr, "SetInputDetectionSourceSet"},
@@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
288} 291}
289 292
290Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { 293Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
291 if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) { 294 if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id,
295 applet->library_applet_mode)) {
292 return ResultSuccess; 296 return ResultSuccess;
293 } 297 }
294 298
@@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
323 rb.Push(ResultSuccess); 327 rb.Push(ResultSuccess);
324} 328}
325 329
330void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) {
331 IPC::RequestParser rp{ctx};
332 const u8 state = rp.Pop<u8>();
333
334 LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
335
336 IPC::ResponseBuilder rb{ctx, 2};
337 rb.Push(ResultSuccess);
338}
339
326void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { 340void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
327 IPC::RequestParser rp{ctx}; 341 IPC::RequestParser rp{ctx};
328 342
diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h
index a63bc2e74..ab21a1881 100644
--- a/src/core/hle/service/am/self_controller.h
+++ b/src/core/hle/service/am/self_controller.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/hle_ipc.h"
6#include "core/hle/service/kernel_helpers.h" 7#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
@@ -38,6 +39,7 @@ private:
38 void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); 39 void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
39 void SetHandlesRequestToDisplay(HLERequestContext& ctx); 40 void SetHandlesRequestToDisplay(HLERequestContext& ctx);
40 void ApproveToDisplay(HLERequestContext& ctx); 41 void ApproveToDisplay(HLERequestContext& ctx);
42 void SetMediaPlaybackState(HLERequestContext& ctx);
41 void SetIdleTimeDetectionExtension(HLERequestContext& ctx); 43 void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
42 void GetIdleTimeDetectionExtension(HLERequestContext& ctx); 44 void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
43 void ReportUserIsActive(HLERequestContext& ctx); 45 void ReportUserIsActive(HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp
index 60a9afc9d..48923fe41 100644
--- a/src/core/hle/service/am/system_buffer_manager.cpp
+++ b/src/core/hle/service/am/system_buffer_manager.cpp
@@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() {
17 17
18 // Clean up shared layers. 18 // Clean up shared layers.
19 if (m_buffer_sharing_enabled) { 19 if (m_buffer_sharing_enabled) {
20 m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
20 } 21 }
21} 22}
22 23
23bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, 24bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
24 AppletId applet_id) { 25 AppletId applet_id, LibraryAppletMode mode) {
25 if (m_nvnflinger) { 26 if (m_nvnflinger) {
26 return m_buffer_sharing_enabled; 27 return m_buffer_sharing_enabled;
27 } 28 }
@@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
36 return false; 37 return false;
37 } 38 }
38 39
40 Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
41 if (mode == LibraryAppletMode::PartialForeground ||
42 mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
43 blending = Nvnflinger::LayerBlending::Coverage;
44 }
45
39 const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); 46 const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
40 const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( 47 const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
41 &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); 48 m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
42 49
43 if (res.IsSuccess()) { 50 if (res.IsSuccess()) {
44 m_buffer_sharing_enabled = true; 51 m_buffer_sharing_enabled = true;
@@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {
62 69
63Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, 70Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
64 s32* out_fbshare_layer_index) { 71 s32* out_fbshare_layer_index) {
65 // TODO 72 if (!m_buffer_sharing_enabled) {
66 R_SUCCEED(); 73 return VI::ResultPermissionDenied;
74 }
75
76 return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
77 out_fbshare_layer_index);
67} 78}
68 79
69} // namespace Service::AM 80} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h
index 98c3cf055..0690f68b6 100644
--- a/src/core/hle/service/am/system_buffer_manager.h
+++ b/src/core/hle/service/am/system_buffer_manager.h
@@ -27,7 +27,8 @@ public:
27 SystemBufferManager(); 27 SystemBufferManager();
28 ~SystemBufferManager(); 28 ~SystemBufferManager();
29 29
30 bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); 30 bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
31 LibraryAppletMode mode);
31 32
32 void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, 33 void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
33 u64* out_system_shared_layer_id) { 34 u64* out_system_shared_layer_id) {
diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp
index f00957f83..c07ef228b 100644
--- a/src/core/hle/service/am/window_controller.cpp
+++ b/src/core/hle/service/am/window_controller.cpp
@@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
62 applet->hid_registration.EnableAppletToGetInput(visible); 62 applet->hid_registration.EnableAppletToGetInput(visible);
63 63
64 if (visible) { 64 if (visible) {
65 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
66 applet->focus_state = FocusState::InFocus; 65 applet->focus_state = FocusState::InFocus;
66 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
67 } else { 67 } else {
68 applet->focus_state = FocusState::NotInFocus; 68 applet->focus_state = FocusState::NotInFocus;
69 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
69 } 70 }
70 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
71 71
72 IPC::ResponseBuilder rb{ctx, 2}; 72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(ResultSuccess); 73 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
index e89cca6f2..9edce03f6 100644
--- a/src/core/hle/service/nvdrv/core/container.cpp
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
49 continue; 49 continue;
50 } 50 }
51 if (session.process == process) { 51 if (session.process == process) {
52 session.ref_count++;
52 return session.id; 53 return session.id;
53 } 54 }
54 } 55 }
@@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
66 } 67 }
67 auto& session = impl->sessions[new_id]; 68 auto& session = impl->sessions[new_id];
68 session.is_active = true; 69 session.is_active = true;
70 session.ref_count = 1;
69 // Optimization 71 // Optimization
70 if (process->IsApplication()) { 72 if (process->IsApplication()) {
71 auto& page_table = process->GetPageTable().GetBasePageTable(); 73 auto& page_table = process->GetPageTable().GetBasePageTable();
@@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
114 116
115void Container::CloseSession(SessionId session_id) { 117void Container::CloseSession(SessionId session_id) {
116 std::scoped_lock lk(impl->session_guard); 118 std::scoped_lock lk(impl->session_guard);
117 impl->file.UnmapAllHandles(session_id);
118 auto& session = impl->sessions[session_id.id]; 119 auto& session = impl->sessions[session_id.id];
120 if (--session.ref_count > 0) {
121 return;
122 }
123 impl->file.UnmapAllHandles(session_id);
119 auto& smmu = impl->host1x.MemoryManager(); 124 auto& smmu = impl->host1x.MemoryManager();
120 if (session.has_preallocated_area) { 125 if (session.has_preallocated_area) {
121 const DAddr region_start = session.mapper->GetRegionStart(); 126 const DAddr region_start = session.mapper->GetRegionStart();
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h
index b4d3938a8..f159ced09 100644
--- a/src/core/hle/service/nvdrv/core/container.h
+++ b/src/core/hle/service/nvdrv/core/container.h
@@ -46,6 +46,7 @@ struct Session {
46 bool has_preallocated_area{}; 46 bool has_preallocated_area{};
47 std::unique_ptr<HeapMapper> mapper{}; 47 std::unique_ptr<HeapMapper> mapper{};
48 bool is_active{}; 48 bool is_active{};
49 s32 ref_count{};
49}; 50};
50 51
51class Container { 52class Container {
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
index bc1c033c6..453cb5831 100644
--- a/src/core/hle/service/nvdrv/core/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
333 }(); 333 }();
334 334
335 for (auto& [id, handle] : handles_copy) { 335 for (auto& [id, handle] : handles_copy) {
336 if (handle->session_id.id == session_id.id) { 336 {
337 FreeHandle(id, false); 337 std::scoped_lock lk{handle->mutex};
338 if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
339 continue;
340 }
338 } 341 }
342 FreeHandle(id, false);
339 } 343 }
340} 344}
341 345
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index abe95303e..995646e25 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -15,6 +15,22 @@
15 15
16namespace Service::Nvidia::Devices { 16namespace Service::Nvidia::Devices {
17 17
18namespace {
19
20Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) {
21 switch (blending) {
22 case Service::Nvnflinger::LayerBlending::None:
23 default:
24 return Tegra::BlendMode::Opaque;
25 case Service::Nvnflinger::LayerBlending::Premultiplied:
26 return Tegra::BlendMode::Premultiplied;
27 case Service::Nvnflinger::LayerBlending::Coverage:
28 return Tegra::BlendMode::Coverage;
29 }
30}
31
32} // namespace
33
18nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) 34nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
19 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} 35 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
20nvdisp_disp0::~nvdisp_disp0() = default; 36nvdisp_disp0::~nvdisp_disp0() = default;
@@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
56 .pixel_format = layer.format, 72 .pixel_format = layer.format,
57 .transform_flags = layer.transform, 73 .transform_flags = layer.transform,
58 .crop_rect = layer.crop_rect, 74 .crop_rect = layer.crop_rect,
75 .blending = ConvertBlending(layer.blending),
59 }); 76 });
60 77
61 for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { 78 for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index e71652cdf..90f7248a0 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -14,24 +14,20 @@
14#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 14#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
15#include "core/hle/service/vi/layer/vi_layer.h" 15#include "core/hle/service/vi/layer/vi_layer.h"
16#include "core/hle/service/vi/vi_results.h" 16#include "core/hle/service/vi/vi_results.h"
17#include "video_core/gpu.h"
18#include "video_core/host1x/host1x.h"
17 19
18namespace Service::Nvnflinger { 20namespace Service::Nvnflinger {
19 21
20namespace { 22namespace {
21 23
22Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, 24Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
23 std::unique_ptr<Kernel::KPageGroup>* out_page_group, 25 Core::System& system, u32 size) {
24 Core::System& system, u32 size) {
25 using Core::Memory::YUZU_PAGESIZE; 26 using Core::Memory::YUZU_PAGESIZE;
26 27
27 // Allocate memory for the system shared buffer. 28 // Allocate memory for the system shared buffer.
28 // FIXME: Because the gmmu can only point to cpu addresses, we need
29 // to map this in the application space to allow it to be used.
30 // FIXME: Add proper smmu emulation.
31 // FIXME: This memory belongs to vi's .data section. 29 // FIXME: This memory belongs to vi's .data section.
32 auto& kernel = system.Kernel(); 30 auto& kernel = system.Kernel();
33 auto* process = system.ApplicationProcess();
34 auto& page_table = process->GetPageTable();
35 31
36 // Hold a temporary page group reference while we try to map it. 32 // Hold a temporary page group reference while we try to map it.
37 auto pg = std::make_unique<Kernel::KPageGroup>( 33 auto pg = std::make_unique<Kernel::KPageGroup>(
@@ -43,6 +39,30 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
43 Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, 39 Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
44 Kernel::KMemoryManager::Direction::FromBack))); 40 Kernel::KMemoryManager::Direction::FromBack)));
45 41
42 // Fill the output data with red.
43 for (auto& block : *pg) {
44 u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
45 u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
46
47 for (; start < end; start++) {
48 *start = 0xFF0000FF;
49 }
50 }
51
52 // Return the mapped page group.
53 *out_page_group = std::move(pg);
54
55 // We succeeded.
56 R_SUCCEED();
57}
58
59Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
60 std::unique_ptr<Kernel::KPageGroup>& pg,
61 Kernel::KProcess* process, Core::System& system) {
62 using Core::Memory::YUZU_PAGESIZE;
63
64 auto& page_table = process->GetPageTable();
65
46 // Get bounds of where mapping is possible. 66 // Get bounds of where mapping is possible.
47 const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); 67 const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
48 const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; 68 const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
@@ -64,9 +84,6 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
64 // Return failure, if necessary 84 // Return failure, if necessary
65 R_UNLESS(i < 64, res); 85 R_UNLESS(i < 64, res);
66 86
67 // Return the mapped page group.
68 *out_page_group = std::move(pg);
69
70 // We succeeded. 87 // We succeeded.
71 R_SUCCEED(); 88 R_SUCCEED();
72} 89}
@@ -135,6 +152,13 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
135 R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); 152 R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
136} 153}
137 154
155void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
156 auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
157 ASSERT(nvmap != nullptr);
158
159 R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
160}
161
138constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; 162constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
139constexpr u32 SharedBufferBlockLinearBpp = 4; 163constexpr u32 SharedBufferBlockLinearBpp = 4;
140 164
@@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli
186 210
187FbShareBufferManager::~FbShareBufferManager() = default; 211FbShareBufferManager::~FbShareBufferManager() = default;
188 212
189Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { 213Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id,
214 u64* out_layer_handle, u64 display_id,
215 LayerBlending blending) {
190 std::scoped_lock lk{m_guard}; 216 std::scoped_lock lk{m_guard};
191 217
192 // Ensure we have not already created a buffer. 218 // Ensure we haven't already created.
193 R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); 219 const u64 aruid = owner_process->GetProcessId();
220 R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
221
222 // Allocate memory for the shared buffer if needed.
223 if (!m_buffer_page_group) {
224 R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
225 SharedBufferSize));
226
227 // Record buffer id.
228 m_buffer_id = m_next_buffer_id++;
229
230 // Record display id.
231 m_display_id = display_id;
232 }
233
234 // Map into process.
235 Common::ProcessAddress map_address{};
236 R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
237 owner_process, m_system));
194 238
195 // Allocate memory and space for the shared buffer. 239 // Create new session.
196 Common::ProcessAddress map_address; 240 auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{});
197 R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), 241 auto& session = it->second;
198 std::addressof(m_buffer_page_group), m_system,
199 SharedBufferSize));
200 242
201 auto& container = m_nvdrv->GetContainer(); 243 auto& container = m_nvdrv->GetContainer();
202 m_session_id = container.OpenSession(m_system.ApplicationProcess()); 244 session.session_id = container.OpenSession(owner_process);
203 m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id); 245 session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
204 246
205 // Create an nvmap handle for the buffer and assign the memory to it. 247 // Create an nvmap handle for the buffer and assign the memory to it.
206 R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd, 248 R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
207 map_address, SharedBufferSize)); 249 session.nvmap_fd, map_address, SharedBufferSize));
208
209 // Record the display id.
210 m_display_id = display_id;
211 250
212 // Create and open a layer for the display. 251 // Create and open a layer for the display.
213 m_layer_id = m_flinger.CreateLayer(m_display_id).value(); 252 session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value();
214 m_flinger.OpenLayer(m_layer_id); 253 m_flinger.OpenLayer(session.layer_id);
215
216 // Set up the buffer.
217 m_buffer_id = m_next_buffer_id++;
218 254
219 // Get the layer. 255 // Get the layer.
220 VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); 256 VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id);
221 ASSERT(layer != nullptr); 257 ASSERT(layer != nullptr);
222 258
223 // Get the producer and set preallocated buffers. 259 // Get the producer and set preallocated buffers.
224 auto& producer = layer->GetBufferQueue(); 260 auto& producer = layer->GetBufferQueue();
225 MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); 261 MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
226 MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); 262 MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
227 263
228 // Assign outputs. 264 // Assign outputs.
229 *out_buffer_id = m_buffer_id; 265 *out_buffer_id = m_buffer_id;
230 *out_layer_id = m_layer_id; 266 *out_layer_handle = session.layer_id;
231 267
232 // We succeeded. 268 // We succeeded.
233 R_SUCCEED(); 269 R_SUCCEED();
234} 270}
235 271
272void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
273 std::scoped_lock lk{m_guard};
274
275 if (m_buffer_id == 0) {
276 return;
277 }
278
279 const u64 aruid = owner_process->GetProcessId();
280 const auto it = m_sessions.find(aruid);
281 if (it == m_sessions.end()) {
282 return;
283 }
284
285 auto& session = it->second;
286
287 // Destroy the layer.
288 m_flinger.DestroyLayer(session.layer_id);
289
290 // Close nvmap handle.
291 FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
292
293 // Close nvmap device.
294 m_nvdrv->Close(session.nvmap_fd);
295
296 // Close session.
297 auto& container = m_nvdrv->GetContainer();
298 container.CloseSession(session.session_id);
299
300 // Erase.
301 m_sessions.erase(it);
302}
303
236Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, 304Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
237 s32* out_nvmap_handle, 305 s32* out_nvmap_handle,
238 SharedMemoryPoolLayout* out_pool_layout, 306 SharedMemoryPoolLayout* out_pool_layout,
@@ -242,17 +310,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
242 310
243 R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); 311 R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
244 R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); 312 R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
313 R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);
245 314
246 *out_pool_layout = SharedBufferPoolLayout; 315 *out_pool_layout = SharedBufferPoolLayout;
247 *out_buffer_size = SharedBufferSize; 316 *out_buffer_size = SharedBufferSize;
248 *out_nvmap_handle = m_buffer_nvmap_handle; 317 *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;
249 318
250 R_SUCCEED(); 319 R_SUCCEED();
251} 320}
252 321
253Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { 322Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
254 // Ensure the layer id is valid. 323 // Ensure the layer id is valid.
255 R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); 324 R_UNLESS(layer_id > 0, VI::ResultNotFound);
256 325
257 // Get the layer. 326 // Get the layer.
258 VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); 327 VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
@@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
309 android::Status::NoError, 378 android::Status::NoError,
310 VI::ResultOperationFailed); 379 VI::ResultOperationFailed);
311 380
381 ON_RESULT_FAILURE {
382 producer.CancelBuffer(static_cast<s32>(slot), fence);
383 };
384
312 // Queue the buffer to the producer. 385 // Queue the buffer to the producer.
313 android::QueueBufferInput input{}; 386 android::QueueBufferInput input{};
314 android::QueueBufferOutput output{}; 387 android::QueueBufferOutput output{};
@@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab
342 R_SUCCEED(); 415 R_SUCCEED();
343} 416}
344 417
418Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
419 std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
420 Common::ScratchBuffer<u32> scratch;
421
422 // TODO: this could be optimized
423 s64 e = -1280 * 768 * 4;
424 for (auto& block : *m_buffer_page_group) {
425 u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
426 u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
427
428 for (; start < end; start++) {
429 *start = 0;
430
431 if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
432 *start = capture_buffer[e];
433 }
434 e++;
435 }
436
437 m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
438 m_system.GPU().InvalidateRegion(addr, end - start);
439 });
440 }
441
442 *out_was_written = true;
443 *out_layer_index = 1;
444 R_SUCCEED();
445}
446
345} // namespace Service::Nvnflinger 447} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
index 033bf4bbe..b79a7d23a 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
@@ -3,9 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <map>
7
6#include "common/math_util.h" 8#include "common/math_util.h"
7#include "core/hle/service/nvdrv/core/container.h" 9#include "core/hle/service/nvdrv/core/container.h"
8#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
11#include "core/hle/service/nvnflinger/hwc_layer.h"
9#include "core/hle/service/nvnflinger/nvnflinger.h" 12#include "core/hle/service/nvnflinger/nvnflinger.h"
10#include "core/hle/service/nvnflinger/ui/fence.h" 13#include "core/hle/service/nvnflinger/ui/fence.h"
11 14
@@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout {
29}; 32};
30static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); 33static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
31 34
35struct FbShareSession;
36
32class FbShareBufferManager final { 37class FbShareBufferManager final {
33public: 38public:
34 explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, 39 explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
35 std::shared_ptr<Nvidia::Module> nvdrv); 40 std::shared_ptr<Nvidia::Module> nvdrv);
36 ~FbShareBufferManager(); 41 ~FbShareBufferManager();
37 42
38 Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); 43 Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
44 u64 display_id, LayerBlending blending);
45 void Finalize(Kernel::KProcess* owner_process);
46
39 Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, 47 Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
40 SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, 48 SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
41 u64 applet_resource_user_id); 49 u64 applet_resource_user_id);
@@ -45,6 +53,8 @@ public:
45 u32 transform, s32 swap_interval, u64 layer_id, s64 slot); 53 u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
46 Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); 54 Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
47 55
56 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
57
48private: 58private:
49 Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); 59 Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
50 60
@@ -52,11 +62,8 @@ private:
52 u64 m_next_buffer_id = 1; 62 u64 m_next_buffer_id = 1;
53 u64 m_display_id = 0; 63 u64 m_display_id = 0;
54 u64 m_buffer_id = 0; 64 u64 m_buffer_id = 0;
55 u64 m_layer_id = 0;
56 u32 m_buffer_nvmap_handle = 0;
57 SharedMemoryPoolLayout m_pool_layout = {}; 65 SharedMemoryPoolLayout m_pool_layout = {};
58 Nvidia::DeviceFD m_nvmap_fd = {}; 66 std::map<u64, FbShareSession> m_sessions;
59 Nvidia::NvCore::SessionId m_session_id = {};
60 std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; 67 std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
61 68
62 std::mutex m_guard; 69 std::mutex m_guard;
@@ -65,4 +72,11 @@ private:
65 std::shared_ptr<Nvidia::Module> m_nvdrv; 72 std::shared_ptr<Nvidia::Module> m_nvdrv;
66}; 73};
67 74
75struct FbShareSession {
76 Nvidia::DeviceFD nvmap_fd = {};
77 Nvidia::NvCore::SessionId session_id = {};
78 u64 layer_id = {};
79 u32 buffer_nvmap_handle = 0;
80};
81
68} // namespace Service::Nvnflinger 82} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
index ba2b5c28c..be7eb97a3 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.cpp
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -86,6 +86,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
86 .height = igbp_buffer.Height(), 86 .height = igbp_buffer.Height(),
87 .stride = igbp_buffer.Stride(), 87 .stride = igbp_buffer.Stride(),
88 .z_index = 0, 88 .z_index = 0,
89 .blending = layer.GetBlending(),
89 .transform = static_cast<android::BufferTransformFlags>(item.transform), 90 .transform = static_cast<android::BufferTransformFlags>(item.transform),
90 .crop_rect = item.crop, 91 .crop_rect = item.crop,
91 .acquire_fence = item.fence, 92 .acquire_fence = item.fence,
diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h
index 3af668a25..f71a5d822 100644
--- a/src/core/hle/service/nvnflinger/hwc_layer.h
+++ b/src/core/hle/service/nvnflinger/hwc_layer.h
@@ -11,6 +11,18 @@
11 11
12namespace Service::Nvnflinger { 12namespace Service::Nvnflinger {
13 13
14// hwc_layer_t::blending values
15enum class LayerBlending : u32 {
16 // No blending
17 None = 0x100,
18
19 // ONE / ONE_MINUS_SRC_ALPHA
20 Premultiplied = 0x105,
21
22 // SRC_ALPHA / ONE_MINUS_SRC_ALPHA
23 Coverage = 0x405,
24};
25
14struct HwcLayer { 26struct HwcLayer {
15 u32 buffer_handle; 27 u32 buffer_handle;
16 u32 offset; 28 u32 offset;
@@ -19,6 +31,7 @@ struct HwcLayer {
19 u32 height; 31 u32 height;
20 u32 stride; 32 u32 stride;
21 s32 z_index; 33 s32 z_index;
34 LayerBlending blending;
22 android::BufferTransformFlags transform; 35 android::BufferTransformFlags transform;
23 Common::Rectangle<int> crop_rect; 36 Common::Rectangle<int> crop_rect;
24 android::Fence acquire_fence; 37 android::Fence acquire_fence;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index d8ba89d43..687ccc9f9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -157,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) {
157 return true; 157 return true;
158} 158}
159 159
160std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { 160std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
161 const auto lock_guard = Lock(); 161 const auto lock_guard = Lock();
162 auto* const display = FindDisplay(display_id); 162 auto* const display = FindDisplay(display_id);
163 163
@@ -166,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
166 } 166 }
167 167
168 const u64 layer_id = next_layer_id++; 168 const u64 layer_id = next_layer_id++;
169 CreateLayerAtId(*display, layer_id); 169 CreateLayerAtId(*display, layer_id, blending);
170 return layer_id; 170 return layer_id;
171} 171}
172 172
173void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 173void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
174 const auto buffer_id = next_buffer_queue_id++; 174 const auto buffer_id = next_buffer_queue_id++;
175 display.CreateLayer(layer_id, buffer_id, nvdrv->container); 175 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
176 display.FindLayer(layer_id)->SetBlending(blending);
176} 177}
177 178
178bool Nvnflinger::OpenLayer(u64 layer_id) { 179bool Nvnflinger::OpenLayer(u64 layer_id) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index c984d55a0..4cf4f069d 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -15,6 +15,7 @@
15#include "common/thread.h" 15#include "common/thread.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
17#include "core/hle/service/kernel_helpers.h" 17#include "core/hle/service/kernel_helpers.h"
18#include "core/hle/service/nvnflinger/hwc_layer.h"
18 19
19namespace Common { 20namespace Common {
20class Event; 21class Event;
@@ -72,7 +73,8 @@ public:
72 /// Creates a layer on the specified display and returns the layer ID. 73 /// Creates a layer on the specified display and returns the layer ID.
73 /// 74 ///
74 /// If an invalid display ID is specified, then an empty optional is returned. 75 /// If an invalid display ID is specified, then an empty optional is returned.
75 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); 76 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
77 LayerBlending blending = LayerBlending::None);
76 78
77 /// Opens a layer on all displays for the given layer ID. 79 /// Opens a layer on all displays for the given layer ID.
78 bool OpenLayer(u64 layer_id); 80 bool OpenLayer(u64 layer_id);
@@ -128,7 +130,7 @@ private:
128 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); 130 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
129 131
130 /// Creates a layer with the specified layer ID in the desired display. 132 /// Creates a layer with the specified layer ID in the desired display.
131 void CreateLayerAtId(VI::Display& display, u64 layer_id); 133 void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
132 134
133 void SplitVSync(std::stop_token stop_token); 135 void SplitVSync(std::stop_token stop_token);
134 136
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 493bd6e9e..eca35d82a 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 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 "core/hle/service/nvnflinger/hwc_layer.h"
4#include "core/hle/service/vi/layer/vi_layer.h" 5#include "core/hle/service/vi/layer/vi_layer.h"
5 6
6namespace Service::VI { 7namespace Service::VI {
@@ -8,8 +9,9 @@ namespace Service::VI {
8Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, 9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
9 android::BufferQueueProducer& binder_, 10 android::BufferQueueProducer& binder_,
10 std::shared_ptr<android::BufferItemConsumer>&& consumer_) 11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
11 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, 12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
12 consumer{std::move(consumer_)}, open{false}, visible{true} {} 13 consumer_)},
14 blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
13 15
14Layer::~Layer() = default; 16Layer::~Layer() = default;
15 17
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index b4b031ee7..14e229903 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -14,6 +14,10 @@ class BufferQueueCore;
14class BufferQueueProducer; 14class BufferQueueProducer;
15} // namespace Service::android 15} // namespace Service::android
16 16
17namespace Service::Nvnflinger {
18enum class LayerBlending : u32;
19}
20
17namespace Service::VI { 21namespace Service::VI {
18 22
19/// Represents a single display layer. 23/// Represents a single display layer.
@@ -92,12 +96,21 @@ public:
92 return !std::exchange(open, true); 96 return !std::exchange(open, true);
93 } 97 }
94 98
99 Nvnflinger::LayerBlending GetBlending() {
100 return blending;
101 }
102
103 void SetBlending(Nvnflinger::LayerBlending b) {
104 blending = b;
105 }
106
95private: 107private:
96 const u64 layer_id; 108 const u64 layer_id;
97 const u32 binder_id; 109 const u32 binder_id;
98 android::BufferQueueCore& core; 110 android::BufferQueueCore& core;
99 android::BufferQueueProducer& binder; 111 android::BufferQueueProducer& binder;
100 std::shared_ptr<android::BufferItemConsumer> consumer; 112 std::shared_ptr<android::BufferItemConsumer> consumer;
113 Service::Nvnflinger::LayerBlending blending;
101 bool open; 114 bool open;
102 bool visible; 115 bool visible;
103}; 116};
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index af6b10db6..2bebfeef9 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -401,6 +401,14 @@ void Config::ReadNetworkValues() {
401 EndGroup(); 401 EndGroup();
402} 402}
403 403
404void Config::ReadLibraryAppletValues() {
405 BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
406
407 ReadCategory(Settings::Category::LibraryApplet);
408
409 EndGroup();
410}
411
404void Config::ReadValues() { 412void Config::ReadValues() {
405 if (global) { 413 if (global) {
406 ReadDataStorageValues(); 414 ReadDataStorageValues();
@@ -410,6 +418,7 @@ void Config::ReadValues() {
410 ReadServiceValues(); 418 ReadServiceValues();
411 ReadWebServiceValues(); 419 ReadWebServiceValues();
412 ReadMiscellaneousValues(); 420 ReadMiscellaneousValues();
421 ReadLibraryAppletValues();
413 } 422 }
414 ReadControlValues(); 423 ReadControlValues();
415 ReadCoreValues(); 424 ReadCoreValues();
@@ -511,6 +520,7 @@ void Config::SaveValues() {
511 SaveNetworkValues(); 520 SaveNetworkValues();
512 SaveWebServiceValues(); 521 SaveWebServiceValues();
513 SaveMiscellaneousValues(); 522 SaveMiscellaneousValues();
523 SaveLibraryAppletValues();
514 } else { 524 } else {
515 LOG_DEBUG(Config, "Saving only generic configuration values"); 525 LOG_DEBUG(Config, "Saving only generic configuration values");
516 } 526 }
@@ -691,6 +701,14 @@ void Config::SaveWebServiceValues() {
691 EndGroup(); 701 EndGroup();
692} 702}
693 703
704void Config::SaveLibraryAppletValues() {
705 BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
706
707 WriteCategory(Settings::Category::LibraryApplet);
708
709 EndGroup();
710}
711
694bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { 712bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
695 std::string full_key = GetFullKey(key, false); 713 std::string full_key = GetFullKey(key, false);
696 if (!default_value.has_value()) { 714 if (!default_value.has_value()) {
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index 4ecb97044..8b0599cc3 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -88,6 +88,7 @@ protected:
88 void ReadSystemValues(); 88 void ReadSystemValues();
89 void ReadWebServiceValues(); 89 void ReadWebServiceValues();
90 void ReadNetworkValues(); 90 void ReadNetworkValues();
91 void ReadLibraryAppletValues();
91 92
92 // Read platform specific sections 93 // Read platform specific sections
93 virtual void ReadHidbusValues() = 0; 94 virtual void ReadHidbusValues() = 0;
@@ -121,6 +122,7 @@ protected:
121 void SaveScreenshotValues(); 122 void SaveScreenshotValues();
122 void SaveSystemValues(); 123 void SaveSystemValues();
123 void SaveWebServiceValues(); 124 void SaveWebServiceValues();
125 void SaveLibraryAppletValues();
124 126
125 // Save platform specific sections 127 // Save platform specific sections
126 virtual void SaveHidbusValues() = 0; 128 virtual void SaveHidbusValues() = 0;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 55180f4b5..2de2beb6e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -18,6 +18,7 @@ add_library(video_core STATIC
18 buffer_cache/usage_tracker.h 18 buffer_cache/usage_tracker.h
19 buffer_cache/word_manager.h 19 buffer_cache/word_manager.h
20 cache_types.h 20 cache_types.h
21 capture.h
21 cdma_pusher.cpp 22 cdma_pusher.cpp
22 cdma_pusher.h 23 cdma_pusher.h
23 compatible_formats.cpp 24 compatible_formats.cpp
@@ -101,6 +102,7 @@ add_library(video_core STATIC
101 memory_manager.cpp 102 memory_manager.cpp
102 memory_manager.h 103 memory_manager.h
103 precompiled_headers.h 104 precompiled_headers.h
105 present.h
104 pte_kind.h 106 pte_kind.h
105 query_cache/bank_base.h 107 query_cache/bank_base.h
106 query_cache/query_base.h 108 query_cache/query_base.h
diff --git a/src/video_core/capture.h b/src/video_core/capture.h
new file mode 100644
index 000000000..8db14a8ec
--- /dev/null
+++ b/src/video_core/capture.h
@@ -0,0 +1,36 @@
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/alignment.h"
7#include "common/bit_util.h"
8#include "common/common_types.h"
9#include "core/frontend/framebuffer_layout.h"
10#include "video_core/surface.h"
11
12namespace VideoCore::Capture {
13
14constexpr u32 BlockHeight = 4;
15constexpr u32 BlockDepth = 0;
16constexpr u32 BppLog2 = 2;
17
18constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM;
19
20constexpr auto LinearWidth = Layout::ScreenUndocked::Width;
21constexpr auto LinearHeight = Layout::ScreenUndocked::Height;
22constexpr auto LinearDepth = 1U;
23constexpr auto BytesPerPixel = 4U;
24
25constexpr auto TiledWidth = LinearWidth;
26constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2);
27constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2);
28
29constexpr Layout::FramebufferLayout Layout{
30 .width = LinearWidth,
31 .height = LinearHeight,
32 .screen = {0, 0, LinearWidth, LinearHeight},
33 .is_srgb = false,
34};
35
36} // namespace VideoCore::Capture
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index 6a18b76fb..8b2a49de5 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -11,6 +11,12 @@
11 11
12namespace Tegra { 12namespace Tegra {
13 13
14enum class BlendMode {
15 Opaque,
16 Premultiplied,
17 Coverage,
18};
19
14/** 20/**
15 * Struct describing framebuffer configuration 21 * Struct describing framebuffer configuration
16 */ 22 */
@@ -23,6 +29,7 @@ struct FramebufferConfig {
23 Service::android::PixelFormat pixel_format{}; 29 Service::android::PixelFormat pixel_format{};
24 Service::android::BufferTransformFlags transform_flags{}; 30 Service::android::BufferTransformFlags transform_flags{};
25 Common::Rectangle<int> crop_rect{}; 31 Common::Rectangle<int> crop_rect{};
32 BlendMode blending{};
26}; 33};
27 34
28Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, 35Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index f4a5d831c..8e663f2a8 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -347,6 +347,17 @@ struct GPU::Impl {
347 WaitForSyncOperation(wait_fence); 347 WaitForSyncOperation(wait_fence);
348 } 348 }
349 349
350 std::vector<u8> GetAppletCaptureBuffer() {
351 std::vector<u8> out;
352
353 const auto wait_fence =
354 RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
355 gpu_thread.TickGPU();
356 WaitForSyncOperation(wait_fence);
357
358 return out;
359 }
360
350 GPU& gpu; 361 GPU& gpu;
351 Core::System& system; 362 Core::System& system;
352 Host1x::Host1x& host1x; 363 Host1x::Host1x& host1x;
@@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
505 impl->RequestComposite(std::move(layers), std::move(fences)); 516 impl->RequestComposite(std::move(layers), std::move(fences));
506} 517}
507 518
519std::vector<u8> GPU::GetAppletCaptureBuffer() {
520 return impl->GetAppletCaptureBuffer();
521}
522
508u64 GPU::GetTicks() const { 523u64 GPU::GetTicks() const {
509 return impl->GetTicks(); 524 return impl->GetTicks();
510} 525}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c4602ca37..ad535512c 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -215,6 +215,8 @@ public:
215 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, 215 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
216 std::vector<Service::Nvidia::NvFence>&& fences); 216 std::vector<Service::Nvidia::NvFence>&& fences);
217 217
218 std::vector<u8> GetAppletCaptureBuffer();
219
218 /// Performs any additional setup necessary in order to begin GPU emulation. 220 /// 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 221 /// This can be used to launch any necessary threads and register any necessary
220 /// core timing events. 222 /// core timing events.
diff --git a/src/video_core/host_shaders/fidelityfx_fsr.frag b/src/video_core/host_shaders/fidelityfx_fsr.frag
index a266e1c4e..54eedb450 100644
--- a/src/video_core/host_shaders/fidelityfx_fsr.frag
+++ b/src/video_core/host_shaders/fidelityfx_fsr.frag
@@ -37,6 +37,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
37 37
38#define A_GPU 1 38#define A_GPU 1
39#define A_GLSL 1 39#define A_GLSL 1
40#define FSR_RCAS_PASSTHROUGH_ALPHA 1
40 41
41#ifndef YUZU_USE_FP16 42#ifndef YUZU_USE_FP16
42 #include "ffx_a.h" 43 #include "ffx_a.h"
@@ -71,9 +72,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
71 72
72#include "ffx_fsr1.h" 73#include "ffx_fsr1.h"
73 74
74#if USE_RCAS 75layout (location = 0) in vec2 frag_texcoord;
75 layout(location = 0) in vec2 frag_texcoord;
76#endif
77layout (location = 0) out vec4 frag_color; 76layout (location = 0) out vec4 frag_color;
78 77
79void CurrFilter(AU2 pos) { 78void CurrFilter(AU2 pos) {
@@ -81,22 +80,22 @@ void CurrFilter(AU2 pos) {
81 #ifndef YUZU_USE_FP16 80 #ifndef YUZU_USE_FP16
82 AF3 c; 81 AF3 c;
83 FsrEasuF(c, pos, Const0, Const1, Const2, Const3); 82 FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
84 frag_color = AF4(c, 1.0); 83 frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
85 #else 84 #else
86 AH3 c; 85 AH3 c;
87 FsrEasuH(c, pos, Const0, Const1, Const2, Const3); 86 FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
88 frag_color = AH4(c, 1.0); 87 frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
89 #endif 88 #endif
90#endif 89#endif
91#if USE_RCAS 90#if USE_RCAS
92 #ifndef YUZU_USE_FP16 91 #ifndef YUZU_USE_FP16
93 AF3 c; 92 AF4 c;
94 FsrRcasF(c.r, c.g, c.b, pos, Const0); 93 FsrRcasF(c.r, c.g, c.b, c.a, pos, Const0);
95 frag_color = AF4(c, 1.0); 94 frag_color = c;
96 #else 95 #else
97 AH3 c; 96 AH4 c;
98 FsrRcasH(c.r, c.g, c.b, pos, Const0); 97 FsrRcasH(c.r, c.g, c.b, c.a, pos, Const0);
99 frag_color = AH4(c, 1.0); 98 frag_color = c;
100 #endif 99 #endif
101#endif 100#endif
102} 101}
diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag
index 9bffc20d5..192a602c1 100644
--- a/src/video_core/host_shaders/fxaa.frag
+++ b/src/video_core/host_shaders/fxaa.frag
@@ -71,5 +71,5 @@ vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) {
71} 71}
72 72
73void main() { 73void main() {
74 frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0); 74 frag_color = vec4(FxaaPixelShader(posPos, input_texture), texture(input_texture, posPos.xy).a);
75} 75}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
index 16d22f58e..fc47d3810 100644
--- a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
@@ -31,6 +31,7 @@ layout (location = 0) uniform uvec4 constants[4];
31 31
32#define A_GPU 1 32#define A_GPU 1
33#define A_GLSL 1 33#define A_GLSL 1
34#define FSR_RCAS_PASSTHROUGH_ALPHA 1
34 35
35#ifdef YUZU_USE_FP16 36#ifdef YUZU_USE_FP16
36 #define A_HALF 37 #define A_HALF
@@ -67,9 +68,7 @@ layout (location = 0) uniform uvec4 constants[4];
67 68
68#include "ffx_fsr1.h" 69#include "ffx_fsr1.h"
69 70
70#if USE_RCAS 71layout (location = 0) in vec2 frag_texcoord;
71 layout(location = 0) in vec2 frag_texcoord;
72#endif
73layout (location = 0) out vec4 frag_color; 72layout (location = 0) out vec4 frag_color;
74 73
75void CurrFilter(AU2 pos) 74void CurrFilter(AU2 pos)
@@ -78,22 +77,22 @@ void CurrFilter(AU2 pos)
78 #ifndef YUZU_USE_FP16 77 #ifndef YUZU_USE_FP16
79 AF3 c; 78 AF3 c;
80 FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]); 79 FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
81 frag_color = AF4(c, 1.0); 80 frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
82 #else 81 #else
83 AH3 c; 82 AH3 c;
84 FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]); 83 FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
85 frag_color = AH4(c, 1.0); 84 frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
86 #endif 85 #endif
87#endif 86#endif
88#if USE_RCAS 87#if USE_RCAS
89 #ifndef YUZU_USE_FP16 88 #ifndef YUZU_USE_FP16
90 AF3 c; 89 AF4 c;
91 FsrRcasF(c.r, c.g, c.b, pos, constants[0]); 90 FsrRcasF(c.r, c.g, c.b, c.a, pos, constants[0]);
92 frag_color = AF4(c, 1.0); 91 frag_color = c;
93 #else 92 #else
94 AH3 c; 93 AH3 c;
95 FsrRcasH(c.r, c.g, c.b, pos, constants[0]); 94 FsrRcasH(c.r, c.g, c.b, c.a, pos, constants[0]);
96 frag_color = AH4(c, 1.0); 95 frag_color = c;
97 #endif 96 #endif
98#endif 97#endif
99} 98}
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
index 5fd7ad297..096b4e4db 100644
--- a/src/video_core/host_shaders/opengl_present.frag
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -9,5 +9,5 @@ layout (location = 0) out vec4 color;
9layout (binding = 0) uniform sampler2D color_texture; 9layout (binding = 0) uniform sampler2D color_texture;
10 10
11void main() { 11void main() {
12 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f); 12 color = vec4(texture(color_texture, frag_tex_coord));
13} 13}
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag
index c814629cf..a9d9d40a3 100644
--- a/src/video_core/host_shaders/present_bicubic.frag
+++ b/src/video_core/host_shaders/present_bicubic.frag
@@ -52,5 +52,5 @@ vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) {
52} 52}
53 53
54void main() { 54void main() {
55 color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); 55 color = textureBicubic(color_texture, frag_tex_coord);
56} 56}
diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag
index ad9bb76a4..78edeb9b4 100644
--- a/src/video_core/host_shaders/present_gaussian.frag
+++ b/src/video_core/host_shaders/present_gaussian.frag
@@ -46,14 +46,14 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) {
46} 46}
47 47
48void main() { 48void main() {
49 vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0]; 49 vec4 base = texture(color_texture, vec2(frag_tex_coord)) * weight[0];
50 vec2 tex_offset = 1.0f / textureSize(color_texture, 0); 50 vec2 tex_offset = 1.0f / textureSize(color_texture, 0);
51 51
52 // TODO(Blinkhawk): This code can be optimized through shader group instructions. 52 // TODO(Blinkhawk): This code can be optimized through shader group instructions.
53 vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb; 53 vec4 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset);
54 vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb; 54 vec4 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset);
55 vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb; 55 vec4 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset);
56 vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb; 56 vec4 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0));
57 vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); 57 vec4 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f);
58 color = vec4(combination + base, 1.0f); 58 color = combination + base;
59} 59}
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
index d369bef06..05d033310 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
@@ -6,5 +6,6 @@
6 6
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_EASU 1 8#define USE_EASU 1
9#define VERSION 1
9 10
10#include "fidelityfx_fsr.frag" 11#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
index 6f25ef00f..7ae11dd66 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
@@ -5,5 +5,6 @@
5#extension GL_GOOGLE_include_directive : enable 5#extension GL_GOOGLE_include_directive : enable
6 6
7#define USE_EASU 1 7#define USE_EASU 1
8#define VERSION 1
8 9
9#include "fidelityfx_fsr.frag" 10#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
index 0c953a900..c017214a5 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
@@ -6,5 +6,6 @@
6 6
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_RCAS 1 8#define USE_RCAS 1
9#define VERSION 1
9 10
10#include "fidelityfx_fsr.frag" 11#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
index 02e9a27c6..976825f4b 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
@@ -5,5 +5,6 @@
5#extension GL_GOOGLE_include_directive : enable 5#extension GL_GOOGLE_include_directive : enable
6 6
7#define USE_RCAS 1 7#define USE_RCAS 1
8#define VERSION 1
8 9
9#include "fidelityfx_fsr.frag" 10#include "fidelityfx_fsr.frag"
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 79ea817c2..cea5dac9d 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
@@ -5,7 +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 VERSION 2
9#define YUZU_USE_FP16 9#define YUZU_USE_FP16
10 10
11#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 9605bb58b..10ddf0401 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
@@ -5,6 +5,6 @@
5 5
6#extension GL_GOOGLE_include_directive : enable 6#extension GL_GOOGLE_include_directive : enable
7 7
8#define VERSION 1 8#define VERSION 2
9 9
10#include "opengl_present_scaleforce.frag" 10#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/present.h b/src/video_core/present.h
new file mode 100644
index 000000000..4fdfcca68
--- /dev/null
+++ b/src/video_core/present.h
@@ -0,0 +1,37 @@
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/settings.h"
7
8static inline Settings::ScalingFilter GetScalingFilter() {
9 return Settings::values.scaling_filter.GetValue();
10}
11
12static inline Settings::AntiAliasing GetAntiAliasing() {
13 return Settings::values.anti_aliasing.GetValue();
14}
15
16static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() {
17 return Settings::ScalingFilter::Bilinear;
18}
19
20static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() {
21 return Settings::AntiAliasing::None;
22}
23
24struct PresentFilters {
25 Settings::ScalingFilter (*get_scaling_filter)();
26 Settings::AntiAliasing (*get_anti_aliasing)();
27};
28
29constexpr PresentFilters PresentFiltersForDisplay{
30 .get_scaling_filter = &GetScalingFilter,
31 .get_anti_aliasing = &GetAntiAliasing,
32};
33
34constexpr PresentFilters PresentFiltersForAppletCapture{
35 .get_scaling_filter = &GetScalingFilterForAppletCapture,
36 .get_anti_aliasing = &GetAntiAliasingForAppletCapture,
37};
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 3ad180f67..67427f937 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -40,6 +40,9 @@ public:
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 Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; 41 virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
42 42
43 /// Get the tiled applet layer capture buffer
44 virtual std::vector<u8> GetAppletCaptureBuffer() = 0;
45
43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; 46 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
44 47
45 [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; 48 [[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
index c89daff53..e6147d66c 100644
--- a/src/video_core/renderer_null/renderer_null.cpp
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -3,6 +3,7 @@
3 3
4#include "core/frontend/emu_window.h" 4#include "core/frontend/emu_window.h"
5#include "core/frontend/graphics_context.h" 5#include "core/frontend/graphics_context.h"
6#include "video_core/capture.h"
6#include "video_core/renderer_null/renderer_null.h" 7#include "video_core/renderer_null/renderer_null.h"
7 8
8namespace Null { 9namespace Null {
@@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff
22 render_window.OnFrameDisplayed(); 23 render_window.OnFrameDisplayed();
23} 24}
24 25
26std::vector<u8> RendererNull::GetAppletCaptureBuffer() {
27 return std::vector<u8>(VideoCore::Capture::TiledSize);
28}
29
25} // namespace Null 30} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
index 063b476bb..34dbe1e4f 100644
--- a/src/video_core/renderer_null/renderer_null.h
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -19,6 +19,8 @@ public:
19 19
20 void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; 20 void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
21 21
22 std::vector<u8> GetAppletCaptureBuffer() override;
23
22 VideoCore::RasterizerInterface* ReadRasterizer() override { 24 VideoCore::RasterizerInterface* ReadRasterizer() override {
23 return &m_rasterizer; 25 return &m_rasterizer;
24 } 26 }
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp
index 6ba8b214b..9260a4dc4 100644
--- a/src/video_core/renderer_opengl/gl_blit_screen.cpp
+++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/settings.h" 4#include "common/settings.h"
5#include "video_core/present.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h" 6#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/gl_state_tracker.h"
7#include "video_core/renderer_opengl/present/filters.h" 8#include "video_core/renderer_opengl/present/filters.h"
@@ -13,14 +14,14 @@ namespace OpenGL {
13BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, 14BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
14 Tegra::MaxwellDeviceMemoryManager& device_memory_, 15 Tegra::MaxwellDeviceMemoryManager& device_memory_,
15 StateTracker& state_tracker_, ProgramManager& program_manager_, 16 StateTracker& state_tracker_, ProgramManager& program_manager_,
16 Device& device_) 17 Device& device_, const PresentFilters& filters_)
17 : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), 18 : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
18 program_manager(program_manager_), device(device_) {} 19 program_manager(program_manager_), device(device_), filters(filters_) {}
19 20
20BlitScreen::~BlitScreen() = default; 21BlitScreen::~BlitScreen() = default;
21 22
22void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, 23void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
23 const Layout::FramebufferLayout& layout) { 24 const Layout::FramebufferLayout& layout, bool invert_y) {
24 // TODO: Signal state tracker about these changes 25 // TODO: Signal state tracker about these changes
25 state_tracker.NotifyScreenDrawVertexArray(); 26 state_tracker.NotifyScreenDrawVertexArray();
26 state_tracker.NotifyPolygonModes(); 27 state_tracker.NotifyPolygonModes();
@@ -56,22 +57,22 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe
56 glDepthRangeIndexed(0, 0.0, 0.0); 57 glDepthRangeIndexed(0, 0.0, 0.0);
57 58
58 while (layers.size() < framebuffers.size()) { 59 while (layers.size() < framebuffers.size()) {
59 layers.emplace_back(rasterizer, device_memory); 60 layers.emplace_back(rasterizer, device_memory, filters);
60 } 61 }
61 62
62 CreateWindowAdapt(); 63 CreateWindowAdapt();
63 window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); 64 window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y);
64 65
65 // TODO 66 // TODO
66 // program_manager.RestoreGuestPipeline(); 67 // program_manager.RestoreGuestPipeline();
67} 68}
68 69
69void BlitScreen::CreateWindowAdapt() { 70void BlitScreen::CreateWindowAdapt() {
70 if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { 71 if (window_adapt && filters.get_scaling_filter() == current_window_adapt) {
71 return; 72 return;
72 } 73 }
73 74
74 current_window_adapt = Settings::values.scaling_filter.GetValue(); 75 current_window_adapt = filters.get_scaling_filter();
75 switch (current_window_adapt) { 76 switch (current_window_adapt) {
76 case Settings::ScalingFilter::NearestNeighbor: 77 case Settings::ScalingFilter::NearestNeighbor:
77 window_adapt = MakeNearestNeighbor(device); 78 window_adapt = MakeNearestNeighbor(device);
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h
index 0c3d838f1..df2da9424 100644
--- a/src/video_core/renderer_opengl/gl_blit_screen.h
+++ b/src/video_core/renderer_opengl/gl_blit_screen.h
@@ -15,6 +15,8 @@ namespace Layout {
15struct FramebufferLayout; 15struct FramebufferLayout;
16} 16}
17 17
18struct PresentFilters;
19
18namespace Tegra { 20namespace Tegra {
19struct FramebufferConfig; 21struct FramebufferConfig;
20} 22}
@@ -46,12 +48,12 @@ public:
46 explicit BlitScreen(RasterizerOpenGL& rasterizer, 48 explicit BlitScreen(RasterizerOpenGL& rasterizer,
47 Tegra::MaxwellDeviceMemoryManager& device_memory, 49 Tegra::MaxwellDeviceMemoryManager& device_memory,
48 StateTracker& state_tracker, ProgramManager& program_manager, 50 StateTracker& state_tracker, ProgramManager& program_manager,
49 Device& device); 51 Device& device, const PresentFilters& filters);
50 ~BlitScreen(); 52 ~BlitScreen();
51 53
52 /// Draws the emulated screens to the emulator window. 54 /// Draws the emulated screens to the emulator window.
53 void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, 55 void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
54 const Layout::FramebufferLayout& layout); 56 const Layout::FramebufferLayout& layout, bool invert_y);
55 57
56private: 58private:
57 void CreateWindowAdapt(); 59 void CreateWindowAdapt();
@@ -61,6 +63,7 @@ private:
61 StateTracker& state_tracker; 63 StateTracker& state_tracker;
62 ProgramManager& program_manager; 64 ProgramManager& program_manager;
63 Device& device; 65 Device& device;
66 const PresentFilters& filters;
64 67
65 Settings::ScalingFilter current_window_adapt{}; 68 Settings::ScalingFilter current_window_adapt{};
66 std::unique_ptr<WindowAdaptPass> window_adapt; 69 std::unique_ptr<WindowAdaptPass> window_adapt;
diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp
index 8643e07c6..6c7092d22 100644
--- a/src/video_core/renderer_opengl/present/layer.cpp
+++ b/src/video_core/renderer_opengl/present/layer.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "video_core/framebuffer_config.h" 4#include "video_core/framebuffer_config.h"
5#include "video_core/present.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h" 6#include "video_core/renderer_opengl/gl_blit_screen.h"
6#include "video_core/renderer_opengl/gl_rasterizer.h" 7#include "video_core/renderer_opengl/gl_rasterizer.h"
7#include "video_core/renderer_opengl/present/fsr.h" 8#include "video_core/renderer_opengl/present/fsr.h"
@@ -14,8 +15,9 @@
14 15
15namespace OpenGL { 16namespace OpenGL {
16 17
17Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) 18Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_,
18 : rasterizer(rasterizer_), device_memory(device_memory_) { 19 const PresentFilters& filters_)
20 : rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) {
19 // Allocate textures for the screen 21 // Allocate textures for the screen
20 framebuffer_texture.resource.Create(GL_TEXTURE_2D); 22 framebuffer_texture.resource.Create(GL_TEXTURE_2D);
21 23
@@ -34,12 +36,12 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
34 std::array<ScreenRectVertex, 4>& out_vertices, 36 std::array<ScreenRectVertex, 4>& out_vertices,
35 ProgramManager& program_manager, 37 ProgramManager& program_manager,
36 const Tegra::FramebufferConfig& framebuffer, 38 const Tegra::FramebufferConfig& framebuffer,
37 const Layout::FramebufferLayout& layout) { 39 const Layout::FramebufferLayout& layout, bool invert_y) {
38 FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); 40 FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
39 auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); 41 auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
40 GLuint texture = info.display_texture; 42 GLuint texture = info.display_texture;
41 43
42 auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); 44 auto anti_aliasing = filters.get_anti_aliasing();
43 if (anti_aliasing != Settings::AntiAliasing::None) { 45 if (anti_aliasing != Settings::AntiAliasing::None) {
44 glEnablei(GL_SCISSOR_TEST, 0); 46 glEnablei(GL_SCISSOR_TEST, 0);
45 auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); 47 auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
@@ -64,7 +66,7 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
64 66
65 glDisablei(GL_SCISSOR_TEST, 0); 67 glDisablei(GL_SCISSOR_TEST, 0);
66 68
67 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { 69 if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
68 if (!fsr || fsr->NeedsRecreation(layout.screen)) { 70 if (!fsr || fsr->NeedsRecreation(layout.screen)) {
69 fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); 71 fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
70 } 72 }
@@ -83,10 +85,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
83 const auto w = screen.GetWidth(); 85 const auto w = screen.GetWidth();
84 const auto h = screen.GetHeight(); 86 const auto h = screen.GetHeight();
85 87
86 out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); 88 const auto left = crop.left;
87 out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); 89 const auto right = crop.right;
88 out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); 90 const auto top = invert_y ? crop.bottom : crop.top;
89 out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); 91 const auto bottom = invert_y ? crop.top : crop.bottom;
92
93 out_vertices[0] = ScreenRectVertex(x, y, left, top);
94 out_vertices[1] = ScreenRectVertex(x + w, y, right, top);
95 out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
96 out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
90 97
91 return texture; 98 return texture;
92} 99}
@@ -131,10 +138,12 @@ FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig&
131 const u64 size_in_bytes{Tegra::Texture::CalculateSize( 138 const u64 size_in_bytes{Tegra::Texture::CalculateSize(
132 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; 139 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)}; 140 const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
134 const std::span<const u8> input_data(host_ptr, size_in_bytes); 141 if (host_ptr) {
135 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, 142 const std::span<const u8> input_data(host_ptr, size_in_bytes);
136 framebuffer.width, framebuffer.height, 1, block_height_log2, 143 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
137 0); 144 framebuffer.width, framebuffer.height, 1,
145 block_height_log2, 0);
146 }
138 147
139 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 148 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
140 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); 149 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h
index ef1055abf..5b15b730f 100644
--- a/src/video_core/renderer_opengl/present/layer.h
+++ b/src/video_core/renderer_opengl/present/layer.h
@@ -13,6 +13,8 @@ namespace Layout {
13struct FramebufferLayout; 13struct FramebufferLayout;
14} 14}
15 15
16struct PresentFilters;
17
16namespace Service::android { 18namespace Service::android {
17enum class PixelFormat : u32; 19enum class PixelFormat : u32;
18}; 20};
@@ -44,14 +46,15 @@ struct ScreenRectVertex;
44 46
45class Layer { 47class Layer {
46public: 48public:
47 explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); 49 explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory,
50 const PresentFilters& filters);
48 ~Layer(); 51 ~Layer();
49 52
50 GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, 53 GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
51 std::array<ScreenRectVertex, 4>& out_vertices, 54 std::array<ScreenRectVertex, 4>& out_vertices,
52 ProgramManager& program_manager, 55 ProgramManager& program_manager,
53 const Tegra::FramebufferConfig& framebuffer, 56 const Tegra::FramebufferConfig& framebuffer,
54 const Layout::FramebufferLayout& layout); 57 const Layout::FramebufferLayout& layout, bool invert_y);
55 58
56private: 59private:
57 /// Loads framebuffer from emulated memory into the active OpenGL texture. 60 /// Loads framebuffer from emulated memory into the active OpenGL texture.
@@ -65,6 +68,7 @@ private:
65private: 68private:
66 RasterizerOpenGL& rasterizer; 69 RasterizerOpenGL& rasterizer;
67 Tegra::MaxwellDeviceMemoryManager& device_memory; 70 Tegra::MaxwellDeviceMemoryManager& device_memory;
71 const PresentFilters& filters;
68 72
69 /// OpenGL framebuffer data 73 /// OpenGL framebuffer data
70 std::vector<u8> gl_framebuffer_data; 74 std::vector<u8> gl_framebuffer_data;
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
index 4d681606b..d8b6a11cb 100644
--- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
@@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default;
37 37
38void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, 38void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
39 std::span<const Tegra::FramebufferConfig> framebuffers, 39 std::span<const Tegra::FramebufferConfig> framebuffers,
40 const Layout::FramebufferLayout& layout) { 40 const Layout::FramebufferLayout& layout, bool invert_y) {
41 GLint old_read_fb; 41 GLint old_read_fb;
42 GLint old_draw_fb; 42 GLint old_draw_fb;
43 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); 43 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
@@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
51 auto layer_it = layers.begin(); 51 auto layer_it = layers.begin();
52 for (size_t i = 0; i < layer_count; i++) { 52 for (size_t i = 0; i < layer_count; i++) {
53 textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, 53 textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
54 framebuffers[i], layout); 54 framebuffers[i], layout, invert_y);
55 layer_it++; 55 layer_it++;
56 } 56 }
57 57
@@ -92,6 +92,21 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
92 glClear(GL_COLOR_BUFFER_BIT); 92 glClear(GL_COLOR_BUFFER_BIT);
93 93
94 for (size_t i = 0; i < layer_count; i++) { 94 for (size_t i = 0; i < layer_count; i++) {
95 switch (framebuffers[i].blending) {
96 case Tegra::BlendMode::Opaque:
97 default:
98 glDisablei(GL_BLEND, 0);
99 break;
100 case Tegra::BlendMode::Premultiplied:
101 glEnablei(GL_BLEND, 0);
102 glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
103 break;
104 case Tegra::BlendMode::Coverage:
105 glEnablei(GL_BLEND, 0);
106 glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
107 break;
108 }
109
95 glBindTextureUnit(0, textures[i]); 110 glBindTextureUnit(0, textures[i]);
96 glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, 111 glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
97 matrices[i].data()); 112 matrices[i].data());
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h
index 00975a9c6..0a8bcef2f 100644
--- a/src/video_core/renderer_opengl/present/window_adapt_pass.h
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h
@@ -31,7 +31,7 @@ public:
31 31
32 void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, 32 void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
33 std::span<const Tegra::FramebufferConfig> framebuffers, 33 std::span<const Tegra::FramebufferConfig> framebuffers,
34 const Layout::FramebufferLayout& layout); 34 const Layout::FramebufferLayout& layout, bool invert_y);
35 35
36private: 36private:
37 const Device& device; 37 const Device& device;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index e33a32592..5fb54635d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -16,6 +16,8 @@
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/capture.h"
20#include "video_core/present.h"
19#include "video_core/renderer_opengl/gl_blit_screen.h" 21#include "video_core/renderer_opengl/gl_blit_screen.h"
20#include "video_core/renderer_opengl/gl_rasterizer.h" 22#include "video_core/renderer_opengl/gl_rasterizer.h"
21#include "video_core/renderer_opengl/gl_shader_manager.h" 23#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -120,7 +122,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
120 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); 122 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
121 } 123 }
122 blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, 124 blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
123 program_manager, device); 125 program_manager, device, PresentFiltersForDisplay);
126 blit_applet =
127 std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager,
128 device, PresentFiltersForAppletCapture);
129 capture_framebuffer.Create();
130 capture_renderbuffer.Create();
131 glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle);
132 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth,
133 VideoCore::Capture::LinearHeight);
124} 134}
125 135
126RendererOpenGL::~RendererOpenGL() = default; 136RendererOpenGL::~RendererOpenGL() = default;
@@ -130,10 +140,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
130 return; 140 return;
131 } 141 }
132 142
143 RenderAppletCaptureLayer(framebuffers);
133 RenderScreenshot(framebuffers); 144 RenderScreenshot(framebuffers);
134 145
135 state_tracker.BindFramebuffer(0); 146 state_tracker.BindFramebuffer(0);
136 blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); 147 blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);
137 148
138 ++m_current_frame; 149 ++m_current_frame;
139 150
@@ -159,11 +170,8 @@ void RendererOpenGL::AddTelemetryFields() {
159 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); 170 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
160} 171}
161 172
162void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { 173void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
163 if (!renderer_settings.screenshot_requested) { 174 const Layout::FramebufferLayout& layout, void* dst) {
164 return;
165 }
166
167 GLint old_read_fb; 175 GLint old_read_fb;
168 GLint old_draw_fb; 176 GLint old_draw_fb;
169 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); 177 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
@@ -173,29 +181,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
173 screenshot_framebuffer.Create(); 181 screenshot_framebuffer.Create();
174 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); 182 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
175 183
176 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
177
178 GLuint renderbuffer; 184 GLuint renderbuffer;
179 glGenRenderbuffers(1, &renderbuffer); 185 glGenRenderbuffers(1, &renderbuffer);
180 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); 186 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
181 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); 187 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
182 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); 188 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
183 189
184 blit_screen->DrawScreen(framebuffers, layout); 190 blit_screen->DrawScreen(framebuffers, layout, false);
185 191
186 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 192 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
187 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 193 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
188 glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 194 glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst);
189 renderer_settings.screenshot_bits);
190 195
191 screenshot_framebuffer.Release(); 196 screenshot_framebuffer.Release();
192 glDeleteRenderbuffers(1, &renderbuffer); 197 glDeleteRenderbuffers(1, &renderbuffer);
193 198
194 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); 199 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
195 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); 200 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
201}
202
203void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
204 if (!renderer_settings.screenshot_requested) {
205 return;
206 }
207
208 RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
209 renderer_settings.screenshot_bits);
196 210
197 renderer_settings.screenshot_complete_callback(true); 211 renderer_settings.screenshot_complete_callback(true);
198 renderer_settings.screenshot_requested = false; 212 renderer_settings.screenshot_requested = false;
199} 213}
200 214
215void RendererOpenGL::RenderAppletCaptureLayer(
216 std::span<const Tegra::FramebufferConfig> framebuffers) {
217 GLint old_read_fb;
218 GLint old_draw_fb;
219 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
220 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
221
222 glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
223 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
224 capture_renderbuffer.handle);
225
226 blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);
227
228 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
229 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
230}
231
232std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() {
233 using namespace VideoCore::Capture;
234
235 std::vector<u8> linear(TiledSize);
236 std::vector<u8> out(TiledSize);
237
238 GLint old_read_fb;
239 GLint old_draw_fb;
240 GLint old_pixel_pack_buffer;
241 GLint old_pack_row_length;
242 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
243 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
244 glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer);
245 glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length);
246
247 glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
248 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
249 capture_renderbuffer.handle);
250 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
251 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
252 glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
253 linear.data());
254
255 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
256 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
257 glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer);
258 glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length);
259
260 Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight,
261 LinearDepth, BlockHeight, BlockDepth);
262
263 return out;
264}
265
201} // namespace OpenGL 266} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c4625c96e..60d6a1477 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -42,6 +42,8 @@ public:
42 42
43 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; 43 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
44 44
45 std::vector<u8> GetAppletCaptureBuffer() override;
46
45 VideoCore::RasterizerInterface* ReadRasterizer() override { 47 VideoCore::RasterizerInterface* ReadRasterizer() override {
46 return &rasterizer; 48 return &rasterizer;
47 } 49 }
@@ -52,7 +54,11 @@ public:
52 54
53private: 55private:
54 void AddTelemetryFields(); 56 void AddTelemetryFields();
57
58 void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
59 const Layout::FramebufferLayout& layout, void* dst);
55 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); 60 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
61 void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
56 62
57 Core::TelemetrySession& telemetry_session; 63 Core::TelemetrySession& telemetry_session;
58 Core::Frontend::EmuWindow& emu_window; 64 Core::Frontend::EmuWindow& emu_window;
@@ -64,8 +70,11 @@ private:
64 ProgramManager program_manager; 70 ProgramManager program_manager;
65 RasterizerOpenGL rasterizer; 71 RasterizerOpenGL rasterizer;
66 OGLFramebuffer screenshot_framebuffer; 72 OGLFramebuffer screenshot_framebuffer;
73 OGLFramebuffer capture_framebuffer;
74 OGLRenderbuffer capture_renderbuffer;
67 75
68 std::unique_ptr<BlitScreen> blit_screen; 76 std::unique_ptr<BlitScreen> blit_screen;
77 std::unique_ptr<BlitScreen> blit_applet;
69}; 78};
70 79
71} // namespace OpenGL 80} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp
index cfc04be44..3847a9a13 100644
--- a/src/video_core/renderer_vulkan/present/layer.cpp
+++ b/src/video_core/renderer_vulkan/present/layer.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 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 "video_core/present.h"
4#include "video_core/renderer_vulkan/vk_rasterizer.h" 5#include "video_core/renderer_vulkan/vk_rasterizer.h"
5 6
6#include "common/settings.h" 7#include "common/settings.h"
@@ -48,12 +49,12 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
48 49
49Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, 50Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
50 Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, 51 Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
51 VkExtent2D output_size, VkDescriptorSetLayout layout) 52 VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_)
52 : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), 53 : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
53 device_memory(device_memory_), image_count(image_count_) { 54 device_memory(device_memory_), filters(filters_), image_count(image_count_) {
54 CreateDescriptorPool(); 55 CreateDescriptorPool();
55 CreateDescriptorSets(layout); 56 CreateDescriptorSets(layout);
56 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { 57 if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
57 CreateFSR(output_size); 58 CreateFSR(output_size);
58 } 59 }
59} 60}
@@ -171,11 +172,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
171} 172}
172 173
173void Layer::SetAntiAliasPass() { 174void Layer::SetAntiAliasPass() {
174 if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { 175 if (anti_alias && anti_alias_setting == filters.get_anti_aliasing()) {
175 return; 176 return;
176 } 177 }
177 178
178 anti_alias_setting = Settings::values.anti_aliasing.GetValue(); 179 anti_alias_setting = filters.get_anti_aliasing();
179 180
180 const VkExtent2D render_area{ 181 const VkExtent2D render_area{
181 .width = Settings::values.resolution_info.ScaleUp(raw_width), 182 .width = Settings::values.resolution_info.ScaleUp(raw_width),
@@ -270,9 +271,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
270 const u64 linear_size{GetSizeInBytes(framebuffer)}; 271 const u64 linear_size{GetSizeInBytes(framebuffer)};
271 const u64 tiled_size{Tegra::Texture::CalculateSize( 272 const u64 tiled_size{Tegra::Texture::CalculateSize(
272 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; 273 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
273 Tegra::Texture::UnswizzleTexture( 274 if (host_ptr) {
274 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), 275 Tegra::Texture::UnswizzleTexture(
275 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); 276 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
277 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
278 }
276 279
277 const VkBufferImageCopy copy{ 280 const VkBufferImageCopy copy{
278 .bufferOffset = image_offset, 281 .bufferOffset = image_offset,
diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h
index 88d43fc5f..f5effdcd7 100644
--- a/src/video_core/renderer_vulkan/present/layer.h
+++ b/src/video_core/renderer_vulkan/present/layer.h
@@ -11,6 +11,8 @@ namespace Layout {
11struct FramebufferLayout; 11struct FramebufferLayout;
12} 12}
13 13
14struct PresentFilters;
15
14namespace Tegra { 16namespace Tegra {
15struct FramebufferConfig; 17struct FramebufferConfig;
16} 18}
@@ -37,7 +39,8 @@ class Layer final {
37public: 39public:
38 explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, 40 explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
39 Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, 41 Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
40 VkExtent2D output_size, VkDescriptorSetLayout layout); 42 VkExtent2D output_size, VkDescriptorSetLayout layout,
43 const PresentFilters& filters);
41 ~Layer(); 44 ~Layer();
42 45
43 void ConfigureDraw(PresentPushConstants* out_push_constants, 46 void ConfigureDraw(PresentPushConstants* out_push_constants,
@@ -71,6 +74,7 @@ private:
71 MemoryAllocator& memory_allocator; 74 MemoryAllocator& memory_allocator;
72 Scheduler& scheduler; 75 Scheduler& scheduler;
73 Tegra::MaxwellDeviceMemoryManager& device_memory; 76 Tegra::MaxwellDeviceMemoryManager& device_memory;
77 const PresentFilters& filters;
74 const size_t image_count{}; 78 const size_t image_count{};
75 vk::DescriptorPool descriptor_pool{}; 79 vk::DescriptorPool descriptor_pool{};
76 vk::DescriptorSets descriptor_sets{}; 80 vk::DescriptorSets descriptor_sets{};
diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index 6ee16595d..7f27c7c1b 100644
--- a/src/video_core/renderer_vulkan/present/util.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -362,10 +362,10 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
362 }); 362 });
363} 363}
364 364
365vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, 365static vk::Pipeline CreateWrappedPipelineImpl(
366 vk::PipelineLayout& layout, 366 const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
367 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, 367 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
368 bool enable_blending) { 368 VkPipelineColorBlendAttachmentState blending) {
369 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ 369 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
370 { 370 {
371 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 371 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -443,30 +443,6 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
443 .alphaToOneEnable = VK_FALSE, 443 .alphaToOneEnable = VK_FALSE,
444 }; 444 };
445 445
446 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
447 .blendEnable = VK_FALSE,
448 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
449 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
450 .colorBlendOp = VK_BLEND_OP_ADD,
451 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
452 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
453 .alphaBlendOp = VK_BLEND_OP_ADD,
454 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
455 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
456 };
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
470 const VkPipelineColorBlendStateCreateInfo color_blend_ci{ 446 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
471 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 447 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
472 .pNext = nullptr, 448 .pNext = nullptr,
@@ -474,8 +450,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
474 .logicOpEnable = VK_FALSE, 450 .logicOpEnable = VK_FALSE,
475 .logicOp = VK_LOGIC_OP_COPY, 451 .logicOp = VK_LOGIC_OP_COPY,
476 .attachmentCount = 1, 452 .attachmentCount = 1,
477 .pAttachments = 453 .pAttachments = &blending,
478 enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled,
479 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, 454 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
480 }; 455 };
481 456
@@ -515,6 +490,63 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
515 }); 490 });
516} 491}
517 492
493vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
494 vk::PipelineLayout& layout,
495 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
496 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
497 .blendEnable = VK_FALSE,
498 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
499 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
500 .colorBlendOp = VK_BLEND_OP_ADD,
501 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
502 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
503 .alphaBlendOp = VK_BLEND_OP_ADD,
504 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
505 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
506 };
507
508 return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
509 color_blend_attachment_disabled);
510}
511
512vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
513 const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
514 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
515 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{
516 .blendEnable = VK_TRUE,
517 .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
518 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
519 .colorBlendOp = VK_BLEND_OP_ADD,
520 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
521 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
522 .alphaBlendOp = VK_BLEND_OP_ADD,
523 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
524 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
525 };
526
527 return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
528 color_blend_attachment_premultiplied);
529}
530
531vk::Pipeline CreateWrappedCoverageBlendingPipeline(
532 const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
533 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
534 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{
535 .blendEnable = VK_TRUE,
536 .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
537 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
538 .colorBlendOp = VK_BLEND_OP_ADD,
539 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
540 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
541 .alphaBlendOp = VK_BLEND_OP_ADD,
542 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
543 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
544 };
545
546 return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
547 color_blend_attachment_coverage);
548}
549
518VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, 550VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
519 VkSampler sampler, VkImageView view, 551 VkSampler sampler, VkImageView view,
520 VkDescriptorSet set, u32 binding) { 552 VkDescriptorSet set, u32 binding) {
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h
index 1104aaa15..5b22f0fa8 100644
--- a/src/video_core/renderer_vulkan/present/util.h
+++ b/src/video_core/renderer_vulkan/present/util.h
@@ -42,8 +42,13 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
42 vk::DescriptorSetLayout& layout); 42 vk::DescriptorSetLayout& layout);
43vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, 43vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
44 vk::PipelineLayout& layout, 44 vk::PipelineLayout& layout,
45 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, 45 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
46 bool enable_blending = false); 46vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
47 const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
48 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
49vk::Pipeline CreateWrappedCoverageBlendingPipeline(
50 const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
51 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
47VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, 52VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
48 VkSampler sampler, VkImageView view, 53 VkSampler sampler, VkImageView view,
49 VkDescriptorSet set, u32 binding); 54 VkDescriptorSet set, u32 binding);
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
index c5db0230d..22ffacf11 100644
--- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
@@ -22,7 +22,7 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format,
22 CreatePipelineLayout(); 22 CreatePipelineLayout();
23 CreateVertexShader(); 23 CreateVertexShader();
24 CreateRenderPass(frame_format); 24 CreateRenderPass(frame_format);
25 CreatePipeline(); 25 CreatePipelines();
26} 26}
27 27
28WindowAdaptPass::~WindowAdaptPass() = default; 28WindowAdaptPass::~WindowAdaptPass() = default;
@@ -34,7 +34,6 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
34 34
35 const VkFramebuffer host_framebuffer{*dst->framebuffer}; 35 const VkFramebuffer host_framebuffer{*dst->framebuffer};
36 const VkRenderPass renderpass{*render_pass}; 36 const VkRenderPass renderpass{*render_pass};
37 const VkPipeline graphics_pipeline{*pipeline};
38 const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; 37 const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
39 const VkExtent2D render_area{ 38 const VkExtent2D render_area{
40 .width = dst->width, 39 .width = dst->width,
@@ -44,9 +43,23 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
44 const size_t layer_count = configs.size(); 43 const size_t layer_count = configs.size();
45 std::vector<PresentPushConstants> push_constants(layer_count); 44 std::vector<PresentPushConstants> push_constants(layer_count);
46 std::vector<VkDescriptorSet> descriptor_sets(layer_count); 45 std::vector<VkDescriptorSet> descriptor_sets(layer_count);
46 std::vector<VkPipeline> graphics_pipelines(layer_count);
47 47
48 auto layer_it = layers.begin(); 48 auto layer_it = layers.begin();
49 for (size_t i = 0; i < layer_count; i++) { 49 for (size_t i = 0; i < layer_count; i++) {
50 switch (configs[i].blending) {
51 case Tegra::BlendMode::Opaque:
52 default:
53 graphics_pipelines[i] = *opaque_pipeline;
54 break;
55 case Tegra::BlendMode::Premultiplied:
56 graphics_pipelines[i] = *premultiplied_pipeline;
57 break;
58 case Tegra::BlendMode::Coverage:
59 graphics_pipelines[i] = *coverage_pipeline;
60 break;
61 }
62
50 layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, 63 layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
51 image_index, configs[i], layout); 64 image_index, configs[i], layout);
52 layer_it++; 65 layer_it++;
@@ -77,8 +90,8 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
77 BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); 90 BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
78 cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); 91 cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
79 92
80 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
81 for (size_t i = 0; i < layer_count; i++) { 93 for (size_t i = 0; i < layer_count; i++) {
94 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]);
82 cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 95 cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
83 push_constants[i]); 96 push_constants[i]);
84 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, 97 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
@@ -129,9 +142,13 @@ void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
129 render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); 142 render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
130} 143}
131 144
132void WindowAdaptPass::CreatePipeline() { 145void WindowAdaptPass::CreatePipelines() {
133 pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, 146 opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
134 std::tie(vertex_shader, fragment_shader), false); 147 std::tie(vertex_shader, fragment_shader));
148 premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline(
149 device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
150 coverage_pipeline = CreateWrappedCoverageBlendingPipeline(
151 device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
135} 152}
136 153
137} // namespace Vulkan 154} // 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
index 0e2edfc31..cf667a4fc 100644
--- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
@@ -42,7 +42,7 @@ private:
42 void CreatePipelineLayout(); 42 void CreatePipelineLayout();
43 void CreateVertexShader(); 43 void CreateVertexShader();
44 void CreateRenderPass(VkFormat frame_format); 44 void CreateRenderPass(VkFormat frame_format);
45 void CreatePipeline(); 45 void CreatePipelines();
46 46
47private: 47private:
48 const Device& device; 48 const Device& device;
@@ -52,7 +52,9 @@ private:
52 vk::ShaderModule vertex_shader; 52 vk::ShaderModule vertex_shader;
53 vk::ShaderModule fragment_shader; 53 vk::ShaderModule fragment_shader;
54 vk::RenderPass render_pass; 54 vk::RenderPass render_pass;
55 vk::Pipeline pipeline; 55 vk::Pipeline opaque_pipeline;
56 vk::Pipeline premultiplied_pipeline;
57 vk::Pipeline coverage_pipeline;
56}; 58};
57 59
58} // namespace Vulkan 60} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 48a105327..d50417116 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -19,7 +19,9 @@
19#include "core/core_timing.h" 19#include "core/core_timing.h"
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/capture.h"
22#include "video_core/gpu.h" 23#include "video_core/gpu.h"
24#include "video_core/present.h"
23#include "video_core/renderer_vulkan/present/util.h" 25#include "video_core/renderer_vulkan/present/util.h"
24#include "video_core/renderer_vulkan/renderer_vulkan.h" 26#include "video_core/renderer_vulkan/renderer_vulkan.h"
25#include "video_core/renderer_vulkan/vk_blit_screen.h" 27#include "video_core/renderer_vulkan/vk_blit_screen.h"
@@ -38,6 +40,20 @@
38 40
39namespace Vulkan { 41namespace Vulkan {
40namespace { 42namespace {
43
44constexpr VkExtent2D CaptureImageSize{
45 .width = VideoCore::Capture::LinearWidth,
46 .height = VideoCore::Capture::LinearHeight,
47};
48
49constexpr VkExtent3D CaptureImageExtent{
50 .width = VideoCore::Capture::LinearWidth,
51 .height = VideoCore::Capture::LinearHeight,
52 .depth = VideoCore::Capture::LinearDepth,
53};
54
55constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
56
41std::string GetReadableVersion(u32 version) { 57std::string GetReadableVersion(u32 version) {
42 return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), 58 return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
43 VK_VERSION_PATCH(version)); 59 VK_VERSION_PATCH(version));
@@ -99,10 +115,15 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
99 render_window.GetFramebufferLayout().height), 115 render_window.GetFramebufferLayout().height),
100 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 116 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
101 surface), 117 surface),
102 blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), 118 blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler,
103 blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), 119 PresentFiltersForDisplay),
120 blit_capture(device_memory, device, memory_allocator, present_manager, scheduler,
121 PresentFiltersForDisplay),
122 blit_applet(device_memory, device, memory_allocator, present_manager, scheduler,
123 PresentFiltersForAppletCapture),
104 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, 124 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
105 scheduler) { 125 scheduler),
126 applet_frame() {
106 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 127 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
107 turbo_mode.emplace(instance, dld); 128 turbo_mode.emplace(instance, dld);
108 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); 129 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
@@ -125,6 +146,8 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
125 146
126 SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); 147 SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
127 148
149 RenderAppletCaptureLayer(framebuffers);
150
128 if (!render_window.IsShown()) { 151 if (!render_window.IsShown()) {
129 return; 152 return;
130 } 153 }
@@ -167,30 +190,20 @@ void RendererVulkan::Report() const {
167 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 190 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
168} 191}
169 192
170void Vulkan::RendererVulkan::RenderScreenshot( 193vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
171 std::span<const Tegra::FramebufferConfig> framebuffers) { 194 const Layout::FramebufferLayout& layout, VkFormat format,
172 if (!renderer_settings.screenshot_requested) { 195 VkDeviceSize buffer_size) {
173 return;
174 }
175
176 constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM};
177 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
178
179 auto frame = [&]() { 196 auto frame = [&]() {
180 Frame f{}; 197 Frame f{};
181 f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, 198 f.image =
182 ScreenshotFormat); 199 CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format);
183 f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); 200 f.image_view = CreateWrappedImageView(device, f.image, format);
184 f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); 201 f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format);
185 return f; 202 return f;
186 }(); 203 }();
187 204
188 blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, 205 auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download);
189 VK_FORMAT_B8G8R8A8_UNORM); 206 blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format);
190
191 const auto dst_buffer = CreateWrappedBuffer(
192 memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4),
193 MemoryUsage::Download);
194 207
195 scheduler.RequestOutsideRenderPassOperationContext(); 208 scheduler.RequestOutsideRenderPassOperationContext();
196 scheduler.Record([&](vk::CommandBuffer cmdbuf) { 209 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
@@ -198,15 +211,68 @@ void Vulkan::RendererVulkan::RenderScreenshot(
198 VkExtent3D{layout.width, layout.height, 1}); 211 VkExtent3D{layout.width, layout.height, 1});
199 }); 212 });
200 213
201 // Ensure the copy is fully completed before saving the screenshot 214 // Ensure the copy is fully completed before saving the capture
202 scheduler.Finish(); 215 scheduler.Finish();
203 216
204 // Copy backing image data to the QImage screenshot buffer 217 // Copy backing image data to the capture buffer
205 dst_buffer.Invalidate(); 218 dst_buffer.Invalidate();
219 return dst_buffer;
220}
221
222void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
223 if (!renderer_settings.screenshot_requested) {
224 return;
225 }
226
227 const auto& layout{renderer_settings.screenshot_framebuffer_layout};
228 const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM,
229 layout.width * layout.height * 4);
230
206 std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(), 231 std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
207 dst_buffer.Mapped().size()); 232 dst_buffer.Mapped().size());
208 renderer_settings.screenshot_complete_callback(false); 233 renderer_settings.screenshot_complete_callback(false);
209 renderer_settings.screenshot_requested = false; 234 renderer_settings.screenshot_requested = false;
210} 235}
211 236
237std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
238 using namespace VideoCore::Capture;
239
240 std::vector<u8> out(VideoCore::Capture::TiledSize);
241
242 if (!applet_frame.image) {
243 return out;
244 }
245
246 const auto dst_buffer =
247 CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download);
248
249 scheduler.RequestOutsideRenderPassOperationContext();
250 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
251 DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent);
252 });
253
254 // Ensure the copy is fully completed before writing the capture
255 scheduler.Finish();
256
257 // Swizzle image data to the capture buffer
258 dst_buffer.Invalidate();
259 Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth,
260 LinearHeight, LinearDepth, BlockHeight, BlockDepth);
261
262 return out;
263}
264
265void RendererVulkan::RenderAppletCaptureLayer(
266 std::span<const Tegra::FramebufferConfig> framebuffers) {
267 if (!applet_frame.image) {
268 applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
269 applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
270 applet_frame.framebuffer = blit_applet.CreateFramebuffer(
271 VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat);
272 }
273
274 blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
275 CaptureFormat);
276}
277
212} // namespace Vulkan 278} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index c6d8a0f21..fb9d83412 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -48,6 +48,8 @@ public:
48 48
49 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; 49 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
50 50
51 std::vector<u8> GetAppletCaptureBuffer() override;
52
51 VideoCore::RasterizerInterface* ReadRasterizer() override { 53 VideoCore::RasterizerInterface* ReadRasterizer() override {
52 return &rasterizer; 54 return &rasterizer;
53 } 55 }
@@ -59,7 +61,11 @@ public:
59private: 61private:
60 void Report() const; 62 void Report() const;
61 63
64 vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
65 const Layout::FramebufferLayout& layout, VkFormat format,
66 VkDeviceSize buffer_size);
62 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); 67 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
68 void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
63 69
64 Core::TelemetrySession& telemetry_session; 70 Core::TelemetrySession& telemetry_session;
65 Tegra::MaxwellDeviceMemoryManager& device_memory; 71 Tegra::MaxwellDeviceMemoryManager& device_memory;
@@ -79,9 +85,12 @@ private:
79 Swapchain swapchain; 85 Swapchain swapchain;
80 PresentManager present_manager; 86 PresentManager present_manager;
81 BlitScreen blit_swapchain; 87 BlitScreen blit_swapchain;
82 BlitScreen blit_screenshot; 88 BlitScreen blit_capture;
89 BlitScreen blit_applet;
83 RasterizerVulkan rasterizer; 90 RasterizerVulkan rasterizer;
84 std::optional<TurboMode> turbo_mode; 91 std::optional<TurboMode> turbo_mode;
92
93 Frame applet_frame;
85}; 94};
86 95
87} // namespace Vulkan 96} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 2275fcc46..b7797f833 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "video_core/framebuffer_config.h" 4#include "video_core/framebuffer_config.h"
5#include "video_core/present.h"
5#include "video_core/renderer_vulkan/present/filters.h" 6#include "video_core/renderer_vulkan/present/filters.h"
6#include "video_core/renderer_vulkan/present/layer.h" 7#include "video_core/renderer_vulkan/present/layer.h"
7#include "video_core/renderer_vulkan/vk_blit_screen.h" 8#include "video_core/renderer_vulkan/vk_blit_screen.h"
@@ -12,9 +13,9 @@ namespace Vulkan {
12 13
13BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, 14BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
14 MemoryAllocator& memory_allocator_, PresentManager& present_manager_, 15 MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
15 Scheduler& scheduler_) 16 Scheduler& scheduler_, const PresentFilters& filters_)
16 : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, 17 : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
17 present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, 18 present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1},
18 swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} 19 swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
19 20
20BlitScreen::~BlitScreen() = default; 21BlitScreen::~BlitScreen() = default;
@@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() {
27 28
28void BlitScreen::SetWindowAdaptPass() { 29void BlitScreen::SetWindowAdaptPass() {
29 layers.clear(); 30 layers.clear();
30 scaling_filter = Settings::values.scaling_filter.GetValue(); 31 scaling_filter = filters.get_scaling_filter();
31 32
32 switch (scaling_filter) { 33 switch (scaling_filter) {
33 case Settings::ScalingFilter::NearestNeighbor: 34 case Settings::ScalingFilter::NearestNeighbor:
@@ -59,7 +60,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
59 bool presentation_recreate_required = false; 60 bool presentation_recreate_required = false;
60 61
61 // Recreate dynamic resources if the adapting filter changed 62 // Recreate dynamic resources if the adapting filter changed
62 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { 63 if (!window_adapt || scaling_filter != filters.get_scaling_filter()) {
63 resource_update_required = true; 64 resource_update_required = true;
64 } 65 }
65 66
@@ -102,7 +103,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
102 103
103 while (layers.size() < framebuffers.size()) { 104 while (layers.size() < framebuffers.size()) {
104 layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, 105 layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
105 window_size, window_adapt->GetDescriptorSetLayout()); 106 window_size, window_adapt->GetDescriptorSetLayout(), filters);
106 } 107 }
107 108
108 // Perform the draw 109 // Perform the draw
@@ -119,8 +120,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l
119 VkFormat current_view_format) { 120 VkFormat current_view_format) {
120 const bool format_updated = 121 const bool format_updated =
121 std::exchange(swapchain_view_format, current_view_format) != current_view_format; 122 std::exchange(swapchain_view_format, current_view_format) != current_view_format;
122 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || 123 if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) {
123 format_updated) {
124 WaitIdle(); 124 WaitIdle();
125 SetWindowAdaptPass(); 125 SetWindowAdaptPass();
126 } 126 }
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index cbdf2d5d0..531c57fc5 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -16,6 +16,8 @@ namespace Core {
16class System; 16class System;
17} 17}
18 18
19struct PresentFilters;
20
19namespace Tegra { 21namespace Tegra {
20struct FramebufferConfig; 22struct FramebufferConfig;
21} 23}
@@ -47,7 +49,7 @@ class BlitScreen {
47public: 49public:
48 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, 50 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
49 MemoryAllocator& memory_allocator, PresentManager& present_manager, 51 MemoryAllocator& memory_allocator, PresentManager& present_manager,
50 Scheduler& scheduler); 52 Scheduler& scheduler, const PresentFilters& filters);
51 ~BlitScreen(); 53 ~BlitScreen();
52 54
53 void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, 55 void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
@@ -70,6 +72,7 @@ private:
70 MemoryAllocator& memory_allocator; 72 MemoryAllocator& memory_allocator;
71 PresentManager& present_manager; 73 PresentManager& present_manager;
72 Scheduler& scheduler; 74 Scheduler& scheduler;
75 const PresentFilters& filters;
73 std::size_t image_count{}; 76 std::size_t image_count{};
74 std::size_t image_index{}; 77 std::size_t image_index{};
75 VkFormat swapchain_view_format{}; 78 VkFormat swapchain_view_format{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index a20c956ff..3a1cc060e 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -746,7 +746,13 @@ std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImage
746 }(); 746 }();
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 ImageViewInfo info{ImageViewType::e2D, view_format};
750 if (config.blending == Tegra::BlendMode::Opaque) {
751 info.x_source = static_cast<u8>(SwizzleSource::R);
752 info.y_source = static_cast<u8>(SwizzleSource::G);
753 info.z_source = static_cast<u8>(SwizzleSource::B);
754 info.w_source = static_cast<u8>(SwizzleSource::OneFloat);
755 }
750 return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], 756 return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
751 slot_images[image_id].IsRescaled()); 757 slot_images[image_id].IsRescaled());
752 }; 758 };
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 76f06da12..0259a8c29 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -41,6 +41,9 @@ add_executable(yuzu
41 configuration/configuration_shared.cpp 41 configuration/configuration_shared.cpp
42 configuration/configuration_shared.h 42 configuration/configuration_shared.h
43 configuration/configure.ui 43 configuration/configure.ui
44 configuration/configure_applets.cpp
45 configuration/configure_applets.h
46 configuration/configure_applets.ui
44 configuration/configure_audio.cpp 47 configuration/configure_audio.cpp
45 configuration/configure_audio.h 48 configuration/configure_audio.h
46 configuration/configure_audio.ui 49 configuration/configure_audio.ui
diff --git a/src/yuzu/configuration/configure_applets.cpp b/src/yuzu/configuration/configure_applets.cpp
new file mode 100644
index 000000000..513ecb548
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.cpp
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/core.h"
6#include "ui_configure_applets.h"
7#include "yuzu/configuration/configuration_shared.h"
8#include "yuzu/configuration/configure_applets.h"
9#include "yuzu/configuration/shared_widget.h"
10
11ConfigureApplets::ConfigureApplets(Core::System& system_,
12 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
13 const ConfigurationShared::Builder& builder, QWidget* parent)
14 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} {
15 ui->setupUi(this);
16
17 Setup(builder);
18
19 SetConfiguration();
20}
21
22ConfigureApplets::~ConfigureApplets() = default;
23
24void ConfigureApplets::changeEvent(QEvent* event) {
25 if (event->type() == QEvent::LanguageChange) {
26 RetranslateUI();
27 }
28
29 QWidget::changeEvent(event);
30}
31
32void ConfigureApplets::RetranslateUI() {
33 ui->retranslateUi(this);
34}
35
36void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) {
37 auto& library_applets_layout = *ui->group_library_applet_modes->layout();
38 std::map<u32, QWidget*> applets_hold{};
39
40 std::vector<Settings::BasicSetting*> settings;
41 auto push = [&settings](auto& list) {
42 for (auto setting : list) {
43 settings.push_back(setting);
44 }
45 };
46
47 push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]);
48
49 for (auto setting : settings) {
50 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
51
52 if (widget == nullptr) {
53 continue;
54 }
55 if (!widget->Valid()) {
56 widget->deleteLater();
57 continue;
58 }
59
60 // Untested applets
61 if (setting->Id() == Settings::values.data_erase_applet_mode.Id() ||
62 setting->Id() == Settings::values.error_applet_mode.Id() ||
63 setting->Id() == Settings::values.net_connect_applet_mode.Id() ||
64 setting->Id() == Settings::values.web_applet_mode.Id() ||
65 setting->Id() == Settings::values.shop_applet_mode.Id() ||
66 setting->Id() == Settings::values.login_share_applet_mode.Id() ||
67 setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() ||
68 setting->Id() == Settings::values.my_page_applet_mode.Id()) {
69 widget->setHidden(true);
70 }
71
72 applets_hold.emplace(setting->Id(), widget);
73 }
74 for (const auto& [label, widget] : applets_hold) {
75 library_applets_layout.addWidget(widget);
76 }
77}
78
79void ConfigureApplets::SetConfiguration() {}
80
81void ConfigureApplets::ApplyConfiguration() {
82 const bool powered_on = system.IsPoweredOn();
83 for (const auto& func : apply_funcs) {
84 func(powered_on);
85 }
86}
diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h
new file mode 100644
index 000000000..54f494d2f
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.h
@@ -0,0 +1,48 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <QWidget>
7#include "yuzu/configuration/configuration_shared.h"
8
9class QCheckBox;
10class QLineEdit;
11class QComboBox;
12class QDateTimeEdit;
13namespace Core {
14class System;
15}
16
17namespace Ui {
18class ConfigureApplets;
19}
20
21namespace ConfigurationShared {
22class Builder;
23}
24
25class ConfigureApplets : public ConfigurationShared::Tab {
26public:
27 explicit ConfigureApplets(Core::System& system_,
28 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
29 const ConfigurationShared::Builder& builder,
30 QWidget* parent = nullptr);
31 ~ConfigureApplets() override;
32
33 void ApplyConfiguration() override;
34 void SetConfiguration() override;
35
36private:
37 void changeEvent(QEvent* event) override;
38 void RetranslateUI();
39
40 void Setup(const ConfigurationShared::Builder& builder);
41
42 std::vector<std::function<void(bool)>> apply_funcs{};
43
44 std::unique_ptr<Ui::ConfigureApplets> ui;
45 bool enabled = false;
46
47 Core::System& system;
48};
diff --git a/src/yuzu/configuration/configure_applets.ui b/src/yuzu/configuration/configure_applets.ui
new file mode 100644
index 000000000..6f2ca66bd
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.ui
@@ -0,0 +1,65 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureApplets</class>
4 <widget class="QWidget" name="ConfigureApplets">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>605</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <property name="accessibleName">
17 <string>Applets</string>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout_1">
20 <item>
21 <layout class="QVBoxLayout" name="verticalLayout">
22 <item>
23 <widget class="QGroupBox" name="group_library_applet_modes">
24 <property name="title">
25 <string>Applet mode preference</string>
26 </property>
27 <layout class="QVBoxLayout">
28 <item>
29 <widget class="QWidget" name="applets_widget" native="true">
30 <layout class="QVBoxLayout" name="verticalLayout_3">
31 <property name="leftMargin">
32 <number>0</number>
33 </property>
34 <property name="topMargin">
35 <number>0</number>
36 </property>
37 <property name="rightMargin">
38 <number>0</number>
39 </property>
40 </layout>
41 </widget>
42 </item>
43 </layout>
44 </widget>
45 </item>
46 </layout>
47 </item>
48 <item>
49 <spacer name="verticalSpacer">
50 <property name="orientation">
51 <enum>Qt::Vertical</enum>
52 </property>
53 <property name="sizeHint" stdset="0">
54 <size>
55 <width>20</width>
56 <height>40</height>
57 </size>
58 </property>
59 </spacer>
60 </item>
61 </layout>
62 </widget>
63 <resources/>
64 <connections/>
65</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index aab54a1cc..37f23388e 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,6 +8,7 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "ui_configure.h" 9#include "ui_configure.h"
10#include "vk_device_info.h" 10#include "vk_device_info.h"
11#include "yuzu/configuration/configure_applets.h"
11#include "yuzu/configuration/configure_audio.h" 12#include "yuzu/configuration/configure_audio.h"
12#include "yuzu/configuration/configure_cpu.h" 13#include "yuzu/configuration/configure_cpu.h"
13#include "yuzu/configuration/configure_debug_tab.h" 14#include "yuzu/configuration/configure_debug_tab.h"
@@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 35 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
35 registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>( 36 registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
36 this, !system_.IsPoweredOn())}, 37 this, !system_.IsPoweredOn())},
38 applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)},
37 audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)}, 39 audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
38 cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)}, 40 cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
39 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, 41 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
@@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
58 60
59 ui->setupUi(this); 61 ui->setupUi(this);
60 62
63 ui->tabWidget->addTab(applets_tab.get(), tr("Applets"));
61 ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); 64 ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
62 ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); 65 ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
63 ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug")); 66 ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug"));
@@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() {
124 debug_tab_tab->ApplyConfiguration(); 127 debug_tab_tab->ApplyConfiguration();
125 web_tab->ApplyConfiguration(); 128 web_tab->ApplyConfiguration();
126 network_tab->ApplyConfiguration(); 129 network_tab->ApplyConfiguration();
130 applets_tab->ApplyConfiguration();
127 system.ApplySettings(); 131 system.ApplySettings();
128 Settings::LogSettings(); 132 Settings::LogSettings();
129} 133}
@@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() {
161 {{tr("General"), 165 {{tr("General"),
162 {general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}}, 166 {general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}},
163 {tr("System"), 167 {tr("System"),
164 {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}}, 168 {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(),
169 applets_tab.get()}},
165 {tr("CPU"), {cpu_tab.get()}}, 170 {tr("CPU"), {cpu_tab.get()}},
166 {tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}}, 171 {tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}},
167 {tr("Audio"), {audio_tab.get()}}, 172 {tr("Audio"), {audio_tab.get()}},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index b28ce288c..d0a24a07b 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -15,6 +15,7 @@ namespace Core {
15class System; 15class System;
16} 16}
17 17
18class ConfigureApplets;
18class ConfigureAudio; 19class ConfigureAudio;
19class ConfigureCpu; 20class ConfigureCpu;
20class ConfigureDebugTab; 21class ConfigureDebugTab;
@@ -75,6 +76,7 @@ private:
75 std::unique_ptr<ConfigurationShared::Builder> builder; 76 std::unique_ptr<ConfigurationShared::Builder> builder;
76 std::vector<ConfigurationShared::Tab*> tab_group; 77 std::vector<ConfigurationShared::Tab*> tab_group;
77 78
79 std::unique_ptr<ConfigureApplets> applets_tab;
78 std::unique_ptr<ConfigureAudio> audio_tab; 80 std::unique_ptr<ConfigureAudio> audio_tab;
79 std::unique_ptr<ConfigureCpu> cpu_tab; 81 std::unique_ptr<ConfigureCpu> cpu_tab;
80 std::unique_ptr<ConfigureDebugTab> debug_tab_tab; 82 std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index ed9c7d859..ce65b2bf1 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
26 26
27 // A setting can be ignored by giving it a blank name 27 // A setting can be ignored by giving it a blank name
28 28
29 // Applets
30 INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral());
31 INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral());
32 INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral());
33 INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral());
34 INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral());
35 INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral());
36 INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral());
37 INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral());
38 INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral());
39 INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral());
40 INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral());
41 INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral());
42 INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral());
43 INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral());
44 INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral());
45
29 // Audio 46 // Audio
30 INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); 47 INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
31 INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); 48 INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
@@ -203,6 +220,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
203#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} 220#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
204 221
205 // Intentionally skipping VSyncMode to let the UI fill that one out 222 // Intentionally skipping VSyncMode to let the UI fill that one out
223 translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
224 {
225 PAIR(AppletMode, HLE, tr("Custom frontend")),
226 PAIR(AppletMode, LLE, tr("Real applet")),
227 }});
206 228
207 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), 229 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
208 { 230 {