mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-11-11 02:34:58 +00:00
Merge pull request #12756 from liamwhite/applet-multiprocess-hwc
general: applet multiprocess
This commit is contained in:
commit
2ff45cd0da
79 changed files with 1173 additions and 255 deletions
|
@ -30,6 +30,7 @@ namespace Settings {
|
||||||
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
|
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
|
||||||
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
|
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
|
||||||
|
|
||||||
|
SETTING(AppletMode, false);
|
||||||
SETTING(AudioEngine, false);
|
SETTING(AudioEngine, false);
|
||||||
SETTING(bool, false);
|
SETTING(bool, false);
|
||||||
SETTING(int, false);
|
SETTING(int, false);
|
||||||
|
@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
|
||||||
return "Debugging";
|
return "Debugging";
|
||||||
case Category::GpuDriver:
|
case Category::GpuDriver:
|
||||||
return "GpuDriver";
|
return "GpuDriver";
|
||||||
|
case Category::LibraryApplet:
|
||||||
|
return "LibraryApplet";
|
||||||
case Category::Miscellaneous:
|
case Category::Miscellaneous:
|
||||||
return "Miscellaneous";
|
return "Miscellaneous";
|
||||||
case Category::Network:
|
case Category::Network:
|
||||||
|
|
|
@ -133,6 +133,38 @@ struct TouchFromButtonMap {
|
||||||
struct Values {
|
struct Values {
|
||||||
Linkage linkage{};
|
Linkage linkage{};
|
||||||
|
|
||||||
|
// Applet
|
||||||
|
Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> player_select_applet_mode{
|
||||||
|
linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> photo_viewer_applet_mode{
|
||||||
|
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> wifi_web_auth_applet_mode{
|
||||||
|
linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
|
||||||
|
Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
|
||||||
|
Category::LibraryApplet};
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
|
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
|
||||||
Category::Audio, Specialization::RuntimeList};
|
Category::Audio, Specialization::RuntimeList};
|
||||||
|
|
|
@ -44,6 +44,7 @@ enum class Category : u32 {
|
||||||
Services,
|
Services,
|
||||||
Paths,
|
Paths,
|
||||||
Linux,
|
Linux,
|
||||||
|
LibraryApplet,
|
||||||
MaxEnum,
|
MaxEnum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||||
|
|
||||||
ENUM(ConsoleMode, Handheld, Docked);
|
ENUM(ConsoleMode, Handheld, Docked);
|
||||||
|
|
||||||
|
ENUM(AppletMode, HLE, LLE);
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
inline std::string CanonicalizeEnum(Type id) {
|
inline std::string CanonicalizeEnum(Type id) {
|
||||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||||
|
|
|
@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const {
|
||||||
return reader->GetSdkAddonVersion();
|
return reader->GetSdkAddonVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8 NCA::GetKeyGeneration() const {
|
||||||
|
return reader->GetKeyGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
bool NCA::IsUpdate() const {
|
bool NCA::IsUpdate() const {
|
||||||
return is_update;
|
return is_update;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ public:
|
||||||
u64 GetTitleId() const;
|
u64 GetTitleId() const;
|
||||||
RightsId GetRightsId() const;
|
RightsId GetRightsId() const;
|
||||||
u32 GetSDKVersion() const;
|
u32 GetSDKVersion() const;
|
||||||
|
u8 GetKeyGeneration() const;
|
||||||
bool IsUpdate() const;
|
bool IsUpdate() const;
|
||||||
|
|
||||||
VirtualFile GetRomFS() const;
|
VirtualFile GetRomFS() const;
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
#include <random>
|
#include <random>
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||||
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/gpu_dirty_memory_manager.h"
|
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||||
#include "core/hle/kernel/k_shared_memory.h"
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
|
@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() {
|
||||||
|
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||||
|
// Register the scoped JIT handler before creating any NCE instances
|
||||||
|
// so that its signal handler will appear first in the signal chain.
|
||||||
|
Core::ScopedJitExecution::RegisterHandler();
|
||||||
|
|
||||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||||
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,9 +130,9 @@ enum class AppletProgramId : u64 {
|
||||||
|
|
||||||
enum class LibraryAppletMode : u32 {
|
enum class LibraryAppletMode : u32 {
|
||||||
AllForeground = 0,
|
AllForeground = 0,
|
||||||
Background = 1,
|
PartialForeground = 1,
|
||||||
NoUI = 2,
|
NoUi = 2,
|
||||||
BackgroundIndirectDisplay = 3,
|
PartialForegroundIndirectDisplay = 3,
|
||||||
AllForegroundInitiallyHidden = 4,
|
AllForegroundInitiallyHidden = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
|
||||||
case LibraryAppletMode::AllForeground:
|
case LibraryAppletMode::AllForeground:
|
||||||
InitializeForeground();
|
InitializeForeground();
|
||||||
break;
|
break;
|
||||||
case LibraryAppletMode::Background:
|
case LibraryAppletMode::PartialForeground:
|
||||||
case LibraryAppletMode::BackgroundIndirectDisplay:
|
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||||
InitializeBackground(applet_mode);
|
InitializePartialForeground(applet_mode);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
|
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
|
||||||
|
@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
|
||||||
InitializeFrontendNormalKeyboard();
|
InitializeFrontendNormalKeyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
|
void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
|
||||||
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
|
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
|
||||||
|
|
||||||
is_background = true;
|
is_background = true;
|
||||||
|
@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
|
||||||
swkbd_inline_initialize_arg.size());
|
swkbd_inline_initialize_arg.size());
|
||||||
|
|
||||||
if (swkbd_initialize_arg.library_applet_mode_flag) {
|
if (swkbd_initialize_arg.library_applet_mode_flag) {
|
||||||
ASSERT(library_applet_mode == LibraryAppletMode::Background);
|
ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay);
|
ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ private:
|
||||||
void InitializeForeground();
|
void InitializeForeground();
|
||||||
|
|
||||||
/// Initializes the inline software keyboard.
|
/// Initializes the inline software keyboard.
|
||||||
void InitializeBackground(LibraryAppletMode library_applet_mode);
|
void InitializePartialForeground(LibraryAppletMode library_applet_mode);
|
||||||
|
|
||||||
/// Processes the text check sent by the application.
|
/// Processes the text check sent by the application.
|
||||||
void ProcessTextCheck();
|
void ProcessTextCheck();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
#include "core/hle/service/am/applet_data_broker.h"
|
#include "core/hle/service/am/applet_data_broker.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
|
@ -16,6 +17,34 @@ namespace Service::AM {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool ShouldCreateGuestApplet(AppletId applet_id) {
|
||||||
|
#define X(Name, name) \
|
||||||
|
if (applet_id == AppletId::Name && \
|
||||||
|
Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
X(Cabinet, cabinet)
|
||||||
|
X(Controller, controller)
|
||||||
|
X(DataErase, data_erase)
|
||||||
|
X(Error, error)
|
||||||
|
X(NetConnect, net_connect)
|
||||||
|
X(ProfileSelect, player_select)
|
||||||
|
X(SoftwareKeyboard, swkbd)
|
||||||
|
X(MiiEdit, mii_edit)
|
||||||
|
X(Web, web)
|
||||||
|
X(Shop, shop)
|
||||||
|
X(PhotoViewer, photo_viewer)
|
||||||
|
X(OfflineWeb, offline_web)
|
||||||
|
X(LoginShare, login_share)
|
||||||
|
X(WebAuth, wifi_web_auth)
|
||||||
|
X(MyPage, my_page)
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||||
switch (applet_id) {
|
switch (applet_id) {
|
||||||
case AppletId::OverlayDisplay:
|
case AppletId::OverlayDisplay:
|
||||||
|
@ -63,17 +92,26 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(
|
std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||||
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
|
std::shared_ptr<Applet> caller_applet,
|
||||||
LibraryAppletMode mode) {
|
AppletId applet_id,
|
||||||
|
LibraryAppletMode mode) {
|
||||||
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
||||||
if (program_id == 0) {
|
if (program_id == 0) {
|
||||||
// Unknown applet
|
// Unknown applet
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: enable other versions of applets
|
||||||
|
enum : u8 {
|
||||||
|
Firmware1400 = 14,
|
||||||
|
Firmware1500 = 15,
|
||||||
|
Firmware1600 = 16,
|
||||||
|
Firmware1700 = 17,
|
||||||
|
};
|
||||||
|
|
||||||
auto process = std::make_unique<Process>(system);
|
auto process = std::make_unique<Process>(system);
|
||||||
if (!process->Initialize(program_id)) {
|
if (!process->Initialize(program_id, Firmware1400, Firmware1700)) {
|
||||||
// Couldn't initialize the guest process
|
// Couldn't initialize the guest process
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -87,24 +125,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||||
// Set focus state
|
// Set focus state
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case LibraryAppletMode::AllForeground:
|
case LibraryAppletMode::AllForeground:
|
||||||
case LibraryAppletMode::NoUI:
|
case LibraryAppletMode::NoUi:
|
||||||
applet->focus_state = FocusState::InFocus;
|
case LibraryAppletMode::PartialForeground:
|
||||||
|
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||||
applet->hid_registration.EnableAppletToGetInput(true);
|
applet->hid_registration.EnableAppletToGetInput(true);
|
||||||
|
applet->focus_state = FocusState::InFocus;
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
break;
|
break;
|
||||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||||
applet->system_buffer_manager.SetWindowVisibility(false);
|
|
||||||
applet->focus_state = FocusState::NotInFocus;
|
|
||||||
applet->hid_registration.EnableAppletToGetInput(false);
|
applet->hid_registration.EnableAppletToGetInput(false);
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
applet->focus_state = FocusState::NotInFocus;
|
||||||
break;
|
applet->system_buffer_manager.SetWindowVisibility(false);
|
||||||
case LibraryAppletMode::Background:
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||||
case LibraryAppletMode::BackgroundIndirectDisplay:
|
|
||||||
default:
|
|
||||||
applet->focus_state = FocusState::Background;
|
|
||||||
applet->hid_registration.EnableAppletToGetInput(true);
|
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,9 +149,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(
|
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||||
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
|
std::shared_ptr<Applet> caller_applet,
|
||||||
LibraryAppletMode mode) {
|
AppletId applet_id,
|
||||||
|
LibraryAppletMode mode) {
|
||||||
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
||||||
|
|
||||||
auto process = std::make_unique<Process>(system);
|
auto process = std::make_unique<Process>(system);
|
||||||
|
@ -163,7 +196,13 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
|
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
|
||||||
applet_mode);
|
applet_mode);
|
||||||
|
|
||||||
auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
|
std::shared_ptr<ILibraryAppletAccessor> library_applet;
|
||||||
|
if (ShouldCreateGuestApplet(applet_id)) {
|
||||||
|
library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode);
|
||||||
|
}
|
||||||
|
if (!library_applet) {
|
||||||
|
library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
|
||||||
|
}
|
||||||
if (!library_applet) {
|
if (!library_applet) {
|
||||||
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
|
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
|
||||||
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
@ -20,7 +21,7 @@ Process::~Process() {
|
||||||
this->Finalize();
|
this->Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Process::Initialize(u64 program_id) {
|
bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||||
// First, ensure we are not holding another process.
|
// First, ensure we are not holding another process.
|
||||||
this->Finalize();
|
this->Finalize();
|
||||||
|
|
||||||
|
@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) {
|
||||||
|
|
||||||
// Attempt to load program NCA.
|
// Attempt to load program NCA.
|
||||||
const FileSys::RegisteredCache* bis_system{};
|
const FileSys::RegisteredCache* bis_system{};
|
||||||
FileSys::VirtualFile nca{};
|
FileSys::VirtualFile nca_raw{};
|
||||||
|
|
||||||
// Get the program NCA from built-in storage.
|
// Get the program NCA from built-in storage.
|
||||||
bis_system = fsc.GetSystemNANDContents();
|
bis_system = fsc.GetSystemNANDContents();
|
||||||
if (bis_system) {
|
if (bis_system) {
|
||||||
nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we retrieved a program NCA.
|
// Ensure we retrieved a program NCA.
|
||||||
if (!nca) {
|
if (!nca_raw) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we have a suitable version.
|
||||||
|
if (minimum_key_generation > 0) {
|
||||||
|
FileSys::NCA nca(nca_raw);
|
||||||
|
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||||
|
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||||
|
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||||
|
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||||
|
nca.GetKeyGeneration());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the appropriate loader to parse this NCA.
|
// Get the appropriate loader to parse this NCA.
|
||||||
auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0);
|
auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0);
|
||||||
|
|
||||||
// Ensure we have a loader which can parse the NCA.
|
// Ensure we have a loader which can parse the NCA.
|
||||||
if (!app_loader) {
|
if (!app_loader) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
explicit Process(Core::System& system);
|
explicit Process(Core::System& system);
|
||||||
~Process();
|
~Process();
|
||||||
|
|
||||||
bool Initialize(u64 program_id);
|
bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||||
void Finalize();
|
void Finalize();
|
||||||
|
|
||||||
bool Run();
|
bool Run();
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/am/am_results.h"
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/am/self_controller.h"
|
#include "core/hle/service/am/self_controller.h"
|
||||||
#include "core/hle/service/caps/caps_su.h"
|
#include "core/hle/service/caps/caps_su.h"
|
||||||
|
#include "core/hle/service/hle_ipc.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
|
@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
|
||||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||||
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
||||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||||
{61, nullptr, "SetMediaPlaybackState"},
|
{61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
|
||||||
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
||||||
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
||||||
{64, nullptr, "SetInputDetectionSourceSet"},
|
{64, nullptr, "SetInputDetectionSourceSet"},
|
||||||
|
@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
|
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
|
||||||
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
|
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id,
|
||||||
|
applet->library_applet_mode)) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u8 state = rp.Pop<u8>();
|
||||||
|
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/hle_ipc.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ private:
|
||||||
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
||||||
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
||||||
void ApproveToDisplay(HLERequestContext& ctx);
|
void ApproveToDisplay(HLERequestContext& ctx);
|
||||||
|
void SetMediaPlaybackState(HLERequestContext& ctx);
|
||||||
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void ReportUserIsActive(HLERequestContext& ctx);
|
void ReportUserIsActive(HLERequestContext& ctx);
|
||||||
|
|
|
@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() {
|
||||||
|
|
||||||
// Clean up shared layers.
|
// Clean up shared layers.
|
||||||
if (m_buffer_sharing_enabled) {
|
if (m_buffer_sharing_enabled) {
|
||||||
|
m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
|
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
|
||||||
AppletId applet_id) {
|
AppletId applet_id, LibraryAppletMode mode) {
|
||||||
if (m_nvnflinger) {
|
if (m_nvnflinger) {
|
||||||
return m_buffer_sharing_enabled;
|
return m_buffer_sharing_enabled;
|
||||||
}
|
}
|
||||||
|
@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
|
||||||
|
if (mode == LibraryAppletMode::PartialForeground ||
|
||||||
|
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
|
||||||
|
blending = Nvnflinger::LayerBlending::Coverage;
|
||||||
|
}
|
||||||
|
|
||||||
const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
|
const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
|
||||||
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
|
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
|
||||||
&m_system_shared_buffer_id, &m_system_shared_layer_id, display_id);
|
m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
|
||||||
|
|
||||||
if (res.IsSuccess()) {
|
if (res.IsSuccess()) {
|
||||||
m_buffer_sharing_enabled = true;
|
m_buffer_sharing_enabled = true;
|
||||||
|
@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {
|
||||||
|
|
||||||
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||||
s32* out_fbshare_layer_index) {
|
s32* out_fbshare_layer_index) {
|
||||||
// TODO
|
if (!m_buffer_sharing_enabled) {
|
||||||
R_SUCCEED();
|
return VI::ResultPermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
|
||||||
|
out_fbshare_layer_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
@ -27,7 +27,8 @@ public:
|
||||||
SystemBufferManager();
|
SystemBufferManager();
|
||||||
~SystemBufferManager();
|
~SystemBufferManager();
|
||||||
|
|
||||||
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id);
|
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
|
||||||
|
LibraryAppletMode mode);
|
||||||
|
|
||||||
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
|
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
|
||||||
u64* out_system_shared_layer_id) {
|
u64* out_system_shared_layer_id) {
|
||||||
|
|
|
@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
|
||||||
applet->hid_registration.EnableAppletToGetInput(visible);
|
applet->hid_registration.EnableAppletToGetInput(visible);
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
|
||||||
applet->focus_state = FocusState::InFocus;
|
applet->focus_state = FocusState::InFocus;
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||||
} else {
|
} else {
|
||||||
applet->focus_state = FocusState::NotInFocus;
|
applet->focus_state = FocusState::NotInFocus;
|
||||||
|
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||||
}
|
}
|
||||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (session.process == process) {
|
if (session.process == process) {
|
||||||
|
session.ref_count++;
|
||||||
return session.id;
|
return session.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||||
}
|
}
|
||||||
auto& session = impl->sessions[new_id];
|
auto& session = impl->sessions[new_id];
|
||||||
session.is_active = true;
|
session.is_active = true;
|
||||||
|
session.ref_count = 1;
|
||||||
// Optimization
|
// Optimization
|
||||||
if (process->IsApplication()) {
|
if (process->IsApplication()) {
|
||||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||||
|
@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||||
|
|
||||||
void Container::CloseSession(SessionId session_id) {
|
void Container::CloseSession(SessionId session_id) {
|
||||||
std::scoped_lock lk(impl->session_guard);
|
std::scoped_lock lk(impl->session_guard);
|
||||||
impl->file.UnmapAllHandles(session_id);
|
|
||||||
auto& session = impl->sessions[session_id.id];
|
auto& session = impl->sessions[session_id.id];
|
||||||
|
if (--session.ref_count > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
impl->file.UnmapAllHandles(session_id);
|
||||||
auto& smmu = impl->host1x.MemoryManager();
|
auto& smmu = impl->host1x.MemoryManager();
|
||||||
if (session.has_preallocated_area) {
|
if (session.has_preallocated_area) {
|
||||||
const DAddr region_start = session.mapper->GetRegionStart();
|
const DAddr region_start = session.mapper->GetRegionStart();
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct Session {
|
||||||
bool has_preallocated_area{};
|
bool has_preallocated_area{};
|
||||||
std::unique_ptr<HeapMapper> mapper{};
|
std::unique_ptr<HeapMapper> mapper{};
|
||||||
bool is_active{};
|
bool is_active{};
|
||||||
|
s32 ref_count{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Container {
|
class Container {
|
||||||
|
|
|
@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
|
||||||
}();
|
}();
|
||||||
|
|
||||||
for (auto& [id, handle] : handles_copy) {
|
for (auto& [id, handle] : handles_copy) {
|
||||||
if (handle->session_id.id == session_id.id) {
|
{
|
||||||
FreeHandle(id, false);
|
std::scoped_lock lk{handle->mutex};
|
||||||
|
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
FreeHandle(id, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,22 @@
|
||||||
|
|
||||||
namespace Service::Nvidia::Devices {
|
namespace Service::Nvidia::Devices {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) {
|
||||||
|
switch (blending) {
|
||||||
|
case Service::Nvnflinger::LayerBlending::None:
|
||||||
|
default:
|
||||||
|
return Tegra::BlendMode::Opaque;
|
||||||
|
case Service::Nvnflinger::LayerBlending::Premultiplied:
|
||||||
|
return Tegra::BlendMode::Premultiplied;
|
||||||
|
case Service::Nvnflinger::LayerBlending::Coverage:
|
||||||
|
return Tegra::BlendMode::Coverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
|
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
|
||||||
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
|
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
|
||||||
nvdisp_disp0::~nvdisp_disp0() = default;
|
nvdisp_disp0::~nvdisp_disp0() = default;
|
||||||
|
@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
|
||||||
.pixel_format = layer.format,
|
.pixel_format = layer.format,
|
||||||
.transform_flags = layer.transform,
|
.transform_flags = layer.transform,
|
||||||
.crop_rect = layer.crop_rect,
|
.crop_rect = layer.crop_rect,
|
||||||
|
.blending = ConvertBlending(layer.blending),
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||||
|
|
|
@ -14,24 +14,20 @@
|
||||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||||
#include "core/hle/service/vi/vi_results.h"
|
#include "core/hle/service/vi/vi_results.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/host1x/host1x.h"
|
||||||
|
|
||||||
namespace Service::Nvnflinger {
|
namespace Service::Nvnflinger {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
|
||||||
std::unique_ptr<Kernel::KPageGroup>* out_page_group,
|
Core::System& system, u32 size) {
|
||||||
Core::System& system, u32 size) {
|
|
||||||
using Core::Memory::YUZU_PAGESIZE;
|
using Core::Memory::YUZU_PAGESIZE;
|
||||||
|
|
||||||
// Allocate memory for the system shared buffer.
|
// Allocate memory for the system shared buffer.
|
||||||
// FIXME: Because the gmmu can only point to cpu addresses, we need
|
|
||||||
// to map this in the application space to allow it to be used.
|
|
||||||
// FIXME: Add proper smmu emulation.
|
|
||||||
// FIXME: This memory belongs to vi's .data section.
|
// FIXME: This memory belongs to vi's .data section.
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
auto* process = system.ApplicationProcess();
|
|
||||||
auto& page_table = process->GetPageTable();
|
|
||||||
|
|
||||||
// Hold a temporary page group reference while we try to map it.
|
// Hold a temporary page group reference while we try to map it.
|
||||||
auto pg = std::make_unique<Kernel::KPageGroup>(
|
auto pg = std::make_unique<Kernel::KPageGroup>(
|
||||||
|
@ -43,6 +39,30 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||||
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
|
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
|
||||||
Kernel::KMemoryManager::Direction::FromBack)));
|
Kernel::KMemoryManager::Direction::FromBack)));
|
||||||
|
|
||||||
|
// Fill the output data with red.
|
||||||
|
for (auto& block : *pg) {
|
||||||
|
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
|
||||||
|
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
|
||||||
|
|
||||||
|
for (; start < end; start++) {
|
||||||
|
*start = 0xFF0000FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the mapped page group.
|
||||||
|
*out_page_group = std::move(pg);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||||
|
std::unique_ptr<Kernel::KPageGroup>& pg,
|
||||||
|
Kernel::KProcess* process, Core::System& system) {
|
||||||
|
using Core::Memory::YUZU_PAGESIZE;
|
||||||
|
|
||||||
|
auto& page_table = process->GetPageTable();
|
||||||
|
|
||||||
// Get bounds of where mapping is possible.
|
// Get bounds of where mapping is possible.
|
||||||
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
||||||
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
||||||
|
@ -64,9 +84,6 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||||
// Return failure, if necessary
|
// Return failure, if necessary
|
||||||
R_UNLESS(i < 64, res);
|
R_UNLESS(i < 64, res);
|
||||||
|
|
||||||
// Return the mapped page group.
|
|
||||||
*out_page_group = std::move(pg);
|
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
@ -135,6 +152,13 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
|
||||||
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
|
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
|
||||||
|
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
|
||||||
|
ASSERT(nvmap != nullptr);
|
||||||
|
|
||||||
|
R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
|
||||||
|
}
|
||||||
|
|
||||||
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
|
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
|
||||||
constexpr u32 SharedBufferBlockLinearBpp = 4;
|
constexpr u32 SharedBufferBlockLinearBpp = 4;
|
||||||
|
|
||||||
|
@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli
|
||||||
|
|
||||||
FbShareBufferManager::~FbShareBufferManager() = default;
|
FbShareBufferManager::~FbShareBufferManager() = default;
|
||||||
|
|
||||||
Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) {
|
Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||||
|
u64* out_layer_handle, u64 display_id,
|
||||||
|
LayerBlending blending) {
|
||||||
std::scoped_lock lk{m_guard};
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
// Ensure we have not already created a buffer.
|
// Ensure we haven't already created.
|
||||||
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed);
|
const u64 aruid = owner_process->GetProcessId();
|
||||||
|
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
|
||||||
|
|
||||||
// Allocate memory and space for the shared buffer.
|
// Allocate memory for the shared buffer if needed.
|
||||||
Common::ProcessAddress map_address;
|
if (!m_buffer_page_group) {
|
||||||
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address),
|
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
|
||||||
std::addressof(m_buffer_page_group), m_system,
|
SharedBufferSize));
|
||||||
SharedBufferSize));
|
|
||||||
|
// Record buffer id.
|
||||||
|
m_buffer_id = m_next_buffer_id++;
|
||||||
|
|
||||||
|
// Record display id.
|
||||||
|
m_display_id = display_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map into process.
|
||||||
|
Common::ProcessAddress map_address{};
|
||||||
|
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||||
|
owner_process, m_system));
|
||||||
|
|
||||||
|
// Create new session.
|
||||||
|
auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{});
|
||||||
|
auto& session = it->second;
|
||||||
|
|
||||||
auto& container = m_nvdrv->GetContainer();
|
auto& container = m_nvdrv->GetContainer();
|
||||||
m_session_id = container.OpenSession(m_system.ApplicationProcess());
|
session.session_id = container.OpenSession(owner_process);
|
||||||
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
|
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
|
||||||
|
|
||||||
// Create an nvmap handle for the buffer and assign the memory to it.
|
// Create an nvmap handle for the buffer and assign the memory to it.
|
||||||
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
|
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
|
||||||
map_address, SharedBufferSize));
|
session.nvmap_fd, map_address, SharedBufferSize));
|
||||||
|
|
||||||
// Record the display id.
|
|
||||||
m_display_id = display_id;
|
|
||||||
|
|
||||||
// Create and open a layer for the display.
|
// Create and open a layer for the display.
|
||||||
m_layer_id = m_flinger.CreateLayer(m_display_id).value();
|
session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value();
|
||||||
m_flinger.OpenLayer(m_layer_id);
|
m_flinger.OpenLayer(session.layer_id);
|
||||||
|
|
||||||
// Set up the buffer.
|
|
||||||
m_buffer_id = m_next_buffer_id++;
|
|
||||||
|
|
||||||
// Get the layer.
|
// Get the layer.
|
||||||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id);
|
VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id);
|
||||||
ASSERT(layer != nullptr);
|
ASSERT(layer != nullptr);
|
||||||
|
|
||||||
// Get the producer and set preallocated buffers.
|
// Get the producer and set preallocated buffers.
|
||||||
auto& producer = layer->GetBufferQueue();
|
auto& producer = layer->GetBufferQueue();
|
||||||
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle);
|
MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
|
||||||
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle);
|
MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
|
||||||
|
|
||||||
// Assign outputs.
|
// Assign outputs.
|
||||||
*out_buffer_id = m_buffer_id;
|
*out_buffer_id = m_buffer_id;
|
||||||
*out_layer_id = m_layer_id;
|
*out_layer_handle = session.layer_id;
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
if (m_buffer_id == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 aruid = owner_process->GetProcessId();
|
||||||
|
const auto it = m_sessions.find(aruid);
|
||||||
|
if (it == m_sessions.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& session = it->second;
|
||||||
|
|
||||||
|
// Destroy the layer.
|
||||||
|
m_flinger.DestroyLayer(session.layer_id);
|
||||||
|
|
||||||
|
// Close nvmap handle.
|
||||||
|
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
|
||||||
|
|
||||||
|
// Close nvmap device.
|
||||||
|
m_nvdrv->Close(session.nvmap_fd);
|
||||||
|
|
||||||
|
// Close session.
|
||||||
|
auto& container = m_nvdrv->GetContainer();
|
||||||
|
container.CloseSession(session.session_id);
|
||||||
|
|
||||||
|
// Erase.
|
||||||
|
m_sessions.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||||
s32* out_nvmap_handle,
|
s32* out_nvmap_handle,
|
||||||
SharedMemoryPoolLayout* out_pool_layout,
|
SharedMemoryPoolLayout* out_pool_layout,
|
||||||
|
@ -242,17 +310,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||||
|
|
||||||
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
|
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
|
||||||
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
|
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
|
||||||
|
R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);
|
||||||
|
|
||||||
*out_pool_layout = SharedBufferPoolLayout;
|
*out_pool_layout = SharedBufferPoolLayout;
|
||||||
*out_buffer_size = SharedBufferSize;
|
*out_buffer_size = SharedBufferSize;
|
||||||
*out_nvmap_handle = m_buffer_nvmap_handle;
|
*out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
|
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
|
||||||
// Ensure the layer id is valid.
|
// Ensure the layer id is valid.
|
||||||
R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound);
|
R_UNLESS(layer_id > 0, VI::ResultNotFound);
|
||||||
|
|
||||||
// Get the layer.
|
// Get the layer.
|
||||||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
|
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
|
||||||
|
@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||||
android::Status::NoError,
|
android::Status::NoError,
|
||||||
VI::ResultOperationFailed);
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
ON_RESULT_FAILURE {
|
||||||
|
producer.CancelBuffer(static_cast<s32>(slot), fence);
|
||||||
|
};
|
||||||
|
|
||||||
// Queue the buffer to the producer.
|
// Queue the buffer to the producer.
|
||||||
android::QueueBufferInput input{};
|
android::QueueBufferInput input{};
|
||||||
android::QueueBufferOutput output{};
|
android::QueueBufferOutput output{};
|
||||||
|
@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
|
||||||
|
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
|
||||||
|
Common::ScratchBuffer<u32> scratch;
|
||||||
|
|
||||||
|
// TODO: this could be optimized
|
||||||
|
s64 e = -1280 * 768 * 4;
|
||||||
|
for (auto& block : *m_buffer_page_group) {
|
||||||
|
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
|
||||||
|
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
|
||||||
|
|
||||||
|
for (; start < end; start++) {
|
||||||
|
*start = 0;
|
||||||
|
|
||||||
|
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
|
||||||
|
*start = capture_buffer[e];
|
||||||
|
}
|
||||||
|
e++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
|
||||||
|
m_system.GPU().InvalidateRegion(addr, end - start);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_was_written = true;
|
||||||
|
*out_layer_index = 1;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::Nvnflinger
|
} // namespace Service::Nvnflinger
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
#include "core/hle/service/nvdrv/core/container.h"
|
#include "core/hle/service/nvdrv/core/container.h"
|
||||||
#include "core/hle/service/nvdrv/nvdata.h"
|
#include "core/hle/service/nvdrv/nvdata.h"
|
||||||
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||||
|
|
||||||
|
@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
|
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
|
||||||
|
|
||||||
|
struct FbShareSession;
|
||||||
|
|
||||||
class FbShareBufferManager final {
|
class FbShareBufferManager final {
|
||||||
public:
|
public:
|
||||||
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
|
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
|
||||||
std::shared_ptr<Nvidia::Module> nvdrv);
|
std::shared_ptr<Nvidia::Module> nvdrv);
|
||||||
~FbShareBufferManager();
|
~FbShareBufferManager();
|
||||||
|
|
||||||
Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id);
|
Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
|
||||||
|
u64 display_id, LayerBlending blending);
|
||||||
|
void Finalize(Kernel::KProcess* owner_process);
|
||||||
|
|
||||||
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
|
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
|
||||||
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
|
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
|
||||||
u64 applet_resource_user_id);
|
u64 applet_resource_user_id);
|
||||||
|
@ -45,6 +53,8 @@ public:
|
||||||
u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
|
u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
|
||||||
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
||||||
|
|
||||||
|
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
|
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
|
||||||
|
|
||||||
|
@ -52,11 +62,8 @@ private:
|
||||||
u64 m_next_buffer_id = 1;
|
u64 m_next_buffer_id = 1;
|
||||||
u64 m_display_id = 0;
|
u64 m_display_id = 0;
|
||||||
u64 m_buffer_id = 0;
|
u64 m_buffer_id = 0;
|
||||||
u64 m_layer_id = 0;
|
|
||||||
u32 m_buffer_nvmap_handle = 0;
|
|
||||||
SharedMemoryPoolLayout m_pool_layout = {};
|
SharedMemoryPoolLayout m_pool_layout = {};
|
||||||
Nvidia::DeviceFD m_nvmap_fd = {};
|
std::map<u64, FbShareSession> m_sessions;
|
||||||
Nvidia::NvCore::SessionId m_session_id = {};
|
|
||||||
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
|
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
|
||||||
|
|
||||||
std::mutex m_guard;
|
std::mutex m_guard;
|
||||||
|
@ -65,4 +72,11 @@ private:
|
||||||
std::shared_ptr<Nvidia::Module> m_nvdrv;
|
std::shared_ptr<Nvidia::Module> m_nvdrv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FbShareSession {
|
||||||
|
Nvidia::DeviceFD nvmap_fd = {};
|
||||||
|
Nvidia::NvCore::SessionId session_id = {};
|
||||||
|
u64 layer_id = {};
|
||||||
|
u32 buffer_nvmap_handle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvnflinger
|
} // namespace Service::Nvnflinger
|
||||||
|
|
|
@ -86,6 +86,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
||||||
.height = igbp_buffer.Height(),
|
.height = igbp_buffer.Height(),
|
||||||
.stride = igbp_buffer.Stride(),
|
.stride = igbp_buffer.Stride(),
|
||||||
.z_index = 0,
|
.z_index = 0,
|
||||||
|
.blending = layer.GetBlending(),
|
||||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||||
.crop_rect = item.crop,
|
.crop_rect = item.crop,
|
||||||
.acquire_fence = item.fence,
|
.acquire_fence = item.fence,
|
||||||
|
|
|
@ -11,6 +11,18 @@
|
||||||
|
|
||||||
namespace Service::Nvnflinger {
|
namespace Service::Nvnflinger {
|
||||||
|
|
||||||
|
// hwc_layer_t::blending values
|
||||||
|
enum class LayerBlending : u32 {
|
||||||
|
// No blending
|
||||||
|
None = 0x100,
|
||||||
|
|
||||||
|
// ONE / ONE_MINUS_SRC_ALPHA
|
||||||
|
Premultiplied = 0x105,
|
||||||
|
|
||||||
|
// SRC_ALPHA / ONE_MINUS_SRC_ALPHA
|
||||||
|
Coverage = 0x405,
|
||||||
|
};
|
||||||
|
|
||||||
struct HwcLayer {
|
struct HwcLayer {
|
||||||
u32 buffer_handle;
|
u32 buffer_handle;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
@ -19,6 +31,7 @@ struct HwcLayer {
|
||||||
u32 height;
|
u32 height;
|
||||||
u32 stride;
|
u32 stride;
|
||||||
s32 z_index;
|
s32 z_index;
|
||||||
|
LayerBlending blending;
|
||||||
android::BufferTransformFlags transform;
|
android::BufferTransformFlags transform;
|
||||||
Common::Rectangle<int> crop_rect;
|
Common::Rectangle<int> crop_rect;
|
||||||
android::Fence acquire_fence;
|
android::Fence acquire_fence;
|
||||||
|
|
|
@ -157,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
|
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
|
||||||
const auto lock_guard = Lock();
|
const auto lock_guard = Lock();
|
||||||
auto* const display = FindDisplay(display_id);
|
auto* const display = FindDisplay(display_id);
|
||||||
|
|
||||||
|
@ -166,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 layer_id = next_layer_id++;
|
const u64 layer_id = next_layer_id++;
|
||||||
CreateLayerAtId(*display, layer_id);
|
CreateLayerAtId(*display, layer_id, blending);
|
||||||
return layer_id;
|
return layer_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
|
||||||
const auto buffer_id = next_buffer_queue_id++;
|
const auto buffer_id = next_buffer_queue_id++;
|
||||||
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
|
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
|
||||||
|
display.FindLayer(layer_id)->SetBlending(blending);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Nvnflinger::OpenLayer(u64 layer_id) {
|
bool Nvnflinger::OpenLayer(u64 layer_id) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
class Event;
|
class Event;
|
||||||
|
@ -72,7 +73,8 @@ public:
|
||||||
/// Creates a layer on the specified display and returns the layer ID.
|
/// Creates a layer on the specified display and returns the layer ID.
|
||||||
///
|
///
|
||||||
/// If an invalid display ID is specified, then an empty optional is returned.
|
/// If an invalid display ID is specified, then an empty optional is returned.
|
||||||
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
|
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
|
||||||
|
LayerBlending blending = LayerBlending::None);
|
||||||
|
|
||||||
/// Opens a layer on all displays for the given layer ID.
|
/// Opens a layer on all displays for the given layer ID.
|
||||||
bool OpenLayer(u64 layer_id);
|
bool OpenLayer(u64 layer_id);
|
||||||
|
@ -128,7 +130,7 @@ private:
|
||||||
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
|
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
|
||||||
|
|
||||||
/// Creates a layer with the specified layer ID in the desired display.
|
/// Creates a layer with the specified layer ID in the desired display.
|
||||||
void CreateLayerAtId(VI::Display& display, u64 layer_id);
|
void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
|
||||||
|
|
||||||
void SplitVSync(std::stop_token stop_token);
|
void SplitVSync(std::stop_token stop_token);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||||
|
|
||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
|
@ -8,8 +9,9 @@ namespace Service::VI {
|
||||||
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
|
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
|
||||||
android::BufferQueueProducer& binder_,
|
android::BufferQueueProducer& binder_,
|
||||||
std::shared_ptr<android::BufferItemConsumer>&& consumer_)
|
std::shared_ptr<android::BufferItemConsumer>&& consumer_)
|
||||||
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_},
|
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
|
||||||
consumer{std::move(consumer_)}, open{false}, visible{true} {}
|
consumer_)},
|
||||||
|
blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
|
||||||
|
|
||||||
Layer::~Layer() = default;
|
Layer::~Layer() = default;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@ class BufferQueueCore;
|
||||||
class BufferQueueProducer;
|
class BufferQueueProducer;
|
||||||
} // namespace Service::android
|
} // namespace Service::android
|
||||||
|
|
||||||
|
namespace Service::Nvnflinger {
|
||||||
|
enum class LayerBlending : u32;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
|
|
||||||
/// Represents a single display layer.
|
/// Represents a single display layer.
|
||||||
|
@ -92,12 +96,21 @@ public:
|
||||||
return !std::exchange(open, true);
|
return !std::exchange(open, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nvnflinger::LayerBlending GetBlending() {
|
||||||
|
return blending;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetBlending(Nvnflinger::LayerBlending b) {
|
||||||
|
blending = b;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const u64 layer_id;
|
const u64 layer_id;
|
||||||
const u32 binder_id;
|
const u32 binder_id;
|
||||||
android::BufferQueueCore& core;
|
android::BufferQueueCore& core;
|
||||||
android::BufferQueueProducer& binder;
|
android::BufferQueueProducer& binder;
|
||||||
std::shared_ptr<android::BufferItemConsumer> consumer;
|
std::shared_ptr<android::BufferItemConsumer> consumer;
|
||||||
|
Service::Nvnflinger::LayerBlending blending;
|
||||||
bool open;
|
bool open;
|
||||||
bool visible;
|
bool visible;
|
||||||
};
|
};
|
||||||
|
|
|
@ -401,6 +401,14 @@ void Config::ReadNetworkValues() {
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::ReadLibraryAppletValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
|
||||||
|
|
||||||
|
ReadCategory(Settings::Category::LibraryApplet);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
void Config::ReadValues() {
|
void Config::ReadValues() {
|
||||||
if (global) {
|
if (global) {
|
||||||
ReadDataStorageValues();
|
ReadDataStorageValues();
|
||||||
|
@ -410,6 +418,7 @@ void Config::ReadValues() {
|
||||||
ReadServiceValues();
|
ReadServiceValues();
|
||||||
ReadWebServiceValues();
|
ReadWebServiceValues();
|
||||||
ReadMiscellaneousValues();
|
ReadMiscellaneousValues();
|
||||||
|
ReadLibraryAppletValues();
|
||||||
}
|
}
|
||||||
ReadControlValues();
|
ReadControlValues();
|
||||||
ReadCoreValues();
|
ReadCoreValues();
|
||||||
|
@ -511,6 +520,7 @@ void Config::SaveValues() {
|
||||||
SaveNetworkValues();
|
SaveNetworkValues();
|
||||||
SaveWebServiceValues();
|
SaveWebServiceValues();
|
||||||
SaveMiscellaneousValues();
|
SaveMiscellaneousValues();
|
||||||
|
SaveLibraryAppletValues();
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(Config, "Saving only generic configuration values");
|
LOG_DEBUG(Config, "Saving only generic configuration values");
|
||||||
}
|
}
|
||||||
|
@ -691,6 +701,14 @@ void Config::SaveWebServiceValues() {
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::SaveLibraryAppletValues() {
|
||||||
|
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
|
||||||
|
|
||||||
|
WriteCategory(Settings::Category::LibraryApplet);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
|
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
|
||||||
std::string full_key = GetFullKey(key, false);
|
std::string full_key = GetFullKey(key, false);
|
||||||
if (!default_value.has_value()) {
|
if (!default_value.has_value()) {
|
||||||
|
|
|
@ -88,6 +88,7 @@ protected:
|
||||||
void ReadSystemValues();
|
void ReadSystemValues();
|
||||||
void ReadWebServiceValues();
|
void ReadWebServiceValues();
|
||||||
void ReadNetworkValues();
|
void ReadNetworkValues();
|
||||||
|
void ReadLibraryAppletValues();
|
||||||
|
|
||||||
// Read platform specific sections
|
// Read platform specific sections
|
||||||
virtual void ReadHidbusValues() = 0;
|
virtual void ReadHidbusValues() = 0;
|
||||||
|
@ -121,6 +122,7 @@ protected:
|
||||||
void SaveScreenshotValues();
|
void SaveScreenshotValues();
|
||||||
void SaveSystemValues();
|
void SaveSystemValues();
|
||||||
void SaveWebServiceValues();
|
void SaveWebServiceValues();
|
||||||
|
void SaveLibraryAppletValues();
|
||||||
|
|
||||||
// Save platform specific sections
|
// Save platform specific sections
|
||||||
virtual void SaveHidbusValues() = 0;
|
virtual void SaveHidbusValues() = 0;
|
||||||
|
|
|
@ -18,6 +18,7 @@ add_library(video_core STATIC
|
||||||
buffer_cache/usage_tracker.h
|
buffer_cache/usage_tracker.h
|
||||||
buffer_cache/word_manager.h
|
buffer_cache/word_manager.h
|
||||||
cache_types.h
|
cache_types.h
|
||||||
|
capture.h
|
||||||
cdma_pusher.cpp
|
cdma_pusher.cpp
|
||||||
cdma_pusher.h
|
cdma_pusher.h
|
||||||
compatible_formats.cpp
|
compatible_formats.cpp
|
||||||
|
@ -101,6 +102,7 @@ add_library(video_core STATIC
|
||||||
memory_manager.cpp
|
memory_manager.cpp
|
||||||
memory_manager.h
|
memory_manager.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
present.h
|
||||||
pte_kind.h
|
pte_kind.h
|
||||||
query_cache/bank_base.h
|
query_cache/bank_base.h
|
||||||
query_cache/query_base.h
|
query_cache/query_base.h
|
||||||
|
|
36
src/video_core/capture.h
Normal file
36
src/video_core/capture.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "video_core/surface.h"
|
||||||
|
|
||||||
|
namespace VideoCore::Capture {
|
||||||
|
|
||||||
|
constexpr u32 BlockHeight = 4;
|
||||||
|
constexpr u32 BlockDepth = 0;
|
||||||
|
constexpr u32 BppLog2 = 2;
|
||||||
|
|
||||||
|
constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM;
|
||||||
|
|
||||||
|
constexpr auto LinearWidth = Layout::ScreenUndocked::Width;
|
||||||
|
constexpr auto LinearHeight = Layout::ScreenUndocked::Height;
|
||||||
|
constexpr auto LinearDepth = 1U;
|
||||||
|
constexpr auto BytesPerPixel = 4U;
|
||||||
|
|
||||||
|
constexpr auto TiledWidth = LinearWidth;
|
||||||
|
constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2);
|
||||||
|
constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2);
|
||||||
|
|
||||||
|
constexpr Layout::FramebufferLayout Layout{
|
||||||
|
.width = LinearWidth,
|
||||||
|
.height = LinearHeight,
|
||||||
|
.screen = {0, 0, LinearWidth, LinearHeight},
|
||||||
|
.is_srgb = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCore::Capture
|
|
@ -11,6 +11,12 @@
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
|
enum class BlendMode {
|
||||||
|
Opaque,
|
||||||
|
Premultiplied,
|
||||||
|
Coverage,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct describing framebuffer configuration
|
* Struct describing framebuffer configuration
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +29,7 @@ struct FramebufferConfig {
|
||||||
Service::android::PixelFormat pixel_format{};
|
Service::android::PixelFormat pixel_format{};
|
||||||
Service::android::BufferTransformFlags transform_flags{};
|
Service::android::BufferTransformFlags transform_flags{};
|
||||||
Common::Rectangle<int> crop_rect{};
|
Common::Rectangle<int> crop_rect{};
|
||||||
|
BlendMode blending{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
|
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
|
||||||
|
|
|
@ -347,6 +347,17 @@ struct GPU::Impl {
|
||||||
WaitForSyncOperation(wait_fence);
|
WaitForSyncOperation(wait_fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> GetAppletCaptureBuffer() {
|
||||||
|
std::vector<u8> out;
|
||||||
|
|
||||||
|
const auto wait_fence =
|
||||||
|
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
|
||||||
|
gpu_thread.TickGPU();
|
||||||
|
WaitForSyncOperation(wait_fence);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
GPU& gpu;
|
GPU& gpu;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Host1x::Host1x& host1x;
|
Host1x::Host1x& host1x;
|
||||||
|
@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
||||||
impl->RequestComposite(std::move(layers), std::move(fences));
|
impl->RequestComposite(std::move(layers), std::move(fences));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> GPU::GetAppletCaptureBuffer() {
|
||||||
|
return impl->GetAppletCaptureBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
u64 GPU::GetTicks() const {
|
u64 GPU::GetTicks() const {
|
||||||
return impl->GetTicks();
|
return impl->GetTicks();
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,6 +215,8 @@ public:
|
||||||
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
|
||||||
std::vector<Service::Nvidia::NvFence>&& fences);
|
std::vector<Service::Nvidia::NvFence>&& fences);
|
||||||
|
|
||||||
|
std::vector<u8> GetAppletCaptureBuffer();
|
||||||
|
|
||||||
/// Performs any additional setup necessary in order to begin GPU emulation.
|
/// Performs any additional setup necessary in order to begin GPU emulation.
|
||||||
/// This can be used to launch any necessary threads and register any necessary
|
/// This can be used to launch any necessary threads and register any necessary
|
||||||
/// core timing events.
|
/// core timing events.
|
||||||
|
|
|
@ -37,6 +37,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
|
||||||
|
|
||||||
#define A_GPU 1
|
#define A_GPU 1
|
||||||
#define A_GLSL 1
|
#define A_GLSL 1
|
||||||
|
#define FSR_RCAS_PASSTHROUGH_ALPHA 1
|
||||||
|
|
||||||
#ifndef YUZU_USE_FP16
|
#ifndef YUZU_USE_FP16
|
||||||
#include "ffx_a.h"
|
#include "ffx_a.h"
|
||||||
|
@ -71,9 +72,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture;
|
||||||
|
|
||||||
#include "ffx_fsr1.h"
|
#include "ffx_fsr1.h"
|
||||||
|
|
||||||
#if USE_RCAS
|
layout (location = 0) in vec2 frag_texcoord;
|
||||||
layout(location = 0) in vec2 frag_texcoord;
|
|
||||||
#endif
|
|
||||||
layout (location = 0) out vec4 frag_color;
|
layout (location = 0) out vec4 frag_color;
|
||||||
|
|
||||||
void CurrFilter(AU2 pos) {
|
void CurrFilter(AU2 pos) {
|
||||||
|
@ -81,22 +80,22 @@ void CurrFilter(AU2 pos) {
|
||||||
#ifndef YUZU_USE_FP16
|
#ifndef YUZU_USE_FP16
|
||||||
AF3 c;
|
AF3 c;
|
||||||
FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
|
FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
|
||||||
frag_color = AF4(c, 1.0);
|
frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
|
||||||
#else
|
#else
|
||||||
AH3 c;
|
AH3 c;
|
||||||
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
|
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
|
||||||
frag_color = AH4(c, 1.0);
|
frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if USE_RCAS
|
#if USE_RCAS
|
||||||
#ifndef YUZU_USE_FP16
|
#ifndef YUZU_USE_FP16
|
||||||
AF3 c;
|
AF4 c;
|
||||||
FsrRcasF(c.r, c.g, c.b, pos, Const0);
|
FsrRcasF(c.r, c.g, c.b, c.a, pos, Const0);
|
||||||
frag_color = AF4(c, 1.0);
|
frag_color = c;
|
||||||
#else
|
#else
|
||||||
AH3 c;
|
AH4 c;
|
||||||
FsrRcasH(c.r, c.g, c.b, pos, Const0);
|
FsrRcasH(c.r, c.g, c.b, c.a, pos, Const0);
|
||||||
frag_color = AH4(c, 1.0);
|
frag_color = c;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,5 +71,5 @@ vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0);
|
frag_color = vec4(FxaaPixelShader(posPos, input_texture), texture(input_texture, posPos.xy).a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ layout (location = 0) uniform uvec4 constants[4];
|
||||||
|
|
||||||
#define A_GPU 1
|
#define A_GPU 1
|
||||||
#define A_GLSL 1
|
#define A_GLSL 1
|
||||||
|
#define FSR_RCAS_PASSTHROUGH_ALPHA 1
|
||||||
|
|
||||||
#ifdef YUZU_USE_FP16
|
#ifdef YUZU_USE_FP16
|
||||||
#define A_HALF
|
#define A_HALF
|
||||||
|
@ -67,9 +68,7 @@ layout (location = 0) uniform uvec4 constants[4];
|
||||||
|
|
||||||
#include "ffx_fsr1.h"
|
#include "ffx_fsr1.h"
|
||||||
|
|
||||||
#if USE_RCAS
|
layout (location = 0) in vec2 frag_texcoord;
|
||||||
layout(location = 0) in vec2 frag_texcoord;
|
|
||||||
#endif
|
|
||||||
layout (location = 0) out vec4 frag_color;
|
layout (location = 0) out vec4 frag_color;
|
||||||
|
|
||||||
void CurrFilter(AU2 pos)
|
void CurrFilter(AU2 pos)
|
||||||
|
@ -78,22 +77,22 @@ void CurrFilter(AU2 pos)
|
||||||
#ifndef YUZU_USE_FP16
|
#ifndef YUZU_USE_FP16
|
||||||
AF3 c;
|
AF3 c;
|
||||||
FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
|
FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
|
||||||
frag_color = AF4(c, 1.0);
|
frag_color = AF4(c, texture(InputTexture, frag_texcoord).a);
|
||||||
#else
|
#else
|
||||||
AH3 c;
|
AH3 c;
|
||||||
FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
|
FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
|
||||||
frag_color = AH4(c, 1.0);
|
frag_color = AH4(c, texture(InputTexture, frag_texcoord).a);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if USE_RCAS
|
#if USE_RCAS
|
||||||
#ifndef YUZU_USE_FP16
|
#ifndef YUZU_USE_FP16
|
||||||
AF3 c;
|
AF4 c;
|
||||||
FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
|
FsrRcasF(c.r, c.g, c.b, c.a, pos, constants[0]);
|
||||||
frag_color = AF4(c, 1.0);
|
frag_color = c;
|
||||||
#else
|
#else
|
||||||
AH3 c;
|
AH3 c;
|
||||||
FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
|
FsrRcasH(c.r, c.g, c.b, c.a, pos, constants[0]);
|
||||||
frag_color = AH4(c, 1.0);
|
frag_color = c;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,5 @@ layout (location = 0) out vec4 color;
|
||||||
layout (binding = 0) uniform sampler2D color_texture;
|
layout (binding = 0) uniform sampler2D color_texture;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
|
color = vec4(texture(color_texture, frag_tex_coord));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,5 +52,5 @@ vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f);
|
color = textureBicubic(color_texture, frag_tex_coord);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,14 +46,14 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0];
|
vec4 base = texture(color_texture, vec2(frag_tex_coord)) * weight[0];
|
||||||
vec2 tex_offset = 1.0f / textureSize(color_texture, 0);
|
vec2 tex_offset = 1.0f / textureSize(color_texture, 0);
|
||||||
|
|
||||||
// TODO(Blinkhawk): This code can be optimized through shader group instructions.
|
// TODO(Blinkhawk): This code can be optimized through shader group instructions.
|
||||||
vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb;
|
vec4 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset);
|
||||||
vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb;
|
vec4 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset);
|
||||||
vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb;
|
vec4 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset);
|
||||||
vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb;
|
vec4 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0));
|
||||||
vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f);
|
vec4 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f);
|
||||||
color = vec4(combination + base, 1.0f);
|
color = combination + base;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,6 @@
|
||||||
|
|
||||||
#define YUZU_USE_FP16
|
#define YUZU_USE_FP16
|
||||||
#define USE_EASU 1
|
#define USE_EASU 1
|
||||||
|
#define VERSION 1
|
||||||
|
|
||||||
#include "fidelityfx_fsr.frag"
|
#include "fidelityfx_fsr.frag"
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
#extension GL_GOOGLE_include_directive : enable
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
#define USE_EASU 1
|
#define USE_EASU 1
|
||||||
|
#define VERSION 1
|
||||||
|
|
||||||
#include "fidelityfx_fsr.frag"
|
#include "fidelityfx_fsr.frag"
|
||||||
|
|
|
@ -6,5 +6,6 @@
|
||||||
|
|
||||||
#define YUZU_USE_FP16
|
#define YUZU_USE_FP16
|
||||||
#define USE_RCAS 1
|
#define USE_RCAS 1
|
||||||
|
#define VERSION 1
|
||||||
|
|
||||||
#include "fidelityfx_fsr.frag"
|
#include "fidelityfx_fsr.frag"
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
#extension GL_GOOGLE_include_directive : enable
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
#define USE_RCAS 1
|
#define USE_RCAS 1
|
||||||
|
#define VERSION 1
|
||||||
|
|
||||||
#include "fidelityfx_fsr.frag"
|
#include "fidelityfx_fsr.frag"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#extension GL_GOOGLE_include_directive : enable
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
#define VERSION 1
|
#define VERSION 2
|
||||||
#define YUZU_USE_FP16
|
#define YUZU_USE_FP16
|
||||||
|
|
||||||
#include "opengl_present_scaleforce.frag"
|
#include "opengl_present_scaleforce.frag"
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
|
|
||||||
#extension GL_GOOGLE_include_directive : enable
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
#define VERSION 1
|
#define VERSION 2
|
||||||
|
|
||||||
#include "opengl_present_scaleforce.frag"
|
#include "opengl_present_scaleforce.frag"
|
||||||
|
|
37
src/video_core/present.h
Normal file
37
src/video_core/present.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
static inline Settings::ScalingFilter GetScalingFilter() {
|
||||||
|
return Settings::values.scaling_filter.GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Settings::AntiAliasing GetAntiAliasing() {
|
||||||
|
return Settings::values.anti_aliasing.GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() {
|
||||||
|
return Settings::ScalingFilter::Bilinear;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() {
|
||||||
|
return Settings::AntiAliasing::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PresentFilters {
|
||||||
|
Settings::ScalingFilter (*get_scaling_filter)();
|
||||||
|
Settings::AntiAliasing (*get_anti_aliasing)();
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr PresentFilters PresentFiltersForDisplay{
|
||||||
|
.get_scaling_filter = &GetScalingFilter,
|
||||||
|
.get_anti_aliasing = &GetAntiAliasing,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr PresentFilters PresentFiltersForAppletCapture{
|
||||||
|
.get_scaling_filter = &GetScalingFilterForAppletCapture,
|
||||||
|
.get_anti_aliasing = &GetAntiAliasingForAppletCapture,
|
||||||
|
};
|
|
@ -40,6 +40,9 @@ public:
|
||||||
/// Finalize rendering the guest frame and draw into the presentation texture
|
/// Finalize rendering the guest frame and draw into the presentation texture
|
||||||
virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
|
virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
|
||||||
|
|
||||||
|
/// Get the tiled applet layer capture buffer
|
||||||
|
virtual std::vector<u8> GetAppletCaptureBuffer() = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
|
[[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
|
[[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/frontend/graphics_context.h"
|
#include "core/frontend/graphics_context.h"
|
||||||
|
#include "video_core/capture.h"
|
||||||
#include "video_core/renderer_null/renderer_null.h"
|
#include "video_core/renderer_null/renderer_null.h"
|
||||||
|
|
||||||
namespace Null {
|
namespace Null {
|
||||||
|
@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> RendererNull::GetAppletCaptureBuffer() {
|
||||||
|
return std::vector<u8>(VideoCore::Capture::TiledSize);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Null
|
} // namespace Null
|
||||||
|
|
|
@ -19,6 +19,8 @@ public:
|
||||||
|
|
||||||
void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
|
void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
|
||||||
|
|
||||||
|
std::vector<u8> GetAppletCaptureBuffer() override;
|
||||||
|
|
||||||
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
||||||
return &m_rasterizer;
|
return &m_rasterizer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
||||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||||
#include "video_core/renderer_opengl/present/filters.h"
|
#include "video_core/renderer_opengl/present/filters.h"
|
||||||
|
@ -13,14 +14,14 @@ namespace OpenGL {
|
||||||
BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
|
BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||||
StateTracker& state_tracker_, ProgramManager& program_manager_,
|
StateTracker& state_tracker_, ProgramManager& program_manager_,
|
||||||
Device& device_)
|
Device& device_, const PresentFilters& filters_)
|
||||||
: rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
|
: rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
|
||||||
program_manager(program_manager_), device(device_) {}
|
program_manager(program_manager_), device(device_), filters(filters_) {}
|
||||||
|
|
||||||
BlitScreen::~BlitScreen() = default;
|
BlitScreen::~BlitScreen() = default;
|
||||||
|
|
||||||
void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
|
void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout) {
|
const Layout::FramebufferLayout& layout, bool invert_y) {
|
||||||
// TODO: Signal state tracker about these changes
|
// TODO: Signal state tracker about these changes
|
||||||
state_tracker.NotifyScreenDrawVertexArray();
|
state_tracker.NotifyScreenDrawVertexArray();
|
||||||
state_tracker.NotifyPolygonModes();
|
state_tracker.NotifyPolygonModes();
|
||||||
|
@ -56,22 +57,22 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe
|
||||||
glDepthRangeIndexed(0, 0.0, 0.0);
|
glDepthRangeIndexed(0, 0.0, 0.0);
|
||||||
|
|
||||||
while (layers.size() < framebuffers.size()) {
|
while (layers.size() < framebuffers.size()) {
|
||||||
layers.emplace_back(rasterizer, device_memory);
|
layers.emplace_back(rasterizer, device_memory, filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateWindowAdapt();
|
CreateWindowAdapt();
|
||||||
window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout);
|
window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// program_manager.RestoreGuestPipeline();
|
// program_manager.RestoreGuestPipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitScreen::CreateWindowAdapt() {
|
void BlitScreen::CreateWindowAdapt() {
|
||||||
if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) {
|
if (window_adapt && filters.get_scaling_filter() == current_window_adapt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_window_adapt = Settings::values.scaling_filter.GetValue();
|
current_window_adapt = filters.get_scaling_filter();
|
||||||
switch (current_window_adapt) {
|
switch (current_window_adapt) {
|
||||||
case Settings::ScalingFilter::NearestNeighbor:
|
case Settings::ScalingFilter::NearestNeighbor:
|
||||||
window_adapt = MakeNearestNeighbor(device);
|
window_adapt = MakeNearestNeighbor(device);
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace Layout {
|
||||||
struct FramebufferLayout;
|
struct FramebufferLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PresentFilters;
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
struct FramebufferConfig;
|
struct FramebufferConfig;
|
||||||
}
|
}
|
||||||
|
@ -46,12 +48,12 @@ public:
|
||||||
explicit BlitScreen(RasterizerOpenGL& rasterizer,
|
explicit BlitScreen(RasterizerOpenGL& rasterizer,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory,
|
Tegra::MaxwellDeviceMemoryManager& device_memory,
|
||||||
StateTracker& state_tracker, ProgramManager& program_manager,
|
StateTracker& state_tracker, ProgramManager& program_manager,
|
||||||
Device& device);
|
Device& device, const PresentFilters& filters);
|
||||||
~BlitScreen();
|
~BlitScreen();
|
||||||
|
|
||||||
/// Draws the emulated screens to the emulator window.
|
/// Draws the emulated screens to the emulator window.
|
||||||
void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
|
void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout);
|
const Layout::FramebufferLayout& layout, bool invert_y);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateWindowAdapt();
|
void CreateWindowAdapt();
|
||||||
|
@ -61,6 +63,7 @@ private:
|
||||||
StateTracker& state_tracker;
|
StateTracker& state_tracker;
|
||||||
ProgramManager& program_manager;
|
ProgramManager& program_manager;
|
||||||
Device& device;
|
Device& device;
|
||||||
|
const PresentFilters& filters;
|
||||||
|
|
||||||
Settings::ScalingFilter current_window_adapt{};
|
Settings::ScalingFilter current_window_adapt{};
|
||||||
std::unique_ptr<WindowAdaptPass> window_adapt;
|
std::unique_ptr<WindowAdaptPass> window_adapt;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "video_core/framebuffer_config.h"
|
#include "video_core/framebuffer_config.h"
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/present/fsr.h"
|
#include "video_core/renderer_opengl/present/fsr.h"
|
||||||
|
@ -14,8 +15,9 @@
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_)
|
Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||||
: rasterizer(rasterizer_), device_memory(device_memory_) {
|
const PresentFilters& filters_)
|
||||||
|
: rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) {
|
||||||
// Allocate textures for the screen
|
// Allocate textures for the screen
|
||||||
framebuffer_texture.resource.Create(GL_TEXTURE_2D);
|
framebuffer_texture.resource.Create(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
@ -34,12 +36,12 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
||||||
std::array<ScreenRectVertex, 4>& out_vertices,
|
std::array<ScreenRectVertex, 4>& out_vertices,
|
||||||
ProgramManager& program_manager,
|
ProgramManager& program_manager,
|
||||||
const Tegra::FramebufferConfig& framebuffer,
|
const Tegra::FramebufferConfig& framebuffer,
|
||||||
const Layout::FramebufferLayout& layout) {
|
const Layout::FramebufferLayout& layout, bool invert_y) {
|
||||||
FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
|
FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
|
||||||
auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
|
auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
|
||||||
GLuint texture = info.display_texture;
|
GLuint texture = info.display_texture;
|
||||||
|
|
||||||
auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
|
auto anti_aliasing = filters.get_anti_aliasing();
|
||||||
if (anti_aliasing != Settings::AntiAliasing::None) {
|
if (anti_aliasing != Settings::AntiAliasing::None) {
|
||||||
glEnablei(GL_SCISSOR_TEST, 0);
|
glEnablei(GL_SCISSOR_TEST, 0);
|
||||||
auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
|
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,
|
||||||
|
|
||||||
glDisablei(GL_SCISSOR_TEST, 0);
|
glDisablei(GL_SCISSOR_TEST, 0);
|
||||||
|
|
||||||
if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
|
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||||
if (!fsr || fsr->NeedsRecreation(layout.screen)) {
|
if (!fsr || fsr->NeedsRecreation(layout.screen)) {
|
||||||
fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
|
fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
|
||||||
}
|
}
|
||||||
|
@ -83,10 +85,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
||||||
const auto w = screen.GetWidth();
|
const auto w = screen.GetWidth();
|
||||||
const auto h = screen.GetHeight();
|
const auto h = screen.GetHeight();
|
||||||
|
|
||||||
out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
|
const auto left = crop.left;
|
||||||
out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
|
const auto right = crop.right;
|
||||||
out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
|
const auto top = invert_y ? crop.bottom : crop.top;
|
||||||
out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
|
const auto bottom = invert_y ? crop.top : crop.bottom;
|
||||||
|
|
||||||
|
out_vertices[0] = ScreenRectVertex(x, y, left, top);
|
||||||
|
out_vertices[1] = ScreenRectVertex(x + w, y, right, top);
|
||||||
|
out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
|
||||||
|
out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
@ -131,10 +138,12 @@ FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig&
|
||||||
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
|
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
|
||||||
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
||||||
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
|
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
|
||||||
const std::span<const u8> input_data(host_ptr, size_in_bytes);
|
if (host_ptr) {
|
||||||
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
|
const std::span<const u8> input_data(host_ptr, size_in_bytes);
|
||||||
framebuffer.width, framebuffer.height, 1, block_height_log2,
|
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
|
||||||
0);
|
framebuffer.width, framebuffer.height, 1,
|
||||||
|
block_height_log2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace Layout {
|
||||||
struct FramebufferLayout;
|
struct FramebufferLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PresentFilters;
|
||||||
|
|
||||||
namespace Service::android {
|
namespace Service::android {
|
||||||
enum class PixelFormat : u32;
|
enum class PixelFormat : u32;
|
||||||
};
|
};
|
||||||
|
@ -44,14 +46,15 @@ struct ScreenRectVertex;
|
||||||
|
|
||||||
class Layer {
|
class Layer {
|
||||||
public:
|
public:
|
||||||
explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory);
|
explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory,
|
||||||
|
const PresentFilters& filters);
|
||||||
~Layer();
|
~Layer();
|
||||||
|
|
||||||
GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
||||||
std::array<ScreenRectVertex, 4>& out_vertices,
|
std::array<ScreenRectVertex, 4>& out_vertices,
|
||||||
ProgramManager& program_manager,
|
ProgramManager& program_manager,
|
||||||
const Tegra::FramebufferConfig& framebuffer,
|
const Tegra::FramebufferConfig& framebuffer,
|
||||||
const Layout::FramebufferLayout& layout);
|
const Layout::FramebufferLayout& layout, bool invert_y);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Loads framebuffer from emulated memory into the active OpenGL texture.
|
/// Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||||
|
@ -65,6 +68,7 @@ private:
|
||||||
private:
|
private:
|
||||||
RasterizerOpenGL& rasterizer;
|
RasterizerOpenGL& rasterizer;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
|
const PresentFilters& filters;
|
||||||
|
|
||||||
/// OpenGL framebuffer data
|
/// OpenGL framebuffer data
|
||||||
std::vector<u8> gl_framebuffer_data;
|
std::vector<u8> gl_framebuffer_data;
|
||||||
|
|
|
@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default;
|
||||||
|
|
||||||
void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
|
void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
|
||||||
std::span<const Tegra::FramebufferConfig> framebuffers,
|
std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout) {
|
const Layout::FramebufferLayout& layout, bool invert_y) {
|
||||||
GLint old_read_fb;
|
GLint old_read_fb;
|
||||||
GLint old_draw_fb;
|
GLint old_draw_fb;
|
||||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||||
|
@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
|
||||||
auto layer_it = layers.begin();
|
auto layer_it = layers.begin();
|
||||||
for (size_t i = 0; i < layer_count; i++) {
|
for (size_t i = 0; i < layer_count; i++) {
|
||||||
textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
|
textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
|
||||||
framebuffers[i], layout);
|
framebuffers[i], layout, invert_y);
|
||||||
layer_it++;
|
layer_it++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +92,21 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
for (size_t i = 0; i < layer_count; i++) {
|
for (size_t i = 0; i < layer_count; i++) {
|
||||||
|
switch (framebuffers[i].blending) {
|
||||||
|
case Tegra::BlendMode::Opaque:
|
||||||
|
default:
|
||||||
|
glDisablei(GL_BLEND, 0);
|
||||||
|
break;
|
||||||
|
case Tegra::BlendMode::Premultiplied:
|
||||||
|
glEnablei(GL_BLEND, 0);
|
||||||
|
glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||||
|
break;
|
||||||
|
case Tegra::BlendMode::Coverage:
|
||||||
|
glEnablei(GL_BLEND, 0);
|
||||||
|
glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
glBindTextureUnit(0, textures[i]);
|
glBindTextureUnit(0, textures[i]);
|
||||||
glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
|
glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
|
||||||
matrices[i].data());
|
matrices[i].data());
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
|
|
||||||
void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
|
void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
|
||||||
std::span<const Tegra::FramebufferConfig> framebuffers,
|
std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout);
|
const Layout::FramebufferLayout& layout, bool invert_y);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Device& device;
|
const Device& device;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
|
#include "video_core/capture.h"
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||||
|
@ -120,7 +122,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
|
||||||
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
|
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
|
||||||
}
|
}
|
||||||
blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
|
blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
|
||||||
program_manager, device);
|
program_manager, device, PresentFiltersForDisplay);
|
||||||
|
blit_applet =
|
||||||
|
std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager,
|
||||||
|
device, PresentFiltersForAppletCapture);
|
||||||
|
capture_framebuffer.Create();
|
||||||
|
capture_renderbuffer.Create();
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth,
|
||||||
|
VideoCore::Capture::LinearHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererOpenGL::~RendererOpenGL() = default;
|
RendererOpenGL::~RendererOpenGL() = default;
|
||||||
|
@ -130,10 +140,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderAppletCaptureLayer(framebuffers);
|
||||||
RenderScreenshot(framebuffers);
|
RenderScreenshot(framebuffers);
|
||||||
|
|
||||||
state_tracker.BindFramebuffer(0);
|
state_tracker.BindFramebuffer(0);
|
||||||
blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout());
|
blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);
|
||||||
|
|
||||||
++m_current_frame;
|
++m_current_frame;
|
||||||
|
|
||||||
|
@ -159,11 +170,8 @@ void RendererOpenGL::AddTelemetryFields() {
|
||||||
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
|
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
if (!renderer_settings.screenshot_requested) {
|
const Layout::FramebufferLayout& layout, void* dst) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLint old_read_fb;
|
GLint old_read_fb;
|
||||||
GLint old_draw_fb;
|
GLint old_draw_fb;
|
||||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||||
|
@ -173,29 +181,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
|
||||||
screenshot_framebuffer.Create();
|
screenshot_framebuffer.Create();
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
|
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
|
||||||
|
|
||||||
const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
|
||||||
|
|
||||||
GLuint renderbuffer;
|
GLuint renderbuffer;
|
||||||
glGenRenderbuffers(1, &renderbuffer);
|
glGenRenderbuffers(1, &renderbuffer);
|
||||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
|
||||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
||||||
|
|
||||||
blit_screen->DrawScreen(framebuffers, layout);
|
blit_screen->DrawScreen(framebuffers, layout, false);
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||||
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst);
|
||||||
renderer_settings.screenshot_bits);
|
|
||||||
|
|
||||||
screenshot_framebuffer.Release();
|
screenshot_framebuffer.Release();
|
||||||
glDeleteRenderbuffers(1, &renderbuffer);
|
glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
if (!renderer_settings.screenshot_requested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
|
||||||
|
renderer_settings.screenshot_bits);
|
||||||
|
|
||||||
renderer_settings.screenshot_complete_callback(true);
|
renderer_settings.screenshot_complete_callback(true);
|
||||||
renderer_settings.screenshot_requested = false;
|
renderer_settings.screenshot_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::RenderAppletCaptureLayer(
|
||||||
|
std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
GLint old_read_fb;
|
||||||
|
GLint old_draw_fb;
|
||||||
|
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||||
|
capture_renderbuffer.handle);
|
||||||
|
|
||||||
|
blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() {
|
||||||
|
using namespace VideoCore::Capture;
|
||||||
|
|
||||||
|
std::vector<u8> linear(TiledSize);
|
||||||
|
std::vector<u8> out(TiledSize);
|
||||||
|
|
||||||
|
GLint old_read_fb;
|
||||||
|
GLint old_draw_fb;
|
||||||
|
GLint old_pixel_pack_buffer;
|
||||||
|
GLint old_pack_row_length;
|
||||||
|
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
|
||||||
|
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer);
|
||||||
|
glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||||
|
capture_renderbuffer.handle);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||||
|
glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||||
|
linear.data());
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer);
|
||||||
|
glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length);
|
||||||
|
|
||||||
|
Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight,
|
||||||
|
LinearDepth, BlockHeight, BlockDepth);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
|
|
@ -42,6 +42,8 @@ public:
|
||||||
|
|
||||||
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
|
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
|
||||||
|
|
||||||
|
std::vector<u8> GetAppletCaptureBuffer() override;
|
||||||
|
|
||||||
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
||||||
return &rasterizer;
|
return &rasterizer;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,11 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddTelemetryFields();
|
void AddTelemetryFields();
|
||||||
|
|
||||||
|
void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
|
const Layout::FramebufferLayout& layout, void* dst);
|
||||||
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
|
||||||
Core::TelemetrySession& telemetry_session;
|
Core::TelemetrySession& telemetry_session;
|
||||||
Core::Frontend::EmuWindow& emu_window;
|
Core::Frontend::EmuWindow& emu_window;
|
||||||
|
@ -64,8 +70,11 @@ private:
|
||||||
ProgramManager program_manager;
|
ProgramManager program_manager;
|
||||||
RasterizerOpenGL rasterizer;
|
RasterizerOpenGL rasterizer;
|
||||||
OGLFramebuffer screenshot_framebuffer;
|
OGLFramebuffer screenshot_framebuffer;
|
||||||
|
OGLFramebuffer capture_framebuffer;
|
||||||
|
OGLRenderbuffer capture_renderbuffer;
|
||||||
|
|
||||||
std::unique_ptr<BlitScreen> blit_screen;
|
std::unique_ptr<BlitScreen> blit_screen;
|
||||||
|
std::unique_ptr<BlitScreen> blit_applet;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
@ -48,12 +49,12 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
|
||||||
|
|
||||||
Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
|
Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
|
||||||
VkExtent2D output_size, VkDescriptorSetLayout layout)
|
VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_)
|
||||||
: device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
|
: device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
|
||||||
device_memory(device_memory_), image_count(image_count_) {
|
device_memory(device_memory_), filters(filters_), image_count(image_count_) {
|
||||||
CreateDescriptorPool();
|
CreateDescriptorPool();
|
||||||
CreateDescriptorSets(layout);
|
CreateDescriptorSets(layout);
|
||||||
if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
|
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||||
CreateFSR(output_size);
|
CreateFSR(output_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,11 +172,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Layer::SetAntiAliasPass() {
|
void Layer::SetAntiAliasPass() {
|
||||||
if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) {
|
if (anti_alias && anti_alias_setting == filters.get_anti_aliasing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
anti_alias_setting = Settings::values.anti_aliasing.GetValue();
|
anti_alias_setting = filters.get_anti_aliasing();
|
||||||
|
|
||||||
const VkExtent2D render_area{
|
const VkExtent2D render_area{
|
||||||
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
||||||
|
@ -270,9 +271,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
|
||||||
const u64 linear_size{GetSizeInBytes(framebuffer)};
|
const u64 linear_size{GetSizeInBytes(framebuffer)};
|
||||||
const u64 tiled_size{Tegra::Texture::CalculateSize(
|
const u64 tiled_size{Tegra::Texture::CalculateSize(
|
||||||
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
||||||
Tegra::Texture::UnswizzleTexture(
|
if (host_ptr) {
|
||||||
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
Tegra::Texture::UnswizzleTexture(
|
||||||
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
||||||
|
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const VkBufferImageCopy copy{
|
const VkBufferImageCopy copy{
|
||||||
.bufferOffset = image_offset,
|
.bufferOffset = image_offset,
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace Layout {
|
||||||
struct FramebufferLayout;
|
struct FramebufferLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PresentFilters;
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
struct FramebufferConfig;
|
struct FramebufferConfig;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,8 @@ class Layer final {
|
||||||
public:
|
public:
|
||||||
explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
|
explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
|
Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
|
||||||
VkExtent2D output_size, VkDescriptorSetLayout layout);
|
VkExtent2D output_size, VkDescriptorSetLayout layout,
|
||||||
|
const PresentFilters& filters);
|
||||||
~Layer();
|
~Layer();
|
||||||
|
|
||||||
void ConfigureDraw(PresentPushConstants* out_push_constants,
|
void ConfigureDraw(PresentPushConstants* out_push_constants,
|
||||||
|
@ -71,6 +74,7 @@ private:
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
|
const PresentFilters& filters;
|
||||||
const size_t image_count{};
|
const size_t image_count{};
|
||||||
vk::DescriptorPool descriptor_pool{};
|
vk::DescriptorPool descriptor_pool{};
|
||||||
vk::DescriptorSets descriptor_sets{};
|
vk::DescriptorSets descriptor_sets{};
|
||||||
|
|
|
@ -362,10 +362,10 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
static vk::Pipeline CreateWrappedPipelineImpl(
|
||||||
vk::PipelineLayout& layout,
|
const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
|
||||||
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
|
||||||
bool enable_blending) {
|
VkPipelineColorBlendAttachmentState blending) {
|
||||||
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
|
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
|
||||||
{
|
{
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
@ -443,30 +443,6 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
|
||||||
.alphaToOneEnable = VK_FALSE,
|
.alphaToOneEnable = VK_FALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
|
|
||||||
.blendEnable = VK_FALSE,
|
|
||||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
||||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
||||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
|
||||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
||||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
||||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
|
||||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
|
||||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{
|
|
||||||
.blendEnable = VK_TRUE,
|
|
||||||
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
|
||||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
||||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
|
||||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
|
||||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
|
||||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
|
||||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
|
||||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
|
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
@ -474,8 +450,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
|
||||||
.logicOpEnable = VK_FALSE,
|
.logicOpEnable = VK_FALSE,
|
||||||
.logicOp = VK_LOGIC_OP_COPY,
|
.logicOp = VK_LOGIC_OP_COPY,
|
||||||
.attachmentCount = 1,
|
.attachmentCount = 1,
|
||||||
.pAttachments =
|
.pAttachments = &blending,
|
||||||
enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled,
|
|
||||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -515,6 +490,63 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
||||||
|
vk::PipelineLayout& layout,
|
||||||
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
|
||||||
|
constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
|
||||||
|
.blendEnable = VK_FALSE,
|
||||||
|
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||||
|
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
|
||||||
|
color_blend_attachment_disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
|
||||||
|
const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
|
||||||
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
|
||||||
|
constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{
|
||||||
|
.blendEnable = VK_TRUE,
|
||||||
|
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||||
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||||
|
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||||
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||||
|
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
|
||||||
|
color_blend_attachment_premultiplied);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Pipeline CreateWrappedCoverageBlendingPipeline(
|
||||||
|
const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
|
||||||
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) {
|
||||||
|
constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{
|
||||||
|
.blendEnable = VK_TRUE,
|
||||||
|
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
||||||
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||||
|
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||||
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||||
|
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CreateWrappedPipelineImpl(device, renderpass, layout, shaders,
|
||||||
|
color_blend_attachment_coverage);
|
||||||
|
}
|
||||||
|
|
||||||
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
|
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
|
||||||
VkSampler sampler, VkImageView view,
|
VkSampler sampler, VkImageView view,
|
||||||
VkDescriptorSet set, u32 binding) {
|
VkDescriptorSet set, u32 binding) {
|
||||||
|
|
|
@ -42,8 +42,13 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
|
||||||
vk::DescriptorSetLayout& layout);
|
vk::DescriptorSetLayout& layout);
|
||||||
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
||||||
vk::PipelineLayout& layout,
|
vk::PipelineLayout& layout,
|
||||||
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
|
||||||
bool enable_blending = false);
|
vk::Pipeline CreateWrappedPremultipliedBlendingPipeline(
|
||||||
|
const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
|
||||||
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
|
||||||
|
vk::Pipeline CreateWrappedCoverageBlendingPipeline(
|
||||||
|
const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout,
|
||||||
|
std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders);
|
||||||
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
|
VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
|
||||||
VkSampler sampler, VkImageView view,
|
VkSampler sampler, VkImageView view,
|
||||||
VkDescriptorSet set, u32 binding);
|
VkDescriptorSet set, u32 binding);
|
||||||
|
|
|
@ -22,7 +22,7 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format,
|
||||||
CreatePipelineLayout();
|
CreatePipelineLayout();
|
||||||
CreateVertexShader();
|
CreateVertexShader();
|
||||||
CreateRenderPass(frame_format);
|
CreateRenderPass(frame_format);
|
||||||
CreatePipeline();
|
CreatePipelines();
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowAdaptPass::~WindowAdaptPass() = default;
|
WindowAdaptPass::~WindowAdaptPass() = default;
|
||||||
|
@ -34,7 +34,6 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||||
|
|
||||||
const VkFramebuffer host_framebuffer{*dst->framebuffer};
|
const VkFramebuffer host_framebuffer{*dst->framebuffer};
|
||||||
const VkRenderPass renderpass{*render_pass};
|
const VkRenderPass renderpass{*render_pass};
|
||||||
const VkPipeline graphics_pipeline{*pipeline};
|
|
||||||
const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
|
const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
|
||||||
const VkExtent2D render_area{
|
const VkExtent2D render_area{
|
||||||
.width = dst->width,
|
.width = dst->width,
|
||||||
|
@ -44,9 +43,23 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||||
const size_t layer_count = configs.size();
|
const size_t layer_count = configs.size();
|
||||||
std::vector<PresentPushConstants> push_constants(layer_count);
|
std::vector<PresentPushConstants> push_constants(layer_count);
|
||||||
std::vector<VkDescriptorSet> descriptor_sets(layer_count);
|
std::vector<VkDescriptorSet> descriptor_sets(layer_count);
|
||||||
|
std::vector<VkPipeline> graphics_pipelines(layer_count);
|
||||||
|
|
||||||
auto layer_it = layers.begin();
|
auto layer_it = layers.begin();
|
||||||
for (size_t i = 0; i < layer_count; i++) {
|
for (size_t i = 0; i < layer_count; i++) {
|
||||||
|
switch (configs[i].blending) {
|
||||||
|
case Tegra::BlendMode::Opaque:
|
||||||
|
default:
|
||||||
|
graphics_pipelines[i] = *opaque_pipeline;
|
||||||
|
break;
|
||||||
|
case Tegra::BlendMode::Premultiplied:
|
||||||
|
graphics_pipelines[i] = *premultiplied_pipeline;
|
||||||
|
break;
|
||||||
|
case Tegra::BlendMode::Coverage:
|
||||||
|
graphics_pipelines[i] = *coverage_pipeline;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
|
layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
|
||||||
image_index, configs[i], layout);
|
image_index, configs[i], layout);
|
||||||
layer_it++;
|
layer_it++;
|
||||||
|
@ -77,8 +90,8 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||||
BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
|
BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
|
||||||
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
|
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
|
||||||
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
|
||||||
for (size_t i = 0; i < layer_count; i++) {
|
for (size_t i = 0; i < layer_count; i++) {
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]);
|
||||||
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
|
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
push_constants[i]);
|
push_constants[i]);
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
|
||||||
|
@ -129,9 +142,13 @@ void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
|
||||||
render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
|
render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowAdaptPass::CreatePipeline() {
|
void WindowAdaptPass::CreatePipelines() {
|
||||||
pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
|
opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
|
||||||
std::tie(vertex_shader, fragment_shader), false);
|
std::tie(vertex_shader, fragment_shader));
|
||||||
|
premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline(
|
||||||
|
device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
|
||||||
|
coverage_pipeline = CreateWrappedCoverageBlendingPipeline(
|
||||||
|
device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -42,7 +42,7 @@ private:
|
||||||
void CreatePipelineLayout();
|
void CreatePipelineLayout();
|
||||||
void CreateVertexShader();
|
void CreateVertexShader();
|
||||||
void CreateRenderPass(VkFormat frame_format);
|
void CreateRenderPass(VkFormat frame_format);
|
||||||
void CreatePipeline();
|
void CreatePipelines();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Device& device;
|
const Device& device;
|
||||||
|
@ -52,7 +52,9 @@ private:
|
||||||
vk::ShaderModule vertex_shader;
|
vk::ShaderModule vertex_shader;
|
||||||
vk::ShaderModule fragment_shader;
|
vk::ShaderModule fragment_shader;
|
||||||
vk::RenderPass render_pass;
|
vk::RenderPass render_pass;
|
||||||
vk::Pipeline pipeline;
|
vk::Pipeline opaque_pipeline;
|
||||||
|
vk::Pipeline premultiplied_pipeline;
|
||||||
|
vk::Pipeline coverage_pipeline;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/frontend/graphics_context.h"
|
#include "core/frontend/graphics_context.h"
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
|
#include "video_core/capture.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||||
|
@ -38,6 +40,20 @@
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr VkExtent2D CaptureImageSize{
|
||||||
|
.width = VideoCore::Capture::LinearWidth,
|
||||||
|
.height = VideoCore::Capture::LinearHeight,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr VkExtent3D CaptureImageExtent{
|
||||||
|
.width = VideoCore::Capture::LinearWidth,
|
||||||
|
.height = VideoCore::Capture::LinearHeight,
|
||||||
|
.depth = VideoCore::Capture::LinearDepth,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
|
|
||||||
std::string GetReadableVersion(u32 version) {
|
std::string GetReadableVersion(u32 version) {
|
||||||
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
|
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
|
||||||
VK_VERSION_PATCH(version));
|
VK_VERSION_PATCH(version));
|
||||||
|
@ -99,10 +115,15 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||||
render_window.GetFramebufferLayout().height),
|
render_window.GetFramebufferLayout().height),
|
||||||
present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
|
present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
|
||||||
surface),
|
surface),
|
||||||
blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler),
|
blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler,
|
||||||
blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler),
|
PresentFiltersForDisplay),
|
||||||
|
blit_capture(device_memory, device, memory_allocator, present_manager, scheduler,
|
||||||
|
PresentFiltersForDisplay),
|
||||||
|
blit_applet(device_memory, device, memory_allocator, present_manager, scheduler,
|
||||||
|
PresentFiltersForAppletCapture),
|
||||||
rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
|
rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
|
||||||
scheduler) {
|
scheduler),
|
||||||
|
applet_frame() {
|
||||||
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
|
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
|
||||||
turbo_mode.emplace(instance, dld);
|
turbo_mode.emplace(instance, dld);
|
||||||
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
||||||
|
@ -125,6 +146,8 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||||
|
|
||||||
SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
|
SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
|
||||||
|
|
||||||
|
RenderAppletCaptureLayer(framebuffers);
|
||||||
|
|
||||||
if (!render_window.IsShown()) {
|
if (!render_window.IsShown()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -167,30 +190,20 @@ void RendererVulkan::Report() const {
|
||||||
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vulkan::RendererVulkan::RenderScreenshot(
|
vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
std::span<const Tegra::FramebufferConfig> framebuffers) {
|
const Layout::FramebufferLayout& layout, VkFormat format,
|
||||||
if (!renderer_settings.screenshot_requested) {
|
VkDeviceSize buffer_size) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM};
|
|
||||||
const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
|
||||||
|
|
||||||
auto frame = [&]() {
|
auto frame = [&]() {
|
||||||
Frame f{};
|
Frame f{};
|
||||||
f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height},
|
f.image =
|
||||||
ScreenshotFormat);
|
CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format);
|
||||||
f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat);
|
f.image_view = CreateWrappedImageView(device, f.image, format);
|
||||||
f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat);
|
f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format);
|
||||||
return f;
|
return f;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1,
|
auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download);
|
||||||
VK_FORMAT_B8G8R8A8_UNORM);
|
blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format);
|
||||||
|
|
||||||
const auto dst_buffer = CreateWrappedBuffer(
|
|
||||||
memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4),
|
|
||||||
MemoryUsage::Download);
|
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
|
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
|
||||||
|
@ -198,15 +211,68 @@ void Vulkan::RendererVulkan::RenderScreenshot(
|
||||||
VkExtent3D{layout.width, layout.height, 1});
|
VkExtent3D{layout.width, layout.height, 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure the copy is fully completed before saving the screenshot
|
// Ensure the copy is fully completed before saving the capture
|
||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
|
|
||||||
// Copy backing image data to the QImage screenshot buffer
|
// Copy backing image data to the capture buffer
|
||||||
dst_buffer.Invalidate();
|
dst_buffer.Invalidate();
|
||||||
|
return dst_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
if (!renderer_settings.screenshot_requested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& layout{renderer_settings.screenshot_framebuffer_layout};
|
||||||
|
const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
layout.width * layout.height * 4);
|
||||||
|
|
||||||
std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
|
std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
|
||||||
dst_buffer.Mapped().size());
|
dst_buffer.Mapped().size());
|
||||||
renderer_settings.screenshot_complete_callback(false);
|
renderer_settings.screenshot_complete_callback(false);
|
||||||
renderer_settings.screenshot_requested = false;
|
renderer_settings.screenshot_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
|
||||||
|
using namespace VideoCore::Capture;
|
||||||
|
|
||||||
|
std::vector<u8> out(VideoCore::Capture::TiledSize);
|
||||||
|
|
||||||
|
if (!applet_frame.image) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dst_buffer =
|
||||||
|
CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download);
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
|
||||||
|
DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure the copy is fully completed before writing the capture
|
||||||
|
scheduler.Finish();
|
||||||
|
|
||||||
|
// Swizzle image data to the capture buffer
|
||||||
|
dst_buffer.Invalidate();
|
||||||
|
Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth,
|
||||||
|
LinearHeight, LinearDepth, BlockHeight, BlockDepth);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererVulkan::RenderAppletCaptureLayer(
|
||||||
|
std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
if (!applet_frame.image) {
|
||||||
|
applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
|
||||||
|
applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
|
||||||
|
applet_frame.framebuffer = blit_applet.CreateFramebuffer(
|
||||||
|
VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
|
||||||
|
CaptureFormat);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
|
|
||||||
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
|
void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
|
||||||
|
|
||||||
|
std::vector<u8> GetAppletCaptureBuffer() override;
|
||||||
|
|
||||||
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
VideoCore::RasterizerInterface* ReadRasterizer() override {
|
||||||
return &rasterizer;
|
return &rasterizer;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +61,11 @@ public:
|
||||||
private:
|
private:
|
||||||
void Report() const;
|
void Report() const;
|
||||||
|
|
||||||
|
vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
|
const Layout::FramebufferLayout& layout, VkFormat format,
|
||||||
|
VkDeviceSize buffer_size);
|
||||||
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
|
||||||
Core::TelemetrySession& telemetry_session;
|
Core::TelemetrySession& telemetry_session;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
|
@ -79,9 +85,12 @@ private:
|
||||||
Swapchain swapchain;
|
Swapchain swapchain;
|
||||||
PresentManager present_manager;
|
PresentManager present_manager;
|
||||||
BlitScreen blit_swapchain;
|
BlitScreen blit_swapchain;
|
||||||
BlitScreen blit_screenshot;
|
BlitScreen blit_capture;
|
||||||
|
BlitScreen blit_applet;
|
||||||
RasterizerVulkan rasterizer;
|
RasterizerVulkan rasterizer;
|
||||||
std::optional<TurboMode> turbo_mode;
|
std::optional<TurboMode> turbo_mode;
|
||||||
|
|
||||||
|
Frame applet_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "video_core/framebuffer_config.h"
|
#include "video_core/framebuffer_config.h"
|
||||||
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_vulkan/present/filters.h"
|
#include "video_core/renderer_vulkan/present/filters.h"
|
||||||
#include "video_core/renderer_vulkan/present/layer.h"
|
#include "video_core/renderer_vulkan/present/layer.h"
|
||||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||||
|
@ -12,9 +13,9 @@ namespace Vulkan {
|
||||||
|
|
||||||
BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
|
BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
|
||||||
MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
|
MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
|
||||||
Scheduler& scheduler_)
|
Scheduler& scheduler_, const PresentFilters& filters_)
|
||||||
: device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
|
: device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
|
||||||
present_manager{present_manager_}, scheduler{scheduler_}, image_count{1},
|
present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1},
|
||||||
swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
|
swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
|
||||||
|
|
||||||
BlitScreen::~BlitScreen() = default;
|
BlitScreen::~BlitScreen() = default;
|
||||||
|
@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() {
|
||||||
|
|
||||||
void BlitScreen::SetWindowAdaptPass() {
|
void BlitScreen::SetWindowAdaptPass() {
|
||||||
layers.clear();
|
layers.clear();
|
||||||
scaling_filter = Settings::values.scaling_filter.GetValue();
|
scaling_filter = filters.get_scaling_filter();
|
||||||
|
|
||||||
switch (scaling_filter) {
|
switch (scaling_filter) {
|
||||||
case Settings::ScalingFilter::NearestNeighbor:
|
case Settings::ScalingFilter::NearestNeighbor:
|
||||||
|
@ -59,7 +60,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
|
||||||
bool presentation_recreate_required = false;
|
bool presentation_recreate_required = false;
|
||||||
|
|
||||||
// Recreate dynamic resources if the adapting filter changed
|
// Recreate dynamic resources if the adapting filter changed
|
||||||
if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) {
|
if (!window_adapt || scaling_filter != filters.get_scaling_filter()) {
|
||||||
resource_update_required = true;
|
resource_update_required = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
|
||||||
|
|
||||||
while (layers.size() < framebuffers.size()) {
|
while (layers.size() < framebuffers.size()) {
|
||||||
layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
|
layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
|
||||||
window_size, window_adapt->GetDescriptorSetLayout());
|
window_size, window_adapt->GetDescriptorSetLayout(), filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the draw
|
// Perform the draw
|
||||||
|
@ -119,8 +120,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l
|
||||||
VkFormat current_view_format) {
|
VkFormat current_view_format) {
|
||||||
const bool format_updated =
|
const bool format_updated =
|
||||||
std::exchange(swapchain_view_format, current_view_format) != current_view_format;
|
std::exchange(swapchain_view_format, current_view_format) != current_view_format;
|
||||||
if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() ||
|
if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) {
|
||||||
format_updated) {
|
|
||||||
WaitIdle();
|
WaitIdle();
|
||||||
SetWindowAdaptPass();
|
SetWindowAdaptPass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PresentFilters;
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
struct FramebufferConfig;
|
struct FramebufferConfig;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ class BlitScreen {
|
||||||
public:
|
public:
|
||||||
explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
|
explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
|
||||||
MemoryAllocator& memory_allocator, PresentManager& present_manager,
|
MemoryAllocator& memory_allocator, PresentManager& present_manager,
|
||||||
Scheduler& scheduler);
|
Scheduler& scheduler, const PresentFilters& filters);
|
||||||
~BlitScreen();
|
~BlitScreen();
|
||||||
|
|
||||||
void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
|
void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
|
||||||
|
@ -70,6 +72,7 @@ private:
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
PresentManager& present_manager;
|
PresentManager& present_manager;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
const PresentFilters& filters;
|
||||||
std::size_t image_count{};
|
std::size_t image_count{};
|
||||||
std::size_t image_index{};
|
std::size_t image_index{};
|
||||||
VkFormat swapchain_view_format{};
|
VkFormat swapchain_view_format{};
|
||||||
|
|
|
@ -746,7 +746,13 @@ std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImage
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
|
const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
|
||||||
const ImageViewInfo info{ImageViewType::e2D, view_format};
|
ImageViewInfo info{ImageViewType::e2D, view_format};
|
||||||
|
if (config.blending == Tegra::BlendMode::Opaque) {
|
||||||
|
info.x_source = static_cast<u8>(SwizzleSource::R);
|
||||||
|
info.y_source = static_cast<u8>(SwizzleSource::G);
|
||||||
|
info.z_source = static_cast<u8>(SwizzleSource::B);
|
||||||
|
info.w_source = static_cast<u8>(SwizzleSource::OneFloat);
|
||||||
|
}
|
||||||
return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
|
return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
|
||||||
slot_images[image_id].IsRescaled());
|
slot_images[image_id].IsRescaled());
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,9 @@ add_executable(yuzu
|
||||||
configuration/configuration_shared.cpp
|
configuration/configuration_shared.cpp
|
||||||
configuration/configuration_shared.h
|
configuration/configuration_shared.h
|
||||||
configuration/configure.ui
|
configuration/configure.ui
|
||||||
|
configuration/configure_applets.cpp
|
||||||
|
configuration/configure_applets.h
|
||||||
|
configuration/configure_applets.ui
|
||||||
configuration/configure_audio.cpp
|
configuration/configure_audio.cpp
|
||||||
configuration/configure_audio.h
|
configuration/configure_audio.h
|
||||||
configuration/configure_audio.ui
|
configuration/configure_audio.ui
|
||||||
|
|
86
src/yuzu/configuration/configure_applets.cpp
Normal file
86
src/yuzu/configuration/configure_applets.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "ui_configure_applets.h"
|
||||||
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
|
#include "yuzu/configuration/configure_applets.h"
|
||||||
|
#include "yuzu/configuration/shared_widget.h"
|
||||||
|
|
||||||
|
ConfigureApplets::ConfigureApplets(Core::System& system_,
|
||||||
|
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
|
||||||
|
const ConfigurationShared::Builder& builder, QWidget* parent)
|
||||||
|
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
Setup(builder);
|
||||||
|
|
||||||
|
SetConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureApplets::~ConfigureApplets() = default;
|
||||||
|
|
||||||
|
void ConfigureApplets::changeEvent(QEvent* event) {
|
||||||
|
if (event->type() == QEvent::LanguageChange) {
|
||||||
|
RetranslateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget::changeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureApplets::RetranslateUI() {
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) {
|
||||||
|
auto& library_applets_layout = *ui->group_library_applet_modes->layout();
|
||||||
|
std::map<u32, QWidget*> applets_hold{};
|
||||||
|
|
||||||
|
std::vector<Settings::BasicSetting*> settings;
|
||||||
|
auto push = [&settings](auto& list) {
|
||||||
|
for (auto setting : list) {
|
||||||
|
settings.push_back(setting);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]);
|
||||||
|
|
||||||
|
for (auto setting : settings) {
|
||||||
|
ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
|
||||||
|
|
||||||
|
if (widget == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!widget->Valid()) {
|
||||||
|
widget->deleteLater();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Untested applets
|
||||||
|
if (setting->Id() == Settings::values.data_erase_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.error_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.net_connect_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.web_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.shop_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.login_share_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() ||
|
||||||
|
setting->Id() == Settings::values.my_page_applet_mode.Id()) {
|
||||||
|
widget->setHidden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
applets_hold.emplace(setting->Id(), widget);
|
||||||
|
}
|
||||||
|
for (const auto& [label, widget] : applets_hold) {
|
||||||
|
library_applets_layout.addWidget(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureApplets::SetConfiguration() {}
|
||||||
|
|
||||||
|
void ConfigureApplets::ApplyConfiguration() {
|
||||||
|
const bool powered_on = system.IsPoweredOn();
|
||||||
|
for (const auto& func : apply_funcs) {
|
||||||
|
func(powered_on);
|
||||||
|
}
|
||||||
|
}
|
48
src/yuzu/configuration/configure_applets.h
Normal file
48
src/yuzu/configuration/configure_applets.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
|
|
||||||
|
class QCheckBox;
|
||||||
|
class QLineEdit;
|
||||||
|
class QComboBox;
|
||||||
|
class QDateTimeEdit;
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ConfigureApplets;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ConfigurationShared {
|
||||||
|
class Builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigureApplets : public ConfigurationShared::Tab {
|
||||||
|
public:
|
||||||
|
explicit ConfigureApplets(Core::System& system_,
|
||||||
|
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
|
||||||
|
const ConfigurationShared::Builder& builder,
|
||||||
|
QWidget* parent = nullptr);
|
||||||
|
~ConfigureApplets() override;
|
||||||
|
|
||||||
|
void ApplyConfiguration() override;
|
||||||
|
void SetConfiguration() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void changeEvent(QEvent* event) override;
|
||||||
|
void RetranslateUI();
|
||||||
|
|
||||||
|
void Setup(const ConfigurationShared::Builder& builder);
|
||||||
|
|
||||||
|
std::vector<std::function<void(bool)>> apply_funcs{};
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::ConfigureApplets> ui;
|
||||||
|
bool enabled = false;
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
};
|
65
src/yuzu/configuration/configure_applets.ui
Normal file
65
src/yuzu/configuration/configure_applets.ui
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigureApplets</class>
|
||||||
|
<widget class="QWidget" name="ConfigureApplets">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>605</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="accessibleName">
|
||||||
|
<string>Applets</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="group_library_applet_modes">
|
||||||
|
<property name="title">
|
||||||
|
<string>Applet mode preference</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="applets_widget" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -8,6 +8,7 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "ui_configure.h"
|
#include "ui_configure.h"
|
||||||
#include "vk_device_info.h"
|
#include "vk_device_info.h"
|
||||||
|
#include "yuzu/configuration/configure_applets.h"
|
||||||
#include "yuzu/configuration/configure_audio.h"
|
#include "yuzu/configuration/configure_audio.h"
|
||||||
#include "yuzu/configuration/configure_cpu.h"
|
#include "yuzu/configuration/configure_cpu.h"
|
||||||
#include "yuzu/configuration/configure_debug_tab.h"
|
#include "yuzu/configuration/configure_debug_tab.h"
|
||||||
|
@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
|
||||||
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
|
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
|
||||||
registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
|
registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
|
||||||
this, !system_.IsPoweredOn())},
|
this, !system_.IsPoweredOn())},
|
||||||
|
applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)},
|
||||||
audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
|
audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
|
||||||
cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
|
cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
|
||||||
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
|
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
|
||||||
|
@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
|
||||||
|
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->tabWidget->addTab(applets_tab.get(), tr("Applets"));
|
||||||
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
|
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
|
||||||
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
|
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
|
||||||
ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug"));
|
ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug"));
|
||||||
|
@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() {
|
||||||
debug_tab_tab->ApplyConfiguration();
|
debug_tab_tab->ApplyConfiguration();
|
||||||
web_tab->ApplyConfiguration();
|
web_tab->ApplyConfiguration();
|
||||||
network_tab->ApplyConfiguration();
|
network_tab->ApplyConfiguration();
|
||||||
|
applets_tab->ApplyConfiguration();
|
||||||
system.ApplySettings();
|
system.ApplySettings();
|
||||||
Settings::LogSettings();
|
Settings::LogSettings();
|
||||||
}
|
}
|
||||||
|
@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() {
|
||||||
{{tr("General"),
|
{{tr("General"),
|
||||||
{general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}},
|
{general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}},
|
||||||
{tr("System"),
|
{tr("System"),
|
||||||
{system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}},
|
{system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(),
|
||||||
|
applets_tab.get()}},
|
||||||
{tr("CPU"), {cpu_tab.get()}},
|
{tr("CPU"), {cpu_tab.get()}},
|
||||||
{tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}},
|
{tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}},
|
||||||
{tr("Audio"), {audio_tab.get()}},
|
{tr("Audio"), {audio_tab.get()}},
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConfigureApplets;
|
||||||
class ConfigureAudio;
|
class ConfigureAudio;
|
||||||
class ConfigureCpu;
|
class ConfigureCpu;
|
||||||
class ConfigureDebugTab;
|
class ConfigureDebugTab;
|
||||||
|
@ -75,6 +76,7 @@ private:
|
||||||
std::unique_ptr<ConfigurationShared::Builder> builder;
|
std::unique_ptr<ConfigurationShared::Builder> builder;
|
||||||
std::vector<ConfigurationShared::Tab*> tab_group;
|
std::vector<ConfigurationShared::Tab*> tab_group;
|
||||||
|
|
||||||
|
std::unique_ptr<ConfigureApplets> applets_tab;
|
||||||
std::unique_ptr<ConfigureAudio> audio_tab;
|
std::unique_ptr<ConfigureAudio> audio_tab;
|
||||||
std::unique_ptr<ConfigureCpu> cpu_tab;
|
std::unique_ptr<ConfigureCpu> cpu_tab;
|
||||||
std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
|
std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
|
||||||
|
|
|
@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||||
|
|
||||||
// A setting can be ignored by giving it a blank name
|
// A setting can be ignored by giving it a blank name
|
||||||
|
|
||||||
|
// Applets
|
||||||
|
INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral());
|
||||||
|
INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral());
|
||||||
|
INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral());
|
||||||
|
INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral());
|
||||||
|
INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral());
|
||||||
|
INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral());
|
||||||
|
INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral());
|
||||||
|
INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral());
|
||||||
|
INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral());
|
||||||
|
INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral());
|
||||||
|
INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral());
|
||||||
|
INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral());
|
||||||
|
INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral());
|
||||||
|
INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral());
|
||||||
|
INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral());
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
|
INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
|
||||||
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
|
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
|
||||||
|
@ -203,6 +220,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||||
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
|
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
|
||||||
|
|
||||||
// Intentionally skipping VSyncMode to let the UI fill that one out
|
// Intentionally skipping VSyncMode to let the UI fill that one out
|
||||||
|
translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
|
||||||
|
{
|
||||||
|
PAIR(AppletMode, HLE, tr("Custom frontend")),
|
||||||
|
PAIR(AppletMode, LLE, tr("Real applet")),
|
||||||
|
}});
|
||||||
|
|
||||||
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
|
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue