diff options
| author | 2024-02-11 20:58:28 -0600 | |
|---|---|---|
| committer | 2024-02-11 20:58:28 -0600 | |
| commit | 2ff45cd0da941dce2564f5c18c580d0283da27bf (patch) | |
| tree | 81fea8c23f9afa39c167288e03ff57eb25413ee4 | |
| parent | Merge pull request #12991 from german77/news2 (diff) | |
| parent | am: use applet program loading for tested versions (diff) | |
| download | yuzu-2ff45cd0da941dce2564f5c18c580d0283da27bf.tar.gz yuzu-2ff45cd0da941dce2564f5c18c580d0283da27bf.tar.xz yuzu-2ff45cd0da941dce2564f5c18c580d0283da27bf.zip | |
Merge pull request #12756 from liamwhite/applet-multiprocess-hwc
general: applet multiprocess
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 | ||
| 33 | SETTING(AppletMode, false); | ||
| 33 | SETTING(AudioEngine, false); | 34 | SETTING(AudioEngine, false); |
| 34 | SETTING(bool, false); | 35 | SETTING(bool, false); |
| 35 | SETTING(int, false); | 36 | SETTING(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 { | |||
| 133 | struct Values { | 133 | struct 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 | ||
| 152 | ENUM(ConsoleMode, Handheld, Docked); | 152 | ENUM(ConsoleMode, Handheld, Docked); |
| 153 | 153 | ||
| 154 | ENUM(AppletMode, HLE, LLE); | ||
| 155 | |||
| 154 | template <typename Type> | 156 | template <typename Type> |
| 155 | inline std::string CanonicalizeEnum(Type id) { | 157 | inline 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 | ||
| 175 | u8 NCA::GetKeyGeneration() const { | ||
| 176 | return reader->GetKeyGeneration(); | ||
| 177 | } | ||
| 178 | |||
| 175 | bool NCA::IsUpdate() const { | 179 | bool 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 | ||
| 131 | enum class LibraryAppletMode : u32 { | 131 | enum 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 | ||
| 246 | void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { | 246 | void 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 | ||
| 17 | namespace { | 18 | namespace { |
| 18 | 19 | ||
| 20 | bool 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 | |||
| 19 | AppletProgramId AppletIdToProgramId(AppletId applet_id) { | 48 | AppletProgramId 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( | 95 | std::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( | 152 | std::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 | ||
| 23 | bool Process::Initialize(u64 program_id) { | 24 | bool 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 | ||
| 290 | Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { | 293 | Result 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 | ||
| 330 | void 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 | |||
| 326 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { | 340 | void 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 | ||
| 23 | bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, | 24 | bool 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 | ||
| 63 | Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, | 70 | Result 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 | ||
| 115 | void Container::CloseSession(SessionId session_id) { | 117 | void 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 | ||
| 51 | class Container { | 52 | class 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 | ||
| 16 | namespace Service::Nvidia::Devices { | 16 | namespace Service::Nvidia::Devices { |
| 17 | 17 | ||
| 18 | namespace { | ||
| 19 | |||
| 20 | Tegra::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 | |||
| 18 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) | 34 | nvdisp_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()} {} |
| 20 | nvdisp_disp0::~nvdisp_disp0() = default; | 36 | nvdisp_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 | ||
| 18 | namespace Service::Nvnflinger { | 20 | namespace Service::Nvnflinger { |
| 19 | 21 | ||
| 20 | namespace { | 22 | namespace { |
| 21 | 23 | ||
| 22 | Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | 24 | Result 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 | |||
| 59 | Result 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 | ||
| 155 | void 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 | |||
| 138 | constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; | 162 | constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; |
| 139 | constexpr u32 SharedBufferBlockLinearBpp = 4; | 163 | constexpr u32 SharedBufferBlockLinearBpp = 4; |
| 140 | 164 | ||
| @@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli | |||
| 186 | 210 | ||
| 187 | FbShareBufferManager::~FbShareBufferManager() = default; | 211 | FbShareBufferManager::~FbShareBufferManager() = default; |
| 188 | 212 | ||
| 189 | Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { | 213 | Result 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 | ||
| 272 | void 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 | |||
| 236 | Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, | 304 | Result 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 | ||
| 253 | Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { | 322 | Result 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 | ||
| 418 | Result 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 | }; |
| 30 | static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); | 33 | static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); |
| 31 | 34 | ||
| 35 | struct FbShareSession; | ||
| 36 | |||
| 32 | class FbShareBufferManager final { | 37 | class FbShareBufferManager final { |
| 33 | public: | 38 | public: |
| 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 | |||
| 48 | private: | 58 | private: |
| 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 | ||
| 75 | struct 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 | ||
| 12 | namespace Service::Nvnflinger { | 12 | namespace Service::Nvnflinger { |
| 13 | 13 | ||
| 14 | // hwc_layer_t::blending values | ||
| 15 | enum 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 | |||
| 14 | struct HwcLayer { | 26 | struct 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 | ||
| 160 | std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { | 160 | std::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 | ||
| 173 | void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 173 | void 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 | ||
| 178 | bool Nvnflinger::OpenLayer(u64 layer_id) { | 179 | bool 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 | ||
| 19 | namespace Common { | 20 | namespace Common { |
| 20 | class Event; | 21 | class 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 | ||
| 6 | namespace Service::VI { | 7 | namespace Service::VI { |
| @@ -8,8 +9,9 @@ namespace Service::VI { | |||
| 8 | Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, | 9 | Layer::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 | ||
| 14 | Layer::~Layer() = default; | 16 | Layer::~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; | |||
| 14 | class BufferQueueProducer; | 14 | class BufferQueueProducer; |
| 15 | } // namespace Service::android | 15 | } // namespace Service::android |
| 16 | 16 | ||
| 17 | namespace Service::Nvnflinger { | ||
| 18 | enum class LayerBlending : u32; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace Service::VI { | 21 | namespace 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 | |||
| 95 | private: | 107 | private: |
| 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 | ||
| 404 | void Config::ReadLibraryAppletValues() { | ||
| 405 | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); | ||
| 406 | |||
| 407 | ReadCategory(Settings::Category::LibraryApplet); | ||
| 408 | |||
| 409 | EndGroup(); | ||
| 410 | } | ||
| 411 | |||
| 404 | void Config::ReadValues() { | 412 | void 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 | ||
| 704 | void Config::SaveLibraryAppletValues() { | ||
| 705 | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); | ||
| 706 | |||
| 707 | WriteCategory(Settings::Category::LibraryApplet); | ||
| 708 | |||
| 709 | EndGroup(); | ||
| 710 | } | ||
| 711 | |||
| 694 | bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { | 712 | bool 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 | |||
| 12 | namespace VideoCore::Capture { | ||
| 13 | |||
| 14 | constexpr u32 BlockHeight = 4; | ||
| 15 | constexpr u32 BlockDepth = 0; | ||
| 16 | constexpr u32 BppLog2 = 2; | ||
| 17 | |||
| 18 | constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM; | ||
| 19 | |||
| 20 | constexpr auto LinearWidth = Layout::ScreenUndocked::Width; | ||
| 21 | constexpr auto LinearHeight = Layout::ScreenUndocked::Height; | ||
| 22 | constexpr auto LinearDepth = 1U; | ||
| 23 | constexpr auto BytesPerPixel = 4U; | ||
| 24 | |||
| 25 | constexpr auto TiledWidth = LinearWidth; | ||
| 26 | constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2); | ||
| 27 | constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2); | ||
| 28 | |||
| 29 | constexpr 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 | ||
| 12 | namespace Tegra { | 12 | namespace Tegra { |
| 13 | 13 | ||
| 14 | enum 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 | ||
| 28 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | 35 | Common::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 | ||
| 519 | std::vector<u8> GPU::GetAppletCaptureBuffer() { | ||
| 520 | return impl->GetAppletCaptureBuffer(); | ||
| 521 | } | ||
| 522 | |||
| 508 | u64 GPU::GetTicks() const { | 523 | u64 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 | 75 | layout (location = 0) in vec2 frag_texcoord; |
| 75 | layout(location = 0) in vec2 frag_texcoord; | ||
| 76 | #endif | ||
| 77 | layout (location = 0) out vec4 frag_color; | 76 | layout (location = 0) out vec4 frag_color; |
| 78 | 77 | ||
| 79 | void CurrFilter(AU2 pos) { | 78 | void 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 | ||
| 73 | void main() { | 73 | void 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 | 71 | layout (location = 0) in vec2 frag_texcoord; |
| 71 | layout(location = 0) in vec2 frag_texcoord; | ||
| 72 | #endif | ||
| 73 | layout (location = 0) out vec4 frag_color; | 72 | layout (location = 0) out vec4 frag_color; |
| 74 | 73 | ||
| 75 | void CurrFilter(AU2 pos) | 74 | void 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; | |||
| 9 | layout (binding = 0) uniform sampler2D color_texture; | 9 | layout (binding = 0) uniform sampler2D color_texture; |
| 10 | 10 | ||
| 11 | void main() { | 11 | void 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 | ||
| 54 | void main() { | 54 | void 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 | ||
| 48 | void main() { | 48 | void 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 | |||
| 8 | static inline Settings::ScalingFilter GetScalingFilter() { | ||
| 9 | return Settings::values.scaling_filter.GetValue(); | ||
| 10 | } | ||
| 11 | |||
| 12 | static inline Settings::AntiAliasing GetAntiAliasing() { | ||
| 13 | return Settings::values.anti_aliasing.GetValue(); | ||
| 14 | } | ||
| 15 | |||
| 16 | static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() { | ||
| 17 | return Settings::ScalingFilter::Bilinear; | ||
| 18 | } | ||
| 19 | |||
| 20 | static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() { | ||
| 21 | return Settings::AntiAliasing::None; | ||
| 22 | } | ||
| 23 | |||
| 24 | struct PresentFilters { | ||
| 25 | Settings::ScalingFilter (*get_scaling_filter)(); | ||
| 26 | Settings::AntiAliasing (*get_anti_aliasing)(); | ||
| 27 | }; | ||
| 28 | |||
| 29 | constexpr PresentFilters PresentFiltersForDisplay{ | ||
| 30 | .get_scaling_filter = &GetScalingFilter, | ||
| 31 | .get_anti_aliasing = &GetAntiAliasing, | ||
| 32 | }; | ||
| 33 | |||
| 34 | constexpr 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 | ||
| 8 | namespace Null { | 9 | namespace 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 | ||
| 26 | std::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 { | |||
| 13 | BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, | 14 | BlitScreen::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 | ||
| 20 | BlitScreen::~BlitScreen() = default; | 21 | BlitScreen::~BlitScreen() = default; |
| 21 | 22 | ||
| 22 | void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, | 23 | void 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 | ||
| 69 | void BlitScreen::CreateWindowAdapt() { | 70 | void 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 { | |||
| 15 | struct FramebufferLayout; | 15 | struct FramebufferLayout; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | struct PresentFilters; | ||
| 19 | |||
| 18 | namespace Tegra { | 20 | namespace Tegra { |
| 19 | struct FramebufferConfig; | 21 | struct 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 | ||
| 56 | private: | 58 | private: |
| 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 | ||
| 15 | namespace OpenGL { | 16 | namespace OpenGL { |
| 16 | 17 | ||
| 17 | Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) | 18 | Layer::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 { | |||
| 13 | struct FramebufferLayout; | 13 | struct FramebufferLayout; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | struct PresentFilters; | ||
| 17 | |||
| 16 | namespace Service::android { | 18 | namespace Service::android { |
| 17 | enum class PixelFormat : u32; | 19 | enum class PixelFormat : u32; |
| 18 | }; | 20 | }; |
| @@ -44,14 +46,15 @@ struct ScreenRectVertex; | |||
| 44 | 46 | ||
| 45 | class Layer { | 47 | class Layer { |
| 46 | public: | 48 | public: |
| 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 | ||
| 56 | private: | 59 | private: |
| 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: | |||
| 65 | private: | 68 | private: |
| 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 | ||
| 38 | void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, | 38 | void 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 | ||
| 36 | private: | 36 | private: |
| 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 | ||
| 126 | RendererOpenGL::~RendererOpenGL() = default; | 136 | RendererOpenGL::~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 | ||
| 162 | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { | 173 | void 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 | |||
| 203 | void 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 | ||
| 215 | void 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 | |||
| 232 | std::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 | ||
| 53 | private: | 55 | private: |
| 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 | ||
| 49 | Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, | 50 | Layer::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 | ||
| 173 | void Layer::SetAntiAliasPass() { | 174 | void 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 { | |||
| 11 | struct FramebufferLayout; | 11 | struct FramebufferLayout; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | struct PresentFilters; | ||
| 15 | |||
| 14 | namespace Tegra { | 16 | namespace Tegra { |
| 15 | struct FramebufferConfig; | 17 | struct FramebufferConfig; |
| 16 | } | 18 | } |
| @@ -37,7 +39,8 @@ class Layer final { | |||
| 37 | public: | 39 | public: |
| 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 | ||
| 365 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | 365 | static 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 | ||
| 493 | vk::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 | |||
| 512 | vk::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 | |||
| 531 | vk::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 | |||
| 518 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | 550 | VkWriteDescriptorSet 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); |
| 43 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | 43 | vk::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); | 46 | vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( |
| 47 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 48 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); | ||
| 49 | vk::Pipeline CreateWrappedCoverageBlendingPipeline( | ||
| 50 | const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, | ||
| 51 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); | ||
| 47 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | 52 | VkWriteDescriptorSet 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 | ||
| 28 | WindowAdaptPass::~WindowAdaptPass() = default; | 28 | WindowAdaptPass::~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 | ||
| 132 | void WindowAdaptPass::CreatePipeline() { | 145 | void 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 | ||
| 47 | private: | 47 | private: |
| 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 | ||
| 39 | namespace Vulkan { | 41 | namespace Vulkan { |
| 40 | namespace { | 42 | namespace { |
| 43 | |||
| 44 | constexpr VkExtent2D CaptureImageSize{ | ||
| 45 | .width = VideoCore::Capture::LinearWidth, | ||
| 46 | .height = VideoCore::Capture::LinearHeight, | ||
| 47 | }; | ||
| 48 | |||
| 49 | constexpr VkExtent3D CaptureImageExtent{ | ||
| 50 | .width = VideoCore::Capture::LinearWidth, | ||
| 51 | .height = VideoCore::Capture::LinearHeight, | ||
| 52 | .depth = VideoCore::Capture::LinearDepth, | ||
| 53 | }; | ||
| 54 | |||
| 55 | constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||
| 56 | |||
| 41 | std::string GetReadableVersion(u32 version) { | 57 | std::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 | ||
| 170 | void Vulkan::RendererVulkan::RenderScreenshot( | 193 | vk::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 | |||
| 222 | void 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 | ||
| 237 | std::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 | |||
| 265 | void 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: | |||
| 59 | private: | 61 | private: |
| 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 | ||
| 13 | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, | 14 | BlitScreen::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 | ||
| 20 | BlitScreen::~BlitScreen() = default; | 21 | BlitScreen::~BlitScreen() = default; |
| @@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() { | |||
| 27 | 28 | ||
| 28 | void BlitScreen::SetWindowAdaptPass() { | 29 | void 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 { | |||
| 16 | class System; | 16 | class System; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | struct PresentFilters; | ||
| 20 | |||
| 19 | namespace Tegra { | 21 | namespace Tegra { |
| 20 | struct FramebufferConfig; | 22 | struct FramebufferConfig; |
| 21 | } | 23 | } |
| @@ -47,7 +49,7 @@ class BlitScreen { | |||
| 47 | public: | 49 | public: |
| 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 | |||
| 11 | ConfigureApplets::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 | |||
| 22 | ConfigureApplets::~ConfigureApplets() = default; | ||
| 23 | |||
| 24 | void ConfigureApplets::changeEvent(QEvent* event) { | ||
| 25 | if (event->type() == QEvent::LanguageChange) { | ||
| 26 | RetranslateUI(); | ||
| 27 | } | ||
| 28 | |||
| 29 | QWidget::changeEvent(event); | ||
| 30 | } | ||
| 31 | |||
| 32 | void ConfigureApplets::RetranslateUI() { | ||
| 33 | ui->retranslateUi(this); | ||
| 34 | } | ||
| 35 | |||
| 36 | void 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 | |||
| 79 | void ConfigureApplets::SetConfiguration() {} | ||
| 80 | |||
| 81 | void 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 | |||
| 9 | class QCheckBox; | ||
| 10 | class QLineEdit; | ||
| 11 | class QComboBox; | ||
| 12 | class QDateTimeEdit; | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Ui { | ||
| 18 | class ConfigureApplets; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace ConfigurationShared { | ||
| 22 | class Builder; | ||
| 23 | } | ||
| 24 | |||
| 25 | class ConfigureApplets : public ConfigurationShared::Tab { | ||
| 26 | public: | ||
| 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 | |||
| 36 | private: | ||
| 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 { | |||
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | class ConfigureApplets; | ||
| 18 | class ConfigureAudio; | 19 | class ConfigureAudio; |
| 19 | class ConfigureCpu; | 20 | class ConfigureCpu; |
| 20 | class ConfigureDebugTab; | 21 | class 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 | { |