Compare commits

..

4 Commits

Author SHA1 Message Date
lizzie 86be89ed46 implode 2026-08-23 17:13:27 +00:00
lizzie c9c59407ca [hle/kernel] remove flusher thread from OutputDebugString
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-23 15:58:31 +00:00
John 55acf5d260 [Vulkan] Apply MK8D Buffer Fix to Linux (#4289)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Credit: MaranBR
- Adds the Rainbow Graphics Bug Fix to Linux as the game needs MAX_STREAM_BUFFER_SIZE = 256_MiB

![image](/attachments/722d4014-9449-47a0-be47-730b99e0b464)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4289
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-23 17:43:35 +02:00
crueter 3e07b466eb [vulkan] Adjustments on MSAA and BlitHelpers (#4287)
PR/Commit Owner: CamilleLaVey

This PR contains changes complementary to the MSAA refactor from some weeks ago, adds proper shader convert to depth, stencil for msaa and non msaa convertions; removes redundant helpers and unify paths on the resolve functions, just to make the readability and maintainability better; includes fixes for the blit operations on Nvidia, Intel/Windows (previously banned) and ensures Linux retain the fix without extra burden; fixes the QCOM driver bug on resolution upscaling on any games above x1 (includes turnip on A8xx series) and fixes crashes/ resolution upscaled bugs on certain games that used to have wrong viewporting effect on screen or directly device loss on Vulkan. Adds fixes for regressions caused by previous MSAA refactor on games as Fire Emblem: Engage.

Special Thanks

1.- Big Smolio (@Gidoly)

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4287
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-23 17:31:09 +02:00
107 changed files with 1917 additions and 1810 deletions
+4 -45
View File
@@ -328,9 +328,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
m_system.RegisterApplicationChangedCallback(
[&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); });
// Register an ExecuteProgram callback such that Core can execute a sub-program
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
m_next_program_index = program_index_;
@@ -410,58 +407,20 @@ void EmulationSession::RunEmulation() {
}
while (true) {
std::optional<u64> reload_title;
{
[[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
return !m_is_running || m_pending_shader_cache_title.has_value();
})) {
if (!m_is_running) {
break;
}
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
break;
}
}
if (reload_title.has_value())
ReloadDiskShaderCache(*reload_title);
}
// Reset current applet ID.
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
}
void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) {
{
std::scoped_lock lock(m_mutex);
m_pending_shader_cache_title = program_id;
}
m_cv.notify_one();
}
void EmulationSession::ReloadDiskShaderCache(u64 program_id) {
if (!Settings::values.use_disk_shader_cache.GetValue())
return;
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
const bool was_paused = m_is_paused;
m_system.Pause();
m_system.GPU().WaitForIdle();
m_system.GPU().ObtainContext();
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{},
LoadDiskCacheProgress);
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
m_system.GPU().ReleaseContext();
if (!was_paused)
m_system.Run();
}
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
return m_software_keyboard;
}
-5
View File
@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <optional>
#include <android/native_window_jni.h>
#include "common/android/applets/software_keyboard.h"
#include "core/core.h"
@@ -46,7 +44,6 @@ public:
void HaltEmulation();
void RunEmulation();
void ShutdownEmulation();
void RequestDiskShaderCacheReload(u64 program_id);
const Core::PerfStatsResults& PerfStats();
int ShadersBuilding();
@@ -68,7 +65,6 @@ private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStopped(Core::SystemResultStatus result);
static void ChangeProgram(std::size_t program_index);
void ReloadDiskShaderCache(u64 program_id);
private:
// Window management
@@ -87,7 +83,6 @@ private:
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
int m_applet_id{1};
std::optional<u64> m_pending_shader_cache_title;
// GPU driver parameters
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
+2 -46
View File
@@ -3,7 +3,6 @@
#include <array>
#include <atomic>
#include <filesystem>
#include <memory>
#include <utility>
@@ -296,8 +295,6 @@ struct System::Impl {
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
InitializeKernel(system);
system.SetCurrentApplicationFilePath(filepath);
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
// Create the application process
@@ -333,7 +330,7 @@ struct System::Impl {
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
// Make the process created be the application
kernel.SetApplicationProcess(process->GetHandle());
kernel.MakeApplicationProcess(process->GetHandle());
// Set up the rest of the system.
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
@@ -470,7 +467,6 @@ struct System::Impl {
Core::SpeedLimiter speed_limiter;
ExecuteProgramCallback execute_program_callback;
ExitCallback exit_callback;
ApplicationChangedCallback application_changed_callback;
std::optional<Service::Services> services;
std::optional<Core::Debugger> debugger;
@@ -492,8 +488,6 @@ struct System::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<u8, 0x20> build_id{};
std::string current_application_filepath;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// ContentProviderUnion instance
@@ -733,25 +727,7 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
}
u64 System::GetApplicationProcessProgramID() const {
const auto* const process = impl->kernel.ApplicationProcess();
return process != nullptr ? process->GetProgramId() : 0;
}
u64 System::GetProgramIdForProcessId(u64 process_id) const {
auto process = impl->kernel.GetProcessByProcessId(process_id);
return process.IsNull() ? 0 : process->GetProgramId();
}
u64 System::ResolveCallerProgramId(u64 process_id) const {
if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) {
return program_id;
}
const auto fallback = this->GetApplicationProcessProgramID();
LOG_WARNING(Core,
"Could not resolve caller process_id={}, falling back to application {:016X}",
process_id, fallback);
return fallback;
return impl->kernel.ApplicationProcess()->GetProgramId();
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
@@ -934,14 +910,6 @@ void System::ExecuteProgram(std::size_t program_index) {
}
}
const std::string& System::GetCurrentApplicationFilePath() const {
return impl->current_application_filepath;
}
void System::SetCurrentApplicationFilePath(const std::string& filepath) {
impl->current_application_filepath = filepath;
}
/// @brief Gets a reference to the user channel stack.
/// It is used to transfer data between programs.
std::vector<std::vector<u8>>& System::GetUserChannel() {
@@ -993,18 +961,6 @@ void System::Exit() {
}
}
void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) {
impl->application_changed_callback = std::move(callback);
}
void System::NotifyApplicationChanged(u64 program_id) {
//LOG_DEBUG(Core, "Running application changed to {:016X}", program_id);
if (impl->application_changed_callback) {
impl->application_changed_callback(program_id);
}
}
void System::ApplySettings() {
impl->RefreshTime(*this);
-11
View File
@@ -322,10 +322,6 @@ public:
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
[[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const;
[[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const;
/// Gets the name of the current game
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
@@ -427,9 +423,6 @@ public:
bool TryPopGeneralChannel(std::vector<u8>& out_data);
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
[[nodiscard]] const std::string& GetCurrentApplicationFilePath() const;
void SetCurrentApplicationFilePath(const std::string& filepath);
/// Type used for the frontend to designate a callback for System to exit the application.
using ExitCallback = std::function<void()>;
@@ -442,10 +435,6 @@ public:
/// Instructs the frontend to exit the application.
void Exit();
using ApplicationChangedCallback = std::function<void(u64 program_id)>;
void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback);
void NotifyApplicationChanged(u64 program_id);
/// Applies any changes to settings to this core instance.
void ApplySettings();
+4 -21
View File
@@ -349,18 +349,9 @@ struct KernelCore::Impl {
object_name_global_data.emplace(kernel);
}
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
if (application_process == process)
return;
KProcess* const previous = application_process;
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
application_process = process;
if (application_process != nullptr)
application_process->Open(kernel);
if (previous != nullptr)
previous->Close(kernel);
application_process->Open(kernel);
}
/// Sets the host thread ID for the caller.
@@ -888,8 +879,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
}
}
void KernelCore::SetApplicationProcess(KProcess* process) {
impl->SetApplicationProcess(*this, process);
void KernelCore::MakeApplicationProcess(KProcess* process) {
impl->MakeApplicationProcess(*this, process);
}
KProcess* KernelCore::ApplicationProcess() {
@@ -900,14 +891,6 @@ const KProcess* KernelCore::ApplicationProcess() const {
return impl->application_process;
}
KScopedAutoObject<KProcess> KernelCore::GetProcessByProcessId(u64 process_id) {
std::scoped_lock lk{impl->process_list_lock};
for (auto* const process : impl->process_list)
if (process != nullptr && process->GetProcessId() == process_id)
return {*this, process};
return {*this, nullptr};
}
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
std::list<KScopedAutoObject<KProcess>> processes;
std::scoped_lock lk{impl->process_list_lock};
+2 -5
View File
@@ -124,8 +124,8 @@ public:
void AppendNewProcess(KProcess* process);
void RemoveProcess(KProcess* process);
/// Makes the given process the current application process.
void SetApplicationProcess(KProcess* process);
/// Makes the given process the new application process.
void MakeApplicationProcess(KProcess* process);
/// Retrieves a pointer to the application process.
KProcess* ApplicationProcess();
@@ -133,9 +133,6 @@ public:
/// Retrieves a const pointer to the application process.
const KProcess* ApplicationProcess() const;
/// Retrieves the process with the given process ID, or a null object.
KScopedAutoObject<KProcess> GetProcessByProcessId(u64 process_id);
/// Retrieves the list of processes.
std::list<KScopedAutoObject<KProcess>> GetProcessList();
+3 -47
View File
@@ -17,56 +17,12 @@
namespace Kernel::Svc {
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
const auto MAX_MSG_SIZE = 0x1000;
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
static struct DebugFlusher {
std::string msg_buffer;
std::mutex msg_mutex;
std::condition_variable msg_cv;
std::chrono::steady_clock::time_point last_msg_time;
std::optional<std::jthread> thread;
} flusher_data;
R_SUCCEED_IF(len == 0);
// Only start the thread the very first time this function is called
if (!flusher_data.thread) {
flusher_data.thread.emplace([](std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
std::unique_lock lock(flusher_data.msg_mutex);
flusher_data.msg_cv.wait(lock, [&stop_token] {
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
});
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
break;
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
});
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
if (!flusher_data.msg_buffer.empty()) {
// Remove trailing newline as LOG_INFO adds that anyways
if (flusher_data.msg_buffer.back() == '\n')
flusher_data.msg_buffer.pop_back();
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
flusher_data.msg_buffer.clear();
}
if (stop_token.stop_requested()) break;
}
}
flusher_data.msg_cv.notify_all();
});
}
{
std::lock_guard lock(flusher_data.msg_mutex);
const auto old_size = flusher_data.msg_buffer.size();
flusher_data.msg_buffer.resize(old_size + len);
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
flusher_data.last_msg_time = std::chrono::steady_clock::now();
}
flusher_data.msg_cv.notify_one();
std::string msg_buffer(len, 0);
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
R_SUCCEED();
}
-4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,7 +10,6 @@ namespace Service::AM {
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
+1 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -201,14 +201,6 @@ enum class ProgramSpecifyKind : u32 {
RestartProgram = 2,
};
// Maufeat: Use enums for zindex instead of using random zindex numbers
enum AppletZIndex : s32 {
Background = 0,
Foreground = 1,
ForegroundVisible = 2,
Overlay = 3,
};
struct CommonArguments {
CommonArgumentVersion arguments_version;
CommonArgumentSize size;
+9 -9
View File
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
}
}
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
void Applet::SetInteractibleLocked(bool interactible) {
if (is_interactible == interactible) {
return;
}
is_pad_interactible = pad_interactible;
is_touch_interactible = touch_interactible;
is_interactible = interactible;
const bool exit_requested = lifecycle_manager.GetExitRequested();
const bool pad_enabled = pad_interactible && !exit_requested;
const bool touch_enabled = touch_interactible && !exit_requested;
const bool input_enabled = interactible && !exit_requested;
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
}
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
hid_registration.EnableAppletToGetInput(input_enabled);
}
void Applet::OnProcessTerminatedLocked() {
+3 -5
View File
@@ -125,11 +125,9 @@ struct Applet {
bool album_image_taken_notification_enabled{};
bool record_volume_muted{};
bool is_activity_runnable{};
bool is_pad_interactible{true};
bool is_touch_interactible{true};
bool is_interactible{true};
bool window_visible{true};
bool overlay_watching_short_home_button{false};
bool overlay_handling_touch_input{false};
bool overlay_in_foreground{false};
// Events
Event overlay_event;
@@ -150,7 +148,7 @@ struct Applet {
// Process state management
void UpdateSuspensionStateLocked(bool force_message);
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
void SetInteractibleLocked(bool interactible);
void OnProcessTerminatedLocked();
};
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -6,7 +6,6 @@
#include "core/core.h"
#include "core/hle/service/am/display_layer_manager.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/container.h"
@@ -34,7 +33,6 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
m_system_shared_buffer_id = 0;
m_system_shared_layer_id = 0;
m_applet_id = applet_id;
m_library_applet_mode = mode;
m_buffer_sharing_enabled = false;
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
@@ -74,16 +72,14 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
this->GetLayerStackMask());
if (m_applet_id != AppletId::Application) {
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
if (m_applet_id == AppletId::OverlayDisplay) {
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
} else {
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
}
}
@@ -126,12 +122,10 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
// Ensure the overlay layer is visible
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
this->GetLayerStackMask());
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
s32 initial_z = Foreground;
s32 initial_z = 1;
if (m_applet_id == AppletId::OverlayDisplay) {
initial_z = Overlay;
initial_z = -1;
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
}
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
@@ -148,36 +142,6 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
R_SUCCEED();
}
u32 DisplayLayerManager::GetLayerStackMask() const {
using Nvnflinger::LayerStackBit;
using Nvnflinger::LayerStackId;
constexpr u32 Displayed = LayerStackBit(LayerStackId::Default);
constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot);
constexpr u32 Recording = LayerStackBit(LayerStackId::Recording);
constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame);
constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug);
switch (m_applet_id) {
case AppletId::Application:
return Displayed | Screenshot | Recording | LastFrame | Debug;
case AppletId::OverlayDisplay:
return Displayed;
case AppletId::QLaunch:
return Displayed;
default:
break;
}
switch (m_library_applet_mode) {
case LibraryAppletMode::AllForeground:
case LibraryAppletMode::AllForegroundInitiallyHidden:
return Displayed | Screenshot | LastFrame;
default:
return Displayed | Screenshot;
}
}
void DisplayLayerManager::SetWindowVisibility(bool visible) {
if (m_visible == visible) {
return;
@@ -221,17 +185,10 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
}
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
s32* out_fbshare_layer_index,
VI::CaptureKind kind) {
s32* out_fbshare_layer_index) {
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
out_was_written, out_fbshare_layer_index, kind));
}
Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) {
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer(
fbshare_layer_index, color));
out_was_written, out_fbshare_layer_index));
}
} // namespace Service::AM
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -23,7 +23,6 @@ class KProcess;
namespace Service::VI {
class IApplicationDisplayService;
class IManagerDisplayService;
enum class CaptureKind : u32;
} // namespace Service::VI
namespace Service::AM {
@@ -49,13 +48,9 @@ public:
void SetOverlayZIndex(s32 z_index);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
VI::CaptureKind kind);
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
private:
u32 GetLayerStackMask() const;
Kernel::KProcess* m_process{};
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
@@ -64,7 +59,6 @@ private:
u64 m_system_shared_buffer_id{};
u64 m_system_shared_layer_id{};
AppletId m_applet_id{};
LibraryAppletMode m_library_applet_mode{};
bool m_buffer_sharing_enabled{};
bool m_blending_enabled{};
bool m_visible{true};
+6 -11
View File
@@ -36,17 +36,12 @@ HidRegistration::~HidRegistration() {
}
}
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
if (!m_process.IsInitialized())
return;
const auto resource_manager = m_hid_server->GetResourceManager();
const u64 aruid = m_process.GetProcessId();
resource_manager->EnablePadInput(aruid, enable_pad);
resource_manager->EnableTouchScreen(aruid, enable_touch);
resource_manager->SetAruidValidForVibration(aruid, enable_pad);
void HidRegistration::EnableAppletToGetInput(bool enable) {
if (m_process.IsInitialized()) {
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
enable);
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
}
}
} // namespace Service::AM
+1 -1
View File
@@ -28,7 +28,7 @@ public:
~HidRegistration();
void RegisterCurrentProcess();
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
void EnableAppletToGetInput(bool enable);
private:
Process& m_process;
@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/service/applet_common_functions.h"
#include "core/hle/service/cmif_serialization.h"
@@ -80,7 +78,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
R_SUCCEED();
}
@@ -150,7 +150,7 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
// FIXME: all of this stuff belongs to ns
// TODO(bunnei): This should be configurable
LOG_DEBUG(Service_AM, "called, program_id={:016X}", m_applet->program_id);
LOG_DEBUG(Service_AM, "called");
// Get supported languages from NACP, if possible
// Default to 0 (all languages supported)
@@ -430,16 +430,15 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value)
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id);
LOG_INFO(Service_AM, "called, application_id={:016X}", application_id);
// If application_id is 0 or matches the current application, relaunch the current application.
if (application_id == 0 || application_id == m_applet->program_id) {
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
system.ExecuteProgram(0);
R_SUCCEED();
}
// If application_id is 0, relaunch the current application
const u64 target_application_id =
(application_id == 0) ? m_applet->program_id : application_id;
R_THROW(ResultUnknown);
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
system.ExecuteProgram(target_application_id);
R_SUCCEED();
}
Result IApplicationFunctions::ClearUserChannel() {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -8,7 +8,6 @@
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/service/display_controller.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/vi/shared_buffer_manager.h"
namespace Service::AM {
@@ -72,16 +71,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
}
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
fbshare_layer_index, color);
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
unknown0, fbshare_layer_index, color);
R_SUCCEED();
}
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_DEBUG(Service_AM, "called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index));
}
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
@@ -91,9 +90,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_DEBUG(Service_AM, "called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index));
}
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
@@ -103,9 +102,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_DEBUG(Service_AM, "called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index));
}
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/am/applet.h"
@@ -22,7 +22,7 @@ namespace Service::AM {
{10, nullptr, "StartShutdownSequenceForOverlay"},
{11, nullptr, "StartRebootSequenceForOverlay"},
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
{30, nullptr, "SetHealthWarningShowingState"},
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
{40, nullptr, "GetApplicationNintendoLogo"},
@@ -43,12 +43,10 @@ namespace Service::AM {
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
LOG_DEBUG(Service_AM, "called");
{
std::scoped_lock lk{m_applet->lock};
m_applet->overlay_watching_short_home_button = true;
}
m_applet->overlay_in_foreground = true;
m_applet->home_button_short_pressed_blocked = false;
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
window_system->RequestUpdate();
}
@@ -58,26 +56,16 @@ namespace Service::AM {
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
LOG_DEBUG(Service_AM, "called");
{
std::scoped_lock lk{m_applet->lock};
m_applet->overlay_watching_short_home_button = false;
}
m_applet->overlay_in_foreground = false;
m_applet->home_button_short_pressed_blocked = false;
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
window_system->RequestUpdate();
}
R_SUCCEED();
}
Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) {
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
std::scoped_lock lk{m_applet->lock};
m_applet->overlay_handling_touch_input = enabled;
R_SUCCEED();
}
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
LOG_DEBUG(Service_AM, "called");
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -20,7 +20,6 @@ namespace Service::AM {
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
Result IsHealthWarningRequired(Out<bool> is_required);
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result SetHandlingTouchScreenInputEnabled(bool enabled);
Result Unknown70();
private:
+80 -136
View File
@@ -4,11 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/applet_manager.h"
@@ -36,82 +32,46 @@ void WindowSystem::RequestUpdate() {
}
void WindowSystem::Update() {
{
std::scoped_lock lk{m_lock};
std::scoped_lock lk{m_lock};
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
// Loop through all applets and remove terminated applets.
this->PruneTerminatedAppletsLocked();
// Loop through all applets and remove terminated applets.
this->PruneTerminatedAppletsLocked();
// If the home menu is being locked into the foreground, handle that.
if (!this->LockHomeMenuIntoForegroundLocked()) {
const bool overlay_takes_input = this->DoesOverlayTakeInputLocked();
this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input);
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input);
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input);
}
// If the home menu is being locked into the foreground, handle that.
if (this->LockHomeMenuIntoForegroundLocked()) {
return;
}
this->NotifyApplicationChangedIfNeeded();
bool overlay_blocks_input = false;
if (m_overlay_display) {
std::scoped_lock lk_overlay{m_overlay_display->lock};
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
}
// Recursively update each applet root.
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
}
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
{
std::scoped_lock lk{m_lock};
std::scoped_lock lk{m_lock};
if (applet->applet_id == AppletId::QLaunch) {
ASSERT(m_home_menu == nullptr);
m_home_menu = applet.get();
} else if (applet->applet_id == AppletId::OverlayDisplay) {
m_overlay_display = applet.get();
} else if (is_application) {
ASSERT(m_application == nullptr);
m_application = applet.get();
}
this->UpdateCurrentApplicationLocked();
m_event_observer->TrackAppletProcess(*applet);
m_applets.emplace(applet->aruid.pid, std::move(applet));
if (applet->applet_id == AppletId::QLaunch) {
ASSERT(m_home_menu == nullptr);
m_home_menu = applet.get();
} else if (applet->applet_id == AppletId::OverlayDisplay) {
m_overlay_display = applet.get();
} else if (is_application) {
ASSERT(m_application == nullptr);
m_application = applet.get();
}
this->NotifyApplicationChangedIfNeeded();
}
void WindowSystem::UpdateCurrentApplicationLocked() {
const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu;
if (candidate == nullptr) {
return;
}
auto* const process = candidate->process->GetHandle();
if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) {
return;
}
LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id);
m_system.Kernel().SetApplicationProcess(process);
Settings::SetCurrentProgramID(candidate->program_id);
m_pending_application_notification = candidate->program_id;
}
void WindowSystem::NotifyApplicationChangedIfNeeded() {
std::optional<u64> program_id;
{
std::scoped_lock lk{m_lock};
program_id = std::exchange(m_pending_application_notification, std::nullopt);
}
if (!program_id.has_value()) {
return;
}
m_system.NotifyApplicationChanged(*program_id);
m_event_observer->TrackAppletProcess(*applet);
m_applets.emplace(applet->aruid.pid, std::move(applet));
}
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
@@ -209,40 +169,18 @@ void WindowSystem::OnExitRequested() {
}
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
const auto is_blocked = [message](const Applet& applet) {
if (message == AppletMessage::DetectShortPressingHomeButton &&
applet.applet_id == AppletId::OverlayDisplay &&
!applet.overlay_watching_short_home_button) {
return true;
}
switch (message) {
case AppletMessage::DetectShortPressingHomeButton:
return applet.home_button_short_pressed_blocked;
case AppletMessage::DetectLongPressingHomeButton:
return applet.home_button_long_pressed_blocked;
default:
return false;
}
};
const auto send_to = [&](Applet* applet) {
if (!applet) {
return;
}
std::scoped_lock lk{applet->lock};
if (is_blocked(*applet)) {
LOG_DEBUG(Service_AM, "Applet {} is blocking message {}",
static_cast<u32>(applet->applet_id), static_cast<u32>(message));
return;
}
applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
};
send_to(m_home_menu);
send_to(m_overlay_display);
send_to(m_application);
if (m_home_menu) {
std::scoped_lock lk_home{m_home_menu->lock};
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
}
if (m_overlay_display) {
std::scoped_lock lk_overlay{m_overlay_display->lock};
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
}
if (m_application) {
std::scoped_lock lk_application{m_application->lock};
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
}
if (m_event_observer) {
m_event_observer->RequestUpdate();
}
@@ -254,9 +192,19 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
case SystemButtonType::HomeButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
break;
case SystemButtonType::HomeButtonLongPressing:
case SystemButtonType::HomeButtonLongPressing: {
// Toggle overlay foreground visibility on long home press
if (m_overlay_display) {
std::scoped_lock lk_overlay{m_overlay_display->lock};
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
}
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
break;
// Force a state update after toggling overlay
if (m_event_observer) {
m_event_observer->RequestUpdate();
}
break; }
case SystemButtonType::CaptureButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
break;
@@ -369,8 +317,6 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
m_overlay_display = nullptr;
}
this->UpdateCurrentApplicationLocked();
// Finalize applet.
applet->OnProcessTerminatedLocked();
@@ -443,20 +389,7 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
applet->lock.lock();
}
bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const {
return overlay.window_visible && overlay.overlay_watching_short_home_button;
}
bool WindowSystem::DoesOverlayTakeInputLocked() const {
if (m_overlay_display == nullptr) {
return false;
}
std::scoped_lock lk{m_overlay_display->lock};
return this->IsOverlayOpenLocked(*m_overlay_display);
}
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) {
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
// With no applet, we don't have anything to do.
if (!applet) {
return;
@@ -487,18 +420,24 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
return false;
}();
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
// Update visibility state.
const bool should_be_visible =
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
? applet->window_visible
: (is_foreground && applet->window_visible);
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
const bool needs_hid_input =
is_overlay ? this->IsOverlayOpenLocked(*applet)
: (is_foreground && applet->window_visible && !overlay_takes_input);
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
? applet->overlay_in_foreground
: (is_foreground && applet->window_visible && !overlay_blocking);
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
}
applet->SetInteractibleLocked(should_be_interactible);
// Update focus state and suspension.
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
@@ -514,18 +453,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
applet->UpdateSuspensionStateLocked(true);
}
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
s32 z_index = Background;
if (is_overlay) {
z_index = Overlay;
} else if (inherited_foreground) {
z_index = is_obscured ? Foreground : ForegroundVisible;
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
s32 z_index = 0;
const bool now_foreground = inherited_foreground;
if (applet->applet_id == AppletId::OverlayDisplay) {
z_index = applet->overlay_in_foreground ? 100000 : -1;
} else if (now_foreground && !is_obscured) {
z_index = 2;
} else if (now_foreground) {
z_index = 1;
} else {
z_index = 0;
}
applet->display_layer_manager.SetOverlayZIndex(z_index);
// Recurse into child applets.
for (const auto& child_applet : applet->child_applets) {
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
}
}
+1 -8
View File
@@ -9,7 +9,6 @@
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include "common/common_types.h"
#include "core/hle/service/am/am_types.h"
@@ -61,16 +60,11 @@ public:
void OnPowerButtonPressed(ButtonPressDuration type) {}
private:
void UpdateCurrentApplicationLocked();
void NotifyApplicationChangedIfNeeded();
void PruneTerminatedAppletsLocked();
bool RestartAppletProcessLocked(Applet* applet);
bool LockHomeMenuIntoForegroundLocked();
void TerminateChildAppletsLocked(Applet* applet);
bool IsOverlayOpenLocked(const Applet& overlay) const;
bool DoesOverlayTakeInputLocked() const;
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
void SendButtonAppletMessageLocked(AppletMessage message);
private:
@@ -94,7 +88,6 @@ private:
// Applet map by aruid.
std::map<u64, std::shared_ptr<Applet>> m_applets{};
std::optional<u64> m_pending_application_notification{};
};
} // namespace Service::AM
@@ -23,7 +23,6 @@
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/am/am_results.h"
#include "core/loader/loader.h"
namespace Service::AOC {
@@ -32,10 +31,6 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
return FileSys::GetBaseTitleID(title_id) == base;
}
static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) {
return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id));
}
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
std::vector<u64> add_on_content;
const auto& rcu = system.GetContentProvider();
@@ -96,7 +91,7 @@ IAddOnContentManager::~IAddOnContentManager() {
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
const auto current = GetCallerBaseTitleID(system, process_id);
const auto current = system.GetApplicationProcessProgramID();
const auto& disabled = Settings::values.disabled_addons[current];
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
@@ -117,7 +112,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
process_id.pid);
const auto current = GetCallerBaseTitleID(system, process_id);
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
std::vector<u32> out;
const auto& disabled = Settings::values.disabled_addons[current];
@@ -131,7 +126,8 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
}
}
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
// TODO(DarkLordZach): Find the correct error code.
R_UNLESS(out.size() >= offset, ResultUnknown);
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
std::rotate(out.begin(), out.begin() + offset, out.end());
@@ -145,7 +141,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
ClientProcessId process_id) {
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
const auto title_id = system.ResolveCallerProgramId(*process_id);
const auto title_id = system.GetApplicationProcessProgramID();
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
+7 -5
View File
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
return out;
}
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
progress{{
ProgressServiceBackend{system_, "Normal"},
ProgressServiceBackend{system_, "Directory"},
@@ -70,7 +70,8 @@ Result IBcatService::RequestSyncDeliveryCache(
LOG_DEBUG(Service_BCAT, "called");
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
GetProgressBackend(SyncType::Normal));
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
@@ -85,8 +86,9 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
LOG_DEBUG(Service_BCAT, "called, name={}", name);
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
name, progress_backend);
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
name, progress_backend);
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
system, progress_backend.GetEvent(), progress_backend.GetImpl());
+1 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -22,7 +19,7 @@ class IDeliveryCacheProgressService;
class IBcatService final : public ServiceFramework<IBcatService> {
public:
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
~IBcatService() override;
private:
@@ -42,7 +39,6 @@ private:
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
BcatBackend& backend;
u64 program_id;
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
};
@@ -1,10 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/bcat/bcat_service.h"
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
#include "core/hle/service/bcat/service_creator.h"
@@ -41,8 +37,7 @@ IServiceCreator::~IServiceCreator() = default;
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
OutInterface<IBcatService> out_interface) {
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
*out_interface =
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
*out_interface = std::make_shared<IBcatService>(system, *backend);
R_SUCCEED();
}
@@ -50,7 +45,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
const auto title_id = system.ResolveCallerProgramId(*process_id);
const auto title_id = system.GetApplicationProcessProgramID();
*out_interface =
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
R_SUCCEED();
+2 -7
View File
@@ -243,15 +243,10 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
}
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
const ScreenShotAttribute& attribute,
AlbumReportOption report_option,
const ApplicationData& app_data, std::span<const u8> image_data,
u64 aruid) {
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, AlbumReportOption report_option, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) {
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
const u64 title_id = system.ResolveCallerProgramId(aruid);
const u64 title_id = system.GetApplicationProcessProgramID();
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
+1 -2
View File
@@ -95,8 +95,7 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
manager->FlipVerticallyOnWrite(invert_y);
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
},
layout,
Nvnflinger::LayerStackId::Screenshot);
layout);
}
} // namespace Service::Capture
+1 -1
View File
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
GetResourceManager()->GetConsoleSixAxis()->Activate();
GetResourceManager()->GetSevenSixAxis()->Activate();
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
R_SUCCEED();
}
+1 -1
View File
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
R_SUCCEED();
}
+2 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -150,7 +147,7 @@ Result IRS::RunImageTransferProcessor(
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
@@ -298,8 +295,7 @@ Result IRS::RunImageTransferExProcessor(
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
t_mem->GetOwner());
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
+1 -1
View File
@@ -35,7 +35,7 @@ public:
, process{kernel, process_}
, user_rx{std::move(user_rx_)}
, user_ro{std::move(user_ro_)}
, context{process_->GetMemory()}
, context{system_.ApplicationMemory()}
{
// clang-format off
@@ -18,7 +18,6 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/language.h"
@@ -337,8 +336,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
constexpr s32 data_offset = 0;
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
auto& memory = t_mem->GetOwner()->GetMemory();
if (t_mem != nullptr && app_count > 0) {
auto& memory = system.ApplicationMemory();
const auto t_mem_address = t_mem->GetSourceAddress();
for (size_t i = 0; i < app_count; ++i) {
@@ -77,7 +77,6 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
.transform_flags = layer.transform,
.crop_rect = layer.crop_rect,
.blending = ConvertBlending(layer.blending),
.layer_stack_mask = layer.layer_stack_mask,
});
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
@@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 0x3:
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
case 0x8:
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false);
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
case 0x9:
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
case 0xb:
@@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 0x1a:
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
case 0x1b:
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true);
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
case 0x1d:
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
default:
@@ -387,19 +387,8 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
return NvResult::Success;
}
Core::Memory::Memory& nvhost_gpu::GetSessionMemory(DeviceFD fd) {
if (const auto it = sessions.find(fd); it != sessions.end())
if (auto* const session = core.GetSession(it->second);
session != nullptr && session->process != nullptr)
return session->process->GetMemory();
LOG_ERROR(Service_NVDRV, "No session for fd={}, falling back to application memory", fd);
return system.ApplicationMemory();
}
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
bool kickoff) {
std::span<Tegra::CommandListHeader> commands, bool kickoff) {
if (params.num_entries > commands.size()) {
UNIMPLEMENTED();
return NvResult::InvalidSize;
@@ -407,7 +396,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
Tegra::CommandList entries(params.num_entries);
if (kickoff) {
this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(),
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
std::memcpy(entries.command_lists.data(), commands.data(),
@@ -16,10 +16,6 @@
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/dma_pusher.h"
namespace Core::Memory {
class Memory;
}
namespace Tegra {
namespace Control {
struct ChannelState;
@@ -200,11 +196,8 @@ private:
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
Core::Memory::Memory& GetSessionMemory(DeviceFD fd);
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
bool kickoff = false);
std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
std::span<const Tegra::CommandListHeader> commands);
+2 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -15,8 +15,7 @@ struct Layer {
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
s32 consumer_id_)
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false),
layer_stack_mask(DefaultLayerStackMask) {}
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
~Layer() {
buffer_item_consumer->Abandon();
}
@@ -27,7 +26,6 @@ struct Layer {
bool visible;
s32 z_index;
bool is_overlay;
u32 layer_stack_mask;
};
struct LayerStack {
@@ -115,7 +115,6 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
.transform = static_cast<android::BufferTransformFlags>(item.transform),
.crop_rect = item.crop,
.acquire_fence = item.fence,
.layer_stack_mask = layer->layer_stack_mask,
});
}
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -26,25 +23,6 @@ enum class LayerBlending : u32 {
Coverage = 0x405,
};
enum class LayerStackId : u32 {
Default = 0,
Lcd = 1,
Screenshot = 2,
Recording = 3,
LastFrame = 4,
Arbitrary = 5,
ApplicationForDebug = 6,
Null = 10,
};
constexpr u32 LayerStackBit(LayerStackId id) {
return 1U << static_cast<u32>(id);
}
constexpr u32 DefaultLayerStackMask =
LayerStackBit(LayerStackId::Default) | LayerStackBit(LayerStackId::Screenshot) |
LayerStackBit(LayerStackId::Recording) | LayerStackBit(LayerStackId::LastFrame);
struct HwcLayer {
u32 buffer_handle;
u32 offset;
@@ -57,7 +35,6 @@ struct HwcLayer {
android::BufferTransformFlags transform;
Common::Rectangle<int> crop_rect;
android::Fence acquire_fence;
u32 layer_stack_mask;
};
} // namespace Service::Nvnflinger
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -104,14 +104,6 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen
}
}
void SurfaceFlinger::SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask) {
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->layer_stack_mask = layer_stack_mask;
LOG_DEBUG(Service_VI, "Layer {} stack mask set to {:#x}", consumer_binder_id,
layer_stack_mask);
}
}
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->is_overlay = is_overlay;
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -48,7 +48,6 @@ public:
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
void SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask);
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -13,10 +13,8 @@
namespace Service::PCTL {
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_,
u64 program_id_)
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_)
: ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
program_id{program_id_},
service_context{system_, "IParentalControlService"}, synchronization_event{service_context},
unlinked_event{service_context}, request_suspension_event{service_context} {
// clang-format off
@@ -204,6 +202,7 @@ Result IParentalControlService::Initialize() {
// TODO(ogniK): Recovery flag initialization for pctl:r
const auto program_id = system.GetApplicationProcessProgramID();
if (program_id != 0) {
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
system.GetContentProvider()};
@@ -16,8 +16,7 @@ namespace Service::PCTL {
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
public:
explicit IParentalControlService(Core::System& system_, Capability capability_,
u64 program_id_);
explicit IParentalControlService(Core::System& system_, Capability capability_);
~IParentalControlService() override;
private:
@@ -85,7 +84,6 @@ private:
RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{};
Capability capability{};
u64 program_id{};
// TODO: this is raw
PlayTimerSettings raw_play_timer_settings{};
@@ -1,10 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/pctl/parental_control_service.h"
#include "core/hle/service/pctl/parental_control_service_factory.h"
@@ -27,17 +23,17 @@ IParentalControlServiceFactory::~IParentalControlServiceFactory() = default;
Result IParentalControlServiceFactory::CreateService(
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
*out_service = std::make_shared<IParentalControlService>(
system, capability, system.ResolveCallerProgramId(*process_id));
LOG_DEBUG(Service_PCTL, "called");
// TODO(ogniK): Get application id from process
*out_service = std::make_shared<IParentalControlService>(system, capability);
R_SUCCEED();
}
Result IParentalControlServiceFactory::CreateServiceWithoutInitialize(
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
*out_service = std::make_shared<IParentalControlService>(
system, capability, system.ResolveCallerProgramId(*process_id));
LOG_DEBUG(Service_PCTL, "called");
// TODO(ogniK): Get application id from process
*out_service = std::make_shared<IParentalControlService>(system, capability);
R_SUCCEED();
}
+5 -2
View File
@@ -132,7 +132,8 @@ private:
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
auto process = kernel.GetProcessByProcessId(pid);
auto list = kernel.GetProcessList();
auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; });
if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -185,7 +186,9 @@ private:
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
auto process = kernel.GetProcessByProcessId(process_id);
auto list = kernel.GetProcessList();
auto process = SearchProcessList(system.Kernel(),
list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
+2 -2
View File
@@ -76,7 +76,7 @@ private:
Type, process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
process_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -98,7 +98,7 @@ private:
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
process_id, user_id);
IPC::ResponseBuilder rb{ctx, 2};
+1 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -166,16 +166,6 @@ Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
R_RETURN(VI::ResultNotFound);
}
Result Container::SetLayerStackMask(u64 layer_id, u32 layer_stack_mask) {
std::scoped_lock lk{m_lock};
auto* const layer = m_layers.GetLayerById(layer_id);
R_UNLESS(layer != nullptr, VI::ResultNotFound);
m_surface_flinger->SetLayerStackMask(layer->GetConsumerBinderId(), layer_stack_mask);
R_SUCCEED();
}
Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) {
std::scoped_lock lk{m_lock};
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -68,7 +68,6 @@ public:
Result SetLayerZIndex(u64 layer_id, s32 z_index);
Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
Result SetLayerIsOverlay(u64 layer_id, bool is_overlay);
Result SetLayerStackMask(u64 layer_id, u32 layer_stack_mask);
void LinkVsyncEvent(u64 display_id, Event* event);
void UnlinkVsyncEvent(u64 display_id, Event* event);
+82 -235
View File
@@ -4,15 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <random>
#include "common/assert.h"
#include "common/logging.h"
#include "common/scratch_buffer.h"
#include "core/core.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
@@ -53,7 +47,7 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
for (; start < end; start++) {
*start = 0x00000000;
*start = 0xFF0000FF;
}
}
@@ -174,14 +168,7 @@ constexpr u32 SharedBufferBlockLinearWidth = 1280;
constexpr u32 SharedBufferBlockLinearHeight = 768;
constexpr u32 SharedBufferBlockLinearStride =
SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
constexpr u32 SharedBufferNumCaptureSlots = 3;
constexpr u32 SharedBufferSlotsPerSession = 2;
constexpr u32 SharedBufferMaxSessions = 2;
constexpr u32 SharedBufferNumSlots =
SharedBufferNumCaptureSlots + SharedBufferSlotsPerSession * SharedBufferMaxSessions;
static_assert(SharedBufferNumSlots <= 16, "Shared buffer pool exceeds the maximum texture count");
constexpr u32 SharedBufferNumSlots = 7;
constexpr u32 SharedBufferWidth = 1280;
constexpr u32 SharedBufferHeight = 720;
@@ -205,52 +192,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
return layout;
}();
constexpr u32 GetCaptureSlot(CaptureKind kind) {
return static_cast<u32>(kind);
}
static_assert(static_cast<u32>(CaptureKind::CallerApplet) + 1 == SharedBufferNumCaptureSlots,
"Capture slot count does not match CaptureKind");
constexpr u32 GetPresentationSlot(u32 slot_base, u32 index) {
return SharedBufferNumCaptureSlots + slot_base + index;
}
constexpr u32 ColorOpaqueBlackRgba32 = 0xFF000000;
template <typename F>
void ForEachPoolChunk(Core::System& system, Kernel::KPageGroup& page_group, u64 offset, u64 size,
F&& writer) {
Common::ScratchBuffer<u32> scratch;
const u64 range_end = offset + size;
u64 pool_pos = 0;
for (auto& block : page_group) {
const u64 block_begin = pool_pos;
const u64 block_end = block_begin + block.GetSize();
pool_pos = block_end;
if (block_end <= offset) {
continue;
}
if (block_begin >= range_end) {
break;
}
const u64 chunk_begin = (std::max)(block_begin, offset);
const u64 chunk_end = (std::min)(block_end, range_end);
const u64 chunk_size = chunk_end - chunk_begin;
u8* const dst =
system.DeviceMemory().GetPointer<u8>(block.GetAddress()) + (chunk_begin - block_begin);
writer(dst, chunk_begin - offset, chunk_size);
system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(
dst, scratch, [&](DAddr addr) { system.GPU().InvalidateRegion(addr, chunk_size); });
}
}
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 producer_slot, u32 pool_slot, u32 handle) {
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
auto buffer = std::make_shared<android::NvGraphicBuffer>();
buffer->width = SharedBufferWidth;
buffer->height = SharedBufferHeight;
@@ -258,8 +200,8 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 producer_slot
buffer->format = SharedBufferBlockLinearFormat;
buffer->external_format = SharedBufferBlockLinearFormat;
buffer->buffer_id = handle;
buffer->offset = pool_slot * SharedBufferSlotSize;
ASSERT(producer.SetPreallocatedBuffer(producer_slot, buffer) == android::Status::NoError);
buffer->offset = slot * SharedBufferSlotSize;
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
}
} // namespace
@@ -273,91 +215,59 @@ SharedBufferManager::~SharedBufferManager() = default;
Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
u64* out_layer_handle, u64 display_id,
bool enable_blending) {
{
std::scoped_lock lk{m_guard};
std::scoped_lock lk{m_guard};
// Ensure we haven't already created.
const u64 aruid = owner_process->GetProcessId();
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
// Ensure we haven't already created.
const u64 aruid = owner_process->GetProcessId();
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
// Allocate memory for the shared buffer if needed.
if (!m_buffer_page_group) {
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
SharedBufferSize));
// Allocate memory for the shared buffer if needed.
if (!m_buffer_page_group) {
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
SharedBufferSize));
// Record buffer id.
m_buffer_id = m_next_buffer_id++;
// Record buffer id.
m_buffer_id = m_next_buffer_id++;
// Record display id.
m_display_id = display_id;
for (u32 slot = 0; slot < SharedBufferNumCaptureSlots; slot++) {
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize,
SharedBufferSlotSize, [](u8* dst, u64, u64 length) {
std::fill_n(reinterpret_cast<u32*>(dst), length / sizeof(u32),
ColorOpaqueBlackRgba32);
});
}
}
// Claim a presentation slot range.
u32 slot_base = 0;
std::array<bool, SharedBufferMaxSessions> in_use{};
for (const auto& [existing_aruid, existing] : m_sessions) {
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
if (index < in_use.size())
in_use[index] = true;
}
u32 index = 0;
while (index < in_use.size() && in_use[index])
index++;
if (index >= in_use.size()) {
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
R_THROW(VI::ResultOperationFailed);
}
slot_base = index * SharedBufferSlotsPerSession;
// 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, SharedBufferSession{});
auto& session = it->second;
session.presentation_slot_base = slot_base;
auto& container = m_nvdrv->GetContainer();
session.session_id = container.OpenSession(owner_process);
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
// Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
session.nvmap_fd, map_address, SharedBufferSize));
// Create and open a layer for the display.
s32 producer_binder_id;
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
std::addressof(session.layer_id), display_id));
// Configure blending and z-index
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
// Get the producer and set preallocated buffers.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
for (u32 i = 0; i < SharedBufferSlotsPerSession; i++)
MakeGraphicBuffer(*producer, i, GetPresentationSlot(session.presentation_slot_base, i), session.buffer_nvmap_handle);
// Assign outputs.
*out_buffer_id = m_buffer_id;
*out_layer_handle = session.layer_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, SharedBufferSession{});
auto& session = it->second;
auto& container = m_nvdrv->GetContainer();
session.session_id = container.OpenSession(owner_process);
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
// Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
session.nvmap_fd, map_address, SharedBufferSize));
// Create and open a layer for the display.
s32 producer_binder_id;
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
std::addressof(session.layer_id), display_id));
// Configure blending and z-index
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
// Get the producer and set preallocated buffers.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);
// Assign outputs.
*out_buffer_id = m_buffer_id;
*out_layer_handle = session.layer_id;
// We succeeded.
R_SUCCEED();
}
@@ -426,51 +336,14 @@ Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
VI::ResultOperationFailed);
out_slot_indexes.fill(-1);
{
std::scoped_lock lk{m_guard};
const auto* const session = this->FindSessionByLayerIdLocked(layer_id);
if (session == nullptr) {
producer->CancelBuffer(slot, *out_fence);
// LOG_DEBUG(Service_VI, "No Session found");
R_THROW(VI::ResultNotFound);
}
for (u32 i = 0; i < SharedBufferSlotsPerSession; i++)
out_slot_indexes[i] =
static_cast<s32>(GetPresentationSlot(session->presentation_slot_base, i));
*out_target_slot = static_cast<s64>(
GetPresentationSlot(session->presentation_slot_base, static_cast<u32>(slot)));
}
// Assign remaining outputs.
*out_target_slot = slot;
out_slot_indexes = {0, 1, -1, -1};
// We succeeded.
R_SUCCEED();
}
Result SharedBufferManager::GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id,
s64 pool_slot) const {
const auto* const session = this->FindSessionByLayerIdLocked(layer_id);
R_UNLESS(session != nullptr, VI::ResultNotFound);
const s64 base = GetPresentationSlot(session->presentation_slot_base, 0);
const s64 producer_slot = pool_slot - base;
R_UNLESS(producer_slot >= 0 && producer_slot < SharedBufferSlotsPerSession,
VI::ResultOperationFailed);
*out_producer_slot = static_cast<s32>(producer_slot);
R_SUCCEED();
}
const SharedBufferSession* SharedBufferManager::FindSessionByLayerIdLocked(u64 layer_id) const {
for (const auto& [aruid, session] : m_sessions)
if (session.layer_id == layer_id)
return std::addressof(session);
return nullptr;
}
Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
Common::Rectangle<s32> crop_region,
u32 transform, s32 swap_interval, u64 layer_id,
@@ -479,20 +352,14 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
s32 producer_slot;
{
std::scoped_lock lk{m_guard};
R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot));
}
// Request to queue the buffer.
std::shared_ptr<android::GraphicBuffer> buffer;
R_UNLESS(producer->RequestBuffer(producer_slot, std::addressof(buffer)) ==
R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
android::Status::NoError,
VI::ResultOperationFailed);
ON_RESULT_FAILURE {
producer->CancelBuffer(producer_slot, fence);
producer->CancelBuffer(static_cast<s32>(slot), fence);
};
// Queue the buffer to the producer.
@@ -502,10 +369,12 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
input.fence = fence;
input.transform = static_cast<android::NativeWindowTransform>(transform);
input.swap_interval = swap_interval;
R_UNLESS(producer->QueueBuffer(producer_slot, input, std::addressof(output)) ==
R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
android::Status::NoError,
VI::ResultOperationFailed);
(void)m_container.SetLayerZIndex(layer_id, 100000);
// We succeeded.
R_SUCCEED();
}
@@ -515,14 +384,8 @@ Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
s32 producer_slot;
{
std::scoped_lock lk{m_guard};
R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot));
}
// Cancel.
producer->CancelBuffer(producer_slot, android::Fence::NoFence());
producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());
// We succeeded.
R_SUCCEED();
@@ -541,47 +404,31 @@ Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadabl
R_SUCCEED();
}
Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind) {
std::scoped_lock lk{m_guard};
R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound);
Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
Common::ScratchBuffer<u32> scratch;
const std::vector<u8> capture = m_system.GPU().GetAppletCaptureBuffer();
const u32 slot = GetCaptureSlot(kind);
// 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());
if (capture.size() < SharedBufferSlotSize) {
//LOG_WARNING(Service_VI, "Capture buffer is {} bytes, expected at least {}; not writing",
// capture.size(), SharedBufferSlotSize);
*out_was_written = false;
*out_layer_index = static_cast<s32>(slot);
R_SUCCEED();
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);
});
}
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize,
SharedBufferSlotSize,
[&](u8* dst, u64 src_offset, u64 length) {
std::memcpy(dst, capture.data() + src_offset, length);
});
*out_was_written = true;
*out_layer_index = static_cast<s32>(slot);
R_SUCCEED();
}
Result SharedBufferManager::ClearAppletCaptureBuffer(s32 layer_index, u32 color) {
std::scoped_lock lk{m_guard};
R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound);
if (layer_index < 0 || layer_index >= static_cast<s32>(SharedBufferNumCaptureSlots)) {
LOG_WARNING(Service_VI, "Couldnt clear non-capture slot {}", layer_index);
R_SUCCEED();
}
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{static_cast<u32>(layer_index)} * SharedBufferSlotSize,
SharedBufferSlotSize, [&](u8* dst, u64 src_offset, u64 length) {
ASSERT(src_offset % sizeof(u32) == 0 && length % sizeof(u32) == 0);
std::fill_n(reinterpret_cast<u32*>(dst), length / sizeof(u32), color);
});
*out_layer_index = 1;
R_SUCCEED();
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -48,12 +48,6 @@ static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout h
struct SharedBufferSession;
enum class CaptureKind : u32 {
LastApplication,
LastForeground,
CallerApplet,
};
class SharedBufferManager final {
public:
explicit SharedBufferManager(Core::System& system, Container& container,
@@ -74,15 +68,9 @@ public:
Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind);
Result ClearAppletCaptureBuffer(s32 layer_index, u32 color);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
private:
const SharedBufferSession* FindSessionByLayerIdLocked(u64 layer_id) const;
/// Converts a pool slot index, which is what the guest works in, back to the buffer queues slot index
Result GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id, s64 pool_slot) const;
u64 m_next_buffer_id = 1;
u64 m_display_id = 0;
u64 m_buffer_id = 0;
@@ -101,7 +89,6 @@ struct SharedBufferSession {
Nvidia::NvCore::SessionId session_id = {};
u64 layer_id = {};
u32 buffer_nvmap_handle = 0;
u32 presentation_slot_base = 0;
};
} // namespace Service::VI
+1 -4
View File
@@ -209,10 +209,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
// TODO(Maufeat): Check if there is a better way to check
if (name == "main")
system.SetApplicationProcessBuildID(nso_header.build_id);
system.SetApplicationProcessBuildID(nso_header.build_id);
const auto cheats = pm->CreateCheatList(nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
+9 -28
View File
@@ -51,41 +51,24 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
StandardVmCallbacks::~StandardVmCallbacks() = default;
Kernel::KProcess* StandardVmCallbacks::GetProcess() const {
if (cached_process != nullptr && cached_process_id == metadata.process_id) {
return cached_process;
}
auto process = system.Kernel().GetProcessByProcessId(metadata.process_id);
cached_process = process.IsNull() ? nullptr : process.GetPointerUnsafe();
cached_process_id = metadata.process_id;
return cached_process;
}
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
auto* const process = this->GetProcess();
// Return zero on invalid address
if (process == nullptr || !IsAddressInRange(address) ||
!process->GetMemory().IsValidVirtualAddress(address)) {
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
process->GetMemory().ReadBlock(address, data, size);
system.ApplicationMemory().ReadBlock(address, data, size);
}
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
auto* const process = this->GetProcess();
// Skip invalid memory write address
if (process == nullptr || !IsAddressInRange(address) ||
!process->GetMemory().IsValidVirtualAddress(address)) {
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
if (process->GetMemory().WriteBlock(address, data, size)) {
Core::InvalidateInstructionCacheRange(process, address, size);
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
}
@@ -108,16 +91,14 @@ u64 StandardVmCallbacks::HidKeysDown() {
}
void StandardVmCallbacks::PauseProcess() {
auto* const process = this->GetProcess();
if (process != nullptr && !process->IsSuspended()) {
process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused);
if (!system.ApplicationProcess()->IsSuspended()) {
system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused);
}
}
void StandardVmCallbacks::ResumeProcess() {
auto* const process = this->GetProcess();
if (process != nullptr && process->IsSuspended()) {
process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable);
if (system.ApplicationProcess()->IsSuspended()) {
system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable);
}
}
-11
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,10 +15,6 @@ namespace Core {
class System;
}
namespace Kernel {
class KProcess;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
@@ -45,12 +38,8 @@ public:
private:
bool IsAddressInRange(VAddr address) const;
Kernel::KProcess* GetProcess() const;
const CheatProcessMetadata& metadata;
Core::System& system;
mutable Kernel::KProcess* cached_process{};
mutable u64 cached_process_id{};
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
+1 -1
View File
@@ -239,7 +239,7 @@ void Reporter::SaveUnimplementedFunctionReport(Service::HLERequestContext& ctx,
const auto title_id = system.GetApplicationProcessProgramID();
auto out = GetFullDataAuto(timestamp, title_id, system);
auto function_out = GetHLERequestContextData(ctx, ctx.GetMemory());
auto function_out = GetHLERequestContextData(ctx, system.ApplicationMemory());
function_out["command_id"] = command_id;
function_out["function_name"] = name;
function_out["service_name"] = service_name;
+1 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -65,9 +62,8 @@ void HidbusBase::DisablePollingMode() {
polling_mode_enabled = false;
}
void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) {
void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem;
transfer_memory_owner = owner;
}
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
+1 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -17,7 +14,6 @@ class System;
namespace Kernel {
class KEvent;
class KProcess;
class KReadableEvent;
} // namespace Kernel
@@ -142,7 +138,7 @@ public:
void DisablePollingMode();
// Called on EnableJoyPollingReceiveMode
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
@@ -179,7 +175,6 @@ protected:
ButtonOnlyPollingDataAccessor button_only_data{};
Common::ProcessAddress transfer_memory{};
Kernel::KProcess* transfer_memory_owner{};
Core::System& system;
Kernel::KEvent* send_command_async_event;
+2 -5
View File
@@ -6,7 +6,6 @@
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/memory.h"
#include "hid_core/frontend/emulated_controller.h"
@@ -68,10 +67,8 @@ void RingController::OnUpdate() {
curr_entry.polling_data.out_size = sizeof(ringcon_value);
std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
if (transfer_memory_owner != nullptr) {
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
sizeof(enable_sixaxis_data));
}
system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
sizeof(enable_sixaxis_data));
break;
}
default:
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
@@ -49,12 +48,10 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
if (transfer_memory == 0 || transfer_memory_owner == nullptr) {
if (transfer_memory == 0) {
return;
}
auto& memory = transfer_memory_owner->GetMemory();
const auto& camera_data = npad_device->GetCamera();
// This indicates how much ambient light is present
@@ -64,14 +61,16 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
if (camera_data.format != current_config.origin_format) {
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
current_config.origin_format);
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
system.ApplicationMemory().ZeroBlock(transfer_memory,
GetDataSize(current_config.trimming_format));
return;
}
if (current_config.origin_format > current_config.trimming_format) {
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
current_config.origin_format, current_config.trimming_format);
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
system.ApplicationMemory().ZeroBlock(transfer_memory,
GetDataSize(current_config.trimming_format));
return;
}
@@ -88,7 +87,8 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
current_config.trimming_start_x, current_config.trimming_start_y,
trimming_width, trimming_height, origin_width, origin_height);
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
system.ApplicationMemory().ZeroBlock(transfer_memory,
GetDataSize(current_config.trimming_format));
return;
}
@@ -102,8 +102,8 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
}
}
memory.WriteBlock(transfer_memory, window_data.data(),
GetDataSize(current_config.trimming_format));
system.ApplicationMemory().WriteBlock(transfer_memory, window_data.data(),
GetDataSize(current_config.trimming_format));
if (!IsProcessorActive()) {
StartProcessor();
@@ -143,19 +143,14 @@ void ImageTransferProcessor::SetConfig(
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem,
Kernel::KProcess* owner) {
void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem;
transfer_memory_owner = owner;
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::span<u8> data) const {
if (transfer_memory_owner == nullptr)
return processor_state;
const auto size = (std::min)(GetDataSize(current_config.trimming_format), data.size());
transfer_memory_owner->GetMemory().ReadBlock(transfer_memory, data.data(), size);
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state;
}
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -12,10 +9,6 @@
#include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h"
namespace Kernel {
class KProcess;
}
namespace Core {
class System;
}
@@ -46,7 +39,7 @@ public:
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
// Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
@@ -82,6 +75,5 @@ private:
Core::System& system;
Common::ProcessAddress transfer_memory{};
Kernel::KProcess* transfer_memory_owner{};
};
} // namespace Service::IRS
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -9,7 +6,6 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/emulated_devices.h"
@@ -28,7 +24,7 @@ void SevenSixAxis::OnInit() {}
void SevenSixAxis::OnRelease() {}
void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || transfer_memory == 0 || transfer_memory_owner == nullptr) {
if (!IsControllerActivated() || transfer_memory == 0) {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
return;
@@ -55,13 +51,12 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
};
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
sizeof(seven_sixaxis_lifo));
system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
sizeof(seven_sixaxis_lifo));
}
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) {
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem;
transfer_memory_owner = owner;
}
void SevenSixAxis::ResetTimestamp() {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -12,10 +9,6 @@
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/ring_lifo.h"
namespace Kernel {
class KProcess;
}
namespace Core {
class System;
} // namespace Core
@@ -40,7 +33,7 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
// Called on ResetSevenSixAxisSensorTimestamp
void ResetTimestamp();
@@ -65,7 +58,6 @@ private:
SevenSixAxisState next_seven_sixaxis_state{};
Common::ProcessAddress transfer_memory{};
Kernel::KProcess* transfer_memory_owner{};
Core::HID::EmulatedConsole* console = nullptr;
Core::System& system;
@@ -548,18 +548,8 @@ void TouchResource::OnTouchUpdate(s64 timestamp) {
}
auto& touch_shared = applet_data->shared_memory_format->touch_screen;
if (applet_data->flag.enable_touchscreen) {
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, true);
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
} else {
TouchScreenState denied{};
denied.sampling_number = current_touch_state.sampling_number;
denied.entry_count = 0;
data.finger_map.finger_count = 0;
data.finger_map.finger_ids = {};
touch_shared.touch_screen_lifo.WriteNextEntry(denied);
}
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, bool(applet_data->flag.enable_touchscreen));
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
}
}
}
+1 -38
View File
@@ -51,18 +51,7 @@ void EmuThread::run() {
QtCommon::system->Run();
m_stopped.Reset();
m_should_run_cv.wait(lk, stop_token, [&] {
return !m_should_run || m_pending_shader_cache_title.has_value();
});
if (m_should_run && m_pending_shader_cache_title.has_value()) {
const u64 program_id = *m_pending_shader_cache_title;
m_pending_shader_cache_title.reset();
lk.unlock();
this->ReloadDiskShaderCache(program_id);
lk.lock();
}
m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
} else {
QtCommon::system->Pause();
m_stopped.Set();
@@ -78,32 +67,6 @@ void EmuThread::run() {
QtCommon::system->ShutdownMainProcess();
}
void EmuThread::ReloadDiskShaderCache(u64 program_id) {
if (!Settings::values.use_disk_shader_cache.GetValue()) {
return;
}
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
auto& system = *QtCommon::system;
auto& gpu = system.GPU();
system.Pause();
gpu.WaitForIdle();
gpu.ObtainContext();
emit ShaderCacheReloadStarted();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, m_stop_source.get_token(),
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
emit ShaderCacheReloadFinished();
gpu.ReleaseContext();
}
// Unlock while emitting signals so that the main thread can
// continue pumping events.
-17
View File
@@ -3,10 +3,7 @@
#pragma once
#include <optional>
#include <QThread>
#include "common/common_types.h"
#include "common/logging.h"
#include "common/thread.h"
@@ -66,19 +63,9 @@ public:
m_stop_source.request_stop();
}
/**
* Requests that the disk shader cache be reloaded for a different title.
*/
void RequestDiskShaderCacheReload(u64 program_id) {
std::unique_lock run_lk{m_should_run_mutex};
m_pending_shader_cache_title = program_id;
m_should_run_cv.notify_one();
}
private:
void EmulationPaused(std::unique_lock<std::mutex>& lk);
void EmulationResumed(std::unique_lock<std::mutex>& lk);
void ReloadDiskShaderCache(u64 program_id);
private:
std::stop_source m_stop_source;
@@ -86,7 +73,6 @@ private:
std::condition_variable_any m_should_run_cv;
Common::Event m_stopped;
bool m_should_run{true};
std::optional<u64> m_pending_shader_cache_title;
signals:
/**
@@ -108,7 +94,4 @@ signals:
void DebugModeLeft();
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
void ShaderCacheReloadStarted();
void ShaderCacheReloadFinished();
};
-24
View File
@@ -1,35 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "common/assert.h"
#include "video_core/framebuffer_config.h"
namespace Tegra {
std::span<const FramebufferConfig> FilterLayerStack(
std::span<const FramebufferConfig> layers, Service::Nvnflinger::LayerStackId stack,
std::vector<FramebufferConfig>& scratch) {
const u32 bit = Service::Nvnflinger::LayerStackBit(stack);
if (std::ranges::all_of(layers,
[bit](const auto& layer) { return (layer.layer_stack_mask & bit) != 0; }))
return layers;
scratch.clear();
for (const auto& layer : layers) {
if ((layer.layer_stack_mask & bit) != 0) {
scratch.push_back(layer);
}
}
return scratch;
}
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
u32 texture_height) {
f32 left, top, right, bottom;
-15
View File
@@ -1,18 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/pixel_format.h"
#include "core/hle/service/nvnflinger/ui/fence.h"
@@ -37,17 +30,9 @@ struct FramebufferConfig {
Service::android::BufferTransformFlags transform_flags{};
Common::Rectangle<int> crop_rect{};
BlendMode blending{};
u32 layer_stack_mask{Service::Nvnflinger::DefaultLayerStackMask};
};
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
u32 texture_height);
/**
* Returns the subset of layers belonging to a stack.
*/
std::span<const FramebufferConfig> FilterLayerStack(std::span<const FramebufferConfig> layers,
Service::Nvnflinger::LayerStackId stack,
std::vector<FramebufferConfig>& scratch);
} // namespace Tegra
-10
View File
@@ -144,12 +144,6 @@ struct GPU::Impl {
sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; });
}
void WaitForIdle() {
const u64 fence = RequestSyncOperation([] {});
gpu_thread.TickGPU(is_async);
WaitForSyncOperation(fence);
}
/// Tick pending requests within the GPU.
void TickWork() {
std::unique_lock lck{sync_request_mutex};
@@ -490,10 +484,6 @@ void GPU::NotifyShutdown() {
impl->NotifyShutdown();
}
void GPU::WaitForIdle() {
impl->WaitForIdle();
}
void GPU::ObtainContext() {
impl->ObtainContext();
}
-2
View File
@@ -168,8 +168,6 @@ public:
void WaitForSyncOperation(u64 fence);
void WaitForIdle();
/// Tick pending requests within the GPU.
void TickWork();
@@ -18,6 +18,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
@@ -30,8 +31,12 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth_stencil.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
@@ -163,6 +168,44 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
endif()
endforeach()
# Integer variants of the MSAA conversion shaders. They only differ from the float
# source in the sampler and output types, so they are generated from it via defines.
set(SHADER_TYPE_VARIANTS
"convert_msaa_to_non_msaa.frag|sint|isampler2DMS|ivec4"
"convert_msaa_to_non_msaa.frag|uint|usampler2DMS|uvec4"
"convert_non_msaa_to_msaa.frag|sint|isampler2D|ivec4"
"convert_non_msaa_to_msaa.frag|uint|usampler2D|uvec4"
)
foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
list(GET VARIANT_PARTS 2 VARIANT_SAMPLER)
list(GET VARIANT_PARTS 3 VARIANT_TEXEL)
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
add_custom_command(
OUTPUT
${VARIANT_HEADER_FILE}
COMMAND
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
-DSAMPLER_TYPE=${VARIANT_SAMPLER} -DTEXEL_TYPE=${VARIANT_TEXEL}
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
--target-env ${SPIR_V_VERSION}
MAIN_DEPENDENCY
${VARIANT_SOURCE}
)
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
endforeach()
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
get_filename_component(FILENAME ${FILEPATH} NAME)
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2D depth_tex;
layout(location = 0) in vec2 texcoord;
void main() {
gl_FragDepth = textureLod(depth_tex, texcoord, 0).r;
}
@@ -8,5 +8,5 @@ layout(binding = 0) uniform sampler2DMS depth_tex;
layout(location = 0) in vec2 texcoord;
void main() {
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
}
@@ -10,6 +10,6 @@ layout(binding = 1) uniform usampler2DMS stencil_tex;
layout(location = 0) in vec2 texcoord;
void main() {
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r);
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r);
}
@@ -3,7 +3,14 @@
#version 450 core
layout(binding = 0) uniform sampler2DMS msaa_in;
#ifndef SAMPLER_TYPE
#define SAMPLER_TYPE sampler2DMS
#endif
#ifndef TEXEL_TYPE
#define TEXEL_TYPE vec4
#endif
layout(binding = 0) uniform SAMPLER_TYPE msaa_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
@@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants {
ivec2 scale;
};
layout(location = 0) out vec4 frag_color;
layout(location = 0) out TEXEL_TYPE frag_color;
void main() {
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2DMS msaa_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
const ivec2 msaa_coord = coord / scale;
const ivec2 sample_offset = coord % scale;
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
gl_FragDepth = texelFetch(msaa_in, msaa_coord, sample_id).r;
}
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2DMS depth_tex;
layout(binding = 1) uniform usampler2DMS stencil_tex;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
const ivec2 msaa_coord = coord / scale;
const ivec2 sample_offset = coord % scale;
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
gl_FragDepth = texelFetch(depth_tex, msaa_coord, sample_id).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, msaa_coord, sample_id).r);
}
@@ -3,7 +3,14 @@
#version 450 core
layout(binding = 0) uniform sampler2D img_in;
#ifndef SAMPLER_TYPE
#define SAMPLER_TYPE sampler2D
#endif
#ifndef TEXEL_TYPE
#define TEXEL_TYPE vec4
#endif
layout(binding = 0) uniform SAMPLER_TYPE img_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
@@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants {
ivec2 scale;
};
layout(location = 0) out vec4 frag_color;
layout(location = 0) out TEXEL_TYPE frag_color;
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2D img_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(img_in, coord, 0).r;
}
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform usampler2D stencil_tex;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
}
+1 -3
View File
@@ -36,8 +36,7 @@ bool RendererBase::IsScreenshotPending() const {
}
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout,
Service::Nvnflinger::LayerStackId layer_stack) {
const Layout::FramebufferLayout& layout) {
if (renderer_settings.screenshot_requested) {
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
return;
@@ -49,7 +48,6 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb
renderer_settings.screenshot_bits = data;
renderer_settings.screenshot_complete_callback = async_callback;
renderer_settings.screenshot_framebuffer_layout = layout;
renderer_settings.screenshot_layer_stack = layer_stack;
renderer_settings.screenshot_requested = true;
}
+2 -8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,7 +26,6 @@ struct RendererSettings {
void* screenshot_bits{};
std::function<void(bool)> screenshot_complete_callback;
Layout::FramebufferLayout screenshot_framebuffer_layout;
Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default};
};
class RendererBase {
@@ -92,11 +88,9 @@ public:
/// Returns true if a screenshot is being processed
bool IsScreenshotPending() const;
/// Request a screenshot of the next frame.
/// Request a screenshot of the next frame
void RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout,
Service::Nvnflinger::LayerStackId layer_stack =
Service::Nvnflinger::LayerStackId::Default);
const Layout::FramebufferLayout& layout);
protected:
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
@@ -75,6 +75,8 @@ public:
void Finish();
void FlushDeferredClear() {}
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
@@ -370,6 +372,7 @@ struct TextureCacheParams {
static constexpr bool HAS_EMULATED_COPIES = true;
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool HAS_MSAA_DOWNLOADS = false;
using Runtime = OpenGL::TextureCacheRuntime;
using Image = OpenGL::Image;
@@ -199,10 +199,7 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
return;
}
const auto screenshot_layers = Tegra::FilterLayerStack(
framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch);
RenderToBuffer(screenshot_layers, renderer_settings.screenshot_framebuffer_layout,
RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
renderer_settings.screenshot_bits);
renderer_settings.screenshot_complete_callback(true);
@@ -211,12 +208,6 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
void RendererOpenGL::RenderAppletCaptureLayer(
std::span<const Tegra::FramebufferConfig> framebuffers) {
const auto capture_layers = Tegra::FilterLayerStack(
framebuffers, Service::Nvnflinger::LayerStackId::LastFrame, applet_capture_layers);
if (capture_layers.empty())
return;
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
@@ -226,7 +217,7 @@ void RendererOpenGL::RenderAppletCaptureLayer(
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
capture_renderbuffer.handle);
blit_applet->DrawScreen(capture_layers, VideoCore::Capture::Layout, true);
blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
@@ -61,9 +61,6 @@ private:
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
std::vector<Tegra::FramebufferConfig> applet_capture_layers;
std::vector<Tegra::FramebufferConfig> screenshot_layer_scratch;
Core::Frontend::EmuWindow& emu_window;
Tegra::MaxwellDeviceMemoryManager& device_memory;
Tegra::GPU& gpu;
+555 -237
View File
@@ -12,6 +12,7 @@
#include "common/settings.h"
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
#include "video_core/host_shaders/blit_depth_frag_spv.h"
#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
@@ -21,7 +22,15 @@
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -518,8 +527,19 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
}
}
[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) {
if (!VideoCore::Surface::IsPixelFormatInteger(format)) {
return MSAACopyFormatClass::Float;
}
if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) {
return MSAACopyFormatClass::SignedInteger;
}
return MSAACopyFormatClass::UnsignedInteger;
}
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
VkFormat format, u32 base_level) {
VkFormat format, u32 base_level, u32 base_layer,
VkImageAspectFlags aspect_mask) {
return device.CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
@@ -534,10 +554,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.aspectMask = aspect_mask,
.baseMipLevel = base_level,
.levelCount = 1,
.baseArrayLayer = 0,
.baseArrayLayer = base_layer,
.layerCount = 1,
},
});
@@ -586,12 +606,17 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
one_texture_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
msaa_copy_depth_stencil_pipeline_layout(
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
two_textures_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)),
blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
@@ -609,7 +634,25 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
convert_msaa_to_non_msaa_sint_frag(
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)),
convert_msaa_to_non_msaa_uint_frag(
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)),
convert_msaa_to_non_msaa_depth_frag(
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)),
convert_msaa_to_non_msaa_depth_stencil_frag(
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)),
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_sint_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)),
convert_non_msaa_to_msaa_uint_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_stencil_frag(
device.IsExtShaderStencilExportSupported()
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -624,24 +667,13 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
src_view](vk::CommandBuffer cmdbuf) {
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
VkSampler sampler = *nearest_sampler;
if (is_linear) {
sampler = *linear_sampler;
}
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
FindOrEmplaceColorPipeline(key), sampler,
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
}
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
@@ -670,6 +702,38 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
});
}
void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, const Region2D& dst_region,
const Region2D& src_region, VkPipeline pipeline, VkSampler sampler,
VkImageView src_view, VkImageView src_stencil_view,
bool blit_stencil) {
VkPipelineLayout layout = *one_texture_pipeline_layout;
if (blit_stencil) {
layout = *two_textures_pipeline_layout;
}
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view,
src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) {
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
if (blit_stencil) {
descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view,
src_stencil_view);
} else {
descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
}
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
}
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, const Region2D& dst_region,
const Region2D& src_region) {
@@ -677,24 +741,34 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
.renderpass = dst_framebuffer->RenderPass(),
.samples = dst_framebuffer->Samples(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler,
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
}
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
src_view](vk::CommandBuffer cmdbuf) {
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer,
ImageView& src_image_view, const Region2D& dst_region,
const Region2D& src_region) {
const bool blit_stencil =
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
const BlitMSAAPipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.samples = dst_framebuffer->Samples(),
};
VkImageView src_stencil_view = VK_NULL_HANDLE;
if (blit_stencil) {
src_stencil_view = src_image_view.StencilView();
}
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler,
src_image_view.DepthView(), src_stencil_view, blit_stencil);
}
void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region) {
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler,
src_image_view.DepthView(), VK_NULL_HANDLE, false);
}
void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
@@ -702,36 +776,14 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
const Region2D& src_region) {
const bool resolve_stencil =
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
const VkPipeline pipeline =
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
const VkPipelineLayout layout =
resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view =
resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE;
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view,
src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) {
if (resolve_stencil) {
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
src_stencil_view);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
} else {
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
}
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
VkImageView src_stencil_view = VK_NULL_HANDLE;
if (resolve_stencil) {
src_stencil_view = src_image_view.StencilView();
}
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(),
resolve_stencil),
*nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil);
}
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
@@ -739,35 +791,23 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation) {
if (!device.IsExtShaderStencilExportSupported()) {
return;
}
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
const bool blit_stencil = device.IsExtShaderStencilExportSupported();
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
};
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view = src_image_view.StencilView();
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view,
src_stencil_view, this](vk::CommandBuffer cmdbuf) {
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
src_stencil_view);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
VkPipeline pipeline{};
VkImageView src_stencil_view = VK_NULL_HANDLE;
if (blit_stencil) {
pipeline = FindOrEmplaceDepthStencilPipeline(key);
src_stencil_view = src_image_view.StencilView();
} else {
pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass);
}
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler,
src_image_view.DepthView(), src_stencil_view, blit_stencil);
}
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
@@ -882,19 +922,183 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
scheduler.InvalidateState();
}
void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
VkPipelineLayout layout, VkImage dst_image,
VkFormat dst_vk_format, VkImage src_image,
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
std::span<const VideoCommon::ImageCopy> copies,
const MSAACopyAspectInfo& aspect_info, bool copy_stencil) {
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
msaa_copy_resources.pop_front();
}
const VkSampler sampler = *nearest_sampler;
for (const VideoCommon::ImageCopy& copy : copies) {
const s32 num_layers = (std::min)(copy.src_subresource.num_layers,
copy.dst_subresource.num_layers);
for (s32 layer = 0; layer < num_layers; ++layer) {
const u32 src_level = static_cast<u32>(copy.src_subresource.base_level);
const u32 src_layer = static_cast<u32>(copy.src_subresource.base_layer + layer);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
src_layer, aspect_info.src_view_aspect);
vk::ImageView src_stencil_view;
if (copy_stencil) {
src_stencil_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
src_layer, VK_IMAGE_ASPECT_STENCIL_BIT);
}
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level),
static_cast<u32>(copy.dst_subresource.base_layer + layer),
aspect_info.attachment_aspect);
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
.offset = dst_offset,
.extent = dst_extent,
};
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = renderpass,
.attachmentCount = 1,
.pAttachments = dst_view.address(),
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
.layers = 1,
});
const MSAACopyPushConstants push_constants{
.dst_offset = {dst_offset.x, dst_offset.y},
.src_offset = {copy.src_offset.x, copy.src_offset.y},
.scale = {scale_x, scale_y},
};
VkImageView src_stencil_handle = VK_NULL_HANDLE;
if (copy_stencil) {
src_stencil_handle = *src_stencil_view;
}
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, pipeline, layout, sampler, renderpass,
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
src_stencil_handle, src = src_image, dst = dst_image, render_area,
aspect_info, push_constants](vk::CommandBuffer cmdbuf) {
const VkImageSubresourceRange barrier_range{
.aspectMask = aspect_info.barrier_aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = aspect_info.pre_src_access,
.dstAccessMask = aspect_info.pre_src_dst_access,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src,
.subresourceRange = barrier_range,
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = aspect_info.pre_src_access,
.dstAccessMask = aspect_info.pre_dst_dst_access,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = barrier_range,
},
};
cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0,
nullptr, nullptr, pre_barriers);
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = renderpass,
.framebuffer = framebuffer_handle,
.renderArea = render_area,
.clearValueCount = 0,
.pClearValues = nullptr,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
if (src_stencil_handle != VK_NULL_HANDLE) {
descriptor_set = two_textures_descriptor_allocator.Commit();
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
src_stencil_handle);
} else {
descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
}
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const VkViewport viewport{
.x = static_cast<float>(render_area.offset.x),
.y = static_cast<float>(render_area.offset.y),
.width = static_cast<float>(render_area.extent.width),
.height = static_cast<float>(render_area.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, render_area);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = aspect_info.post_src_access,
.dstAccessMask = aspect_info.post_dst_access,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = barrier_range,
};
cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0,
post_barrier);
});
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_view),
.dst_view = std::move(dst_view),
.framebuffer = std::move(framebuffer),
});
if (copy_stencil) {
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_stencil_view),
.dst_view = vk::ImageView{},
.framebuffer = vk::Framebuffer{},
});
}
}
}
scheduler.InvalidateState();
}
void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies,
bool msaa_to_non_msaa) {
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
msaa_copy_resources.pop_front();
}
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
const s32 scale_x = 1 << samples_x;
const s32 scale_y = 1 << samples_y;
const VkSampleCountFlagBits samples =
msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
if (msaa_to_non_msaa) {
samples = VK_SAMPLE_COUNT_1_BIT;
}
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
renderpass_key.color_formats[0] = dst_format;
@@ -905,149 +1109,35 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = msaa_to_non_msaa,
.format_class = FormatClass(dst_format),
};
const MSAACopyAspectInfo aspect_info{
.src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
.attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
.barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
.pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
.pre_dst_dst_access =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
.pre_dst_stages =
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
.post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
};
const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkFormat src_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
const VkFormat dst_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
for (const VideoCommon::ImageCopy& copy : copies) {
ASSERT(copy.src_subresource.base_layer == 0);
ASSERT(copy.src_subresource.num_layers == 1);
ASSERT(copy.dst_subresource.base_layer == 0);
ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level));
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level));
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
.offset = dst_offset,
.extent = dst_extent,
};
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = renderpass,
.attachmentCount = 1,
.pAttachments = dst_view.address(),
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
.layers = 1,
});
const MSAACopyPushConstants push_constants{
.dst_offset = {dst_offset.x, dst_offset.y},
.src_offset = {copy.src_offset.x, copy.src_offset.y},
.scale = {scale_x, scale_y},
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, pipeline, layout, sampler, renderpass,
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
src = src_image, dst = dst_image, render_area,
push_constants](vk::CommandBuffer cmdbuf) {
constexpr VkImageSubresourceRange color_range{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src,
.subresourceRange = color_range,
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = color_range,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, nullptr, nullptr, pre_barriers);
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = renderpass,
.framebuffer = framebuffer_handle,
.renderArea = render_area,
.clearValueCount = 0,
.pClearValues = nullptr,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const VkViewport viewport{
.x = static_cast<float>(render_area.offset.x),
.y = static_cast<float>(render_area.offset.y),
.width = static_cast<float>(render_area.extent.width),
.height = static_cast<float>(render_area.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, render_area);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = color_range,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, post_barrier);
});
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_view),
.dst_view = std::move(dst_view),
.framebuffer = std::move(framebuffer),
});
}
scheduler.InvalidateState();
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout,
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
aspect_info, false);
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
@@ -1379,6 +1469,87 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
return *blit_msaa_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline(
const BlitMSAAPipelineKey& key, bool blit_stencil) {
auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys;
auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines;
const auto it = std::ranges::find(keys, key);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(key);
const std::array stages =
MakeStages(*full_screen_vert,
blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.rasterizationSamples = key.samples,
.sampleShadingEnable = VK_TRUE,
.minSampleShading = 1.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &multisample_ci,
.pDepthStencilState = blit_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
: &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
return *pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) {
const auto it = std::ranges::find(blit_depth_keys, renderpass);
if (it != blit_depth_keys.end()) {
return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)];
}
blit_depth_keys.push_back(renderpass);
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *one_texture_pipeline_layout,
.renderPass = renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
return *blit_depth_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil) {
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
@@ -1417,15 +1588,83 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
return *pipelines.back();
}
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies,
bool copy_stencil, bool msaa_to_non_msaa) {
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
const s32 scale_x = 1 << samples_x;
const s32 scale_y = 1 << samples_y;
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
if (msaa_to_non_msaa) {
samples = VK_SAMPLE_COUNT_1_BIT;
}
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
renderpass_key.depth_format = dst_format;
renderpass_key.samples = samples;
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
const MSAACopyPipelineKey key{
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = msaa_to_non_msaa,
.format_class = MSAACopyFormatClass::Float,
};
VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
if (VideoCore::Surface::GetFormatType(dst_format) ==
VideoCore::Surface::SurfaceType::DepthStencil) {
attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
VkPipelineLayout layout = *msaa_copy_pipeline_layout;
if (copy_stencil) {
layout = *msaa_copy_depth_stencil_pipeline_layout;
}
const MSAACopyAspectInfo aspect_info{
.src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT,
.attachment_aspect = attachment_aspect,
.barrier_aspect = attachment_aspect,
.pre_src_access =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
.pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
.pre_dst_stages =
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
.post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
.post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
};
const VkFormat src_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
const VkFormat dst_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout,
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
aspect_info, copy_stencil);
}
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
const auto it = std::ranges::find(msaa_copy_keys, key);
if (it != msaa_copy_keys.end()) {
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
}
msaa_copy_keys.push_back(key);
const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
? *convert_msaa_to_non_msaa_frag
: *convert_non_msaa_to_msaa_frag);
VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag
: *convert_non_msaa_to_msaa_frag;
if (key.format_class == MSAACopyFormatClass::SignedInteger) {
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag
: *convert_non_msaa_to_msaa_sint_frag;
} else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) {
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag
: *convert_non_msaa_to_msaa_uint_frag;
}
const std::array stages = MakeStages(*clear_color_vert, frag_module);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -1462,6 +1701,85 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
return *msaa_copy_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil) {
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
const auto it = std::ranges::find(keys, key);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(key);
VkShaderModule frag_module;
if (key.msaa_to_non_msaa) {
frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag
: *convert_msaa_to_non_msaa_depth_frag;
} else {
frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
: *convert_non_msaa_to_msaa_depth_frag;
}
const std::array stages = MakeStages(*clear_color_vert, frag_module);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.rasterizationSamples = key.samples,
.sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE,
.minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0xFF,
.writeMask = 0xFF,
.reference = 0,
};
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &multisample_ci,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
: *msaa_copy_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
return *pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
}
@@ -45,12 +45,19 @@ struct BlitDepthStencilPipelineKey {
u32 stencil_ref;
};
enum class MSAACopyFormatClass : u32 {
Float,
SignedInteger,
UnsignedInteger,
};
struct MSAACopyPipelineKey {
constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
VkRenderPass renderpass;
VkSampleCountFlagBits samples;
bool msaa_to_non_msaa;
MSAACopyFormatClass format_class;
};
struct BlitMSAAPipelineKey {
@@ -78,6 +85,12 @@ public:
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
void BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region);
@@ -116,7 +129,39 @@ public:
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil,
bool msaa_to_non_msaa);
private:
struct MSAACopyAspectInfo {
VkImageAspectFlags src_view_aspect;
VkImageAspectFlags attachment_aspect;
VkImageAspectFlags barrier_aspect;
VkAccessFlags pre_src_access;
VkAccessFlags pre_src_dst_access;
VkAccessFlags pre_dst_dst_access;
VkPipelineStageFlags pre_src_stages;
VkPipelineStageFlags pre_dst_stages;
VkAccessFlags post_src_access;
VkAccessFlags post_dst_access;
VkPipelineStageFlags post_src_stages;
VkPipelineStageFlags post_dst_stages;
};
void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline,
VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view,
bool blit_stencil);
void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout,
VkImage dst_image, VkFormat dst_vk_format, VkImage src_image,
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
std::span<const VideoCommon::ImageCopy> copies,
const MSAACopyAspectInfo& aspect_info, bool copy_stencil);
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -131,7 +176,13 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline(
const BlitMSAAPipelineKey& key, bool blit_stencil);
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil);
@@ -162,10 +213,12 @@ private:
vk::PipelineLayout two_textures_pipeline_layout;
vk::PipelineLayout clear_color_pipeline_layout;
vk::PipelineLayout msaa_copy_pipeline_layout;
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag;
vk::ShaderModule blit_color_msaa_frag;
vk::ShaderModule blit_depth_stencil_frag;
vk::ShaderModule blit_depth_frag;
vk::ShaderModule blit_depth_msaa_frag;
vk::ShaderModule blit_depth_stencil_msaa_frag;
vk::ShaderModule clear_color_vert;
@@ -179,7 +232,15 @@ private:
vk::ShaderModule convert_d24s8_to_abgr8_frag;
vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::ShaderModule convert_msaa_to_non_msaa_frag;
vk::ShaderModule convert_msaa_to_non_msaa_sint_frag;
vk::ShaderModule convert_msaa_to_non_msaa_uint_frag;
vk::ShaderModule convert_msaa_to_non_msaa_depth_frag;
vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag;
vk::ShaderModule convert_non_msaa_to_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_sint_frag;
vk::ShaderModule convert_non_msaa_to_msaa_uint_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -193,8 +254,18 @@ private:
std::vector<vk::Pipeline> clear_stencil_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
std::vector<vk::Pipeline> msaa_copy_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
std::vector<VkRenderPass> blit_depth_keys;
std::vector<vk::Pipeline> blit_depth_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_keys;
std::vector<vk::Pipeline> blit_msaa_depth_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_stencil_keys;
std::vector<vk::Pipeline> blit_msaa_depth_stencil_pipelines;
std::vector<VkRenderPass> resolve_depth_keys;
std::vector<vk::Pipeline> resolve_depth_pipelines;
std::vector<VkRenderPass> resolve_depth_stencil_keys;
@@ -292,11 +292,8 @@ void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
return;
}
const auto screenshot_layers = Tegra::FilterLayerStack(
framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch);
const auto& layout{renderer_settings.screenshot_framebuffer_layout};
const auto dst_buffer = RenderToBuffer(screenshot_layers, layout, VK_FORMAT_B8G8R8A8_UNORM,
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(),
@@ -335,12 +332,6 @@ std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
void RendererVulkan::RenderAppletCaptureLayer(
std::span<const Tegra::FramebufferConfig> framebuffers) {
const auto capture_layers = Tegra::FilterLayerStack(
framebuffers, Service::Nvnflinger::LayerStackId::LastFrame, applet_capture_layers);
if (capture_layers.empty())
return;
if (!applet_frame.image) {
applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
@@ -349,7 +340,7 @@ void RendererVulkan::RenderAppletCaptureLayer(
}
scheduler.RequestOutsideRenderPassOperationContext();
blit_applet.DrawToFrame(device, rasterizer, &applet_frame, capture_layers, VideoCore::Capture::Layout, 1,
blit_applet.DrawToFrame(device, rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
CaptureFormat);
}
@@ -75,9 +75,6 @@ private:
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
std::vector<Tegra::FramebufferConfig> applet_capture_layers;
std::vector<Tegra::FramebufferConfig> screenshot_layer_scratch;
Tegra::MaxwellDeviceMemoryManager& device_memory;
Tegra::GPU& gpu;
@@ -589,14 +589,6 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (title_id == 0) {
return;
}
if (!pipeline_cache_filename.empty()) {
serialization_thread.WaitForRequests();
if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
}
}
const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir)};
const auto base_dir{shader_dir / fmt::format("{:016x}", title_id)};
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
@@ -424,24 +424,12 @@ void RasterizerVulkan::Clear(u32 layer_count) {
const bool ds_deferrable =
!ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) &&
(!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial);
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor &&
regs.clear_surface.layer == 0 &&
!scheduler.IsRenderPassActive() &&
(!use_color || color_full_channels) && ds_deferrable;
if (!can_defer_clear) {
scheduler.RequestRenderpass(framebuffer);
}
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
up_scale = Settings::values.resolution_info.up_scale;
down_shift = Settings::values.resolution_info.down_shift;
}
UpdateViewportsState(regs);
VkRect2D default_scissor{};
default_scissor.offset.x = 0;
default_scissor.offset.y = 0;
@@ -497,6 +485,22 @@ void RasterizerVulkan::Clear(u32 layer_count) {
return;
}
const bool clear_covers_render_area =
clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 &&
clear_rect.rect.extent.width >= render_area.width &&
clear_rect.rect.extent.height >= render_area.height;
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) &&
regs.clear_surface.layer == 0 &&
!scheduler.IsRenderPassActive() &&
(!use_color || color_full_channels) && ds_deferrable;
if (!can_defer_clear) {
scheduler.RequestRenderpass(framebuffer);
}
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
UpdateViewportsState(regs);
const u32 color_attachment = regs.clear_surface.RT;
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format);
@@ -65,8 +65,72 @@ using VideoCore::Surface::SurfaceType;
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
}
struct ResolveAspects {
bool depth;
bool stencil;
};
struct ResolveModes {
VkResolveModeFlagBits depth;
VkResolveModeFlagBits stencil;
};
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
const SurfaceType surface_type = GetSurfaceType(format);
return ResolveAspects{
.depth = surface_type == SurfaceType::Depth ||
surface_type == SurfaceType::DepthStencil,
.stencil = surface_type == SurfaceType::Stencil ||
surface_type == SurfaceType::DepthStencil,
};
}
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
const ResolveAspects aspects = GetResolveAspects(format);
const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0;
const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0;
ResolveModes modes{
.depth = VK_RESOLVE_MODE_NONE,
.stencil = VK_RESOLVE_MODE_NONE,
};
if (aspects.depth && depth_mode_supported) {
modes.depth = mode;
}
if (aspects.stencil && stencil_mode_supported) {
modes.stencil = mode;
}
if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) {
return modes;
}
if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) {
modes.stencil = mode;
} else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) {
modes.depth = mode;
}
return modes;
}
} // Anonymous namespace
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
return false;
}
const ResolveAspects aspects = GetResolveAspects(depth_format);
if (!aspects.depth && !aspects.stencil) {
return false;
}
const ResolveModes modes = PickResolveModes(device, depth_format);
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
return false;
}
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
}
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
@@ -75,7 +139,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
if (!is_new) {
return *pair->second;
}
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
static constexpr size_t MAX_ATTACHMENTS =
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
std::array<VkAttachmentReference, 8> references{};
u32 num_attachments{};
u32 num_colors{};
@@ -109,8 +175,11 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
? VK_ATTACHMENT_LOAD_OP_CLEAR
: VK_ATTACHMENT_LOAD_OP_LOAD;
const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard
? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE;
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
depth_load_op, VK_ATTACHMENT_STORE_OP_STORE));
depth_load_op, depth_store_op));
}
std::array<VkAttachmentReference, 8> resolve_references{};
const bool do_resolve_color =
@@ -133,6 +202,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
}
}
}
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
key.samples != VK_SAMPLE_COUNT_1_BIT &&
SupportsDepthStencilResolve(*device, key.depth_format);
VkAttachmentReference depth_resolve_reference{};
if (do_resolve_depth_stencil) {
depth_resolve_reference = VkAttachmentReference{
.attachment = static_cast<u32>(descriptions.size()),
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
VkAttachmentDescription resolve_desc =
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
descriptions.push_back(resolve_desc);
}
const VkSubpassDescription subpass{
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -157,6 +241,92 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
};
if (device->IsKhrCreateRenderPass2Supported()) {
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
for (const VkAttachmentDescription& description : descriptions) {
descriptions2.push_back(VkAttachmentDescription2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
.pNext = nullptr,
.flags = description.flags,
.format = description.format,
.samples = description.samples,
.loadOp = description.loadOp,
.storeOp = description.storeOp,
.stencilLoadOp = description.stencilLoadOp,
.stencilStoreOp = description.stencilStoreOp,
.initialLayout = description.initialLayout,
.finalLayout = description.finalLayout,
});
}
const auto promote = [](const VkAttachmentReference& reference) {
return VkAttachmentReference2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.pNext = nullptr,
.attachment = reference.attachment,
.layout = reference.layout,
.aspectMask = 0,
};
};
std::array<VkAttachmentReference2, 8> references2{};
std::array<VkAttachmentReference2, 8> resolve_references2{};
for (size_t index = 0; index < references.size(); ++index) {
references2[index] = promote(references[index]);
resolve_references2[index] = promote(resolve_references[index]);
}
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
.pNext = nullptr,
.depthResolveMode = resolve_modes.depth,
.stencilResolveMode = resolve_modes.stencil,
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
};
const VkSubpassDescription2 subpass2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.viewMask = 0,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = num_attachments,
.pColorAttachments = references2.data(),
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
const VkSubpassDependency2 dependency2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
.pNext = nullptr,
.srcSubpass = dependency.srcSubpass,
.dstSubpass = dependency.dstSubpass,
.srcStageMask = dependency.srcStageMask,
.dstStageMask = dependency.dstStageMask,
.srcAccessMask = dependency.srcAccessMask,
.dstAccessMask = dependency.dstAccessMask,
.dependencyFlags = dependency.dependencyFlags,
.viewOffset = 0,
};
pair->second = device->GetLogical().CreateRenderPass2({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
.pNext = nullptr,
.flags = 0,
.attachmentCount = static_cast<u32>(descriptions2.size()),
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
.subpassCount = 1,
.pSubpasses = &subpass2,
.dependencyCount = 1,
.pDependencies = &dependency2,
.correlatedViewMaskCount = 0,
.pCorrelatedViewMasks = nullptr,
});
return *pair->second;
}
pair->second = device->GetLogical().CreateRenderPass({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr,
@@ -9,6 +9,7 @@
#include <mutex>
#include <ankerl/unordered_dense.h>
#include "common/container_hash.h"
#include "video_core/surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -21,9 +22,11 @@ struct RenderPassKey {
VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples;
bool resolve_color;
bool resolve_depth_stencil;
u32 color_clear_mask;
bool depth_stencil_clear;
u32 color_discard_mask;
bool depth_stencil_discard;
};
} // namespace Vulkan
@@ -31,17 +34,28 @@ struct RenderPassKey {
namespace std {
template <>
struct hash<Vulkan::RenderPassKey> {
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
size_t value = static_cast<size_t>(key.depth_format) << 48;
value ^= static_cast<size_t>(key.samples) << 52;
value ^= static_cast<size_t>(key.resolve_color) << 63;
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
for (size_t i = 0; i < key.color_formats.size(); ++i) {
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
u64 formats = 0;
for (size_t index = 0; index < key.color_formats.size(); ++index) {
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
}
return value;
const u64 state = static_cast<u64>(key.depth_format) |
(static_cast<u64>(key.samples) << 8) |
(static_cast<u64>(key.color_clear_mask) << 16) |
(static_cast<u64>(key.color_discard_mask) << 24) |
(static_cast<u64>(key.resolve_color) << 32) |
(static_cast<u64>(key.depth_stencil_clear) << 33) |
(static_cast<u64>(key.resolve_depth_stencil) << 34) |
(static_cast<u64>(key.depth_stencil_discard) << 35);
size_t seed = 0;
Common::HashCombine(seed, formats);
Common::HashCombine(seed, state);
return seed;
}
};
} // namespace std
@@ -50,6 +64,9 @@ namespace Vulkan {
class Device;
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
VideoCore::Surface::PixelFormat depth_format);
class RenderPassCache {
public:
explicit RenderPassCache(const Device& device_);
@@ -132,6 +132,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass
num_renderpass_images = framebuffer->NumImages();
renderpass_images = framebuffer->Images();
renderpass_image_ranges = framebuffer->ImageRanges();
framebuffer->MarkResolveShadowsUpToDate();
}
void Scheduler::RealizeDeferredClear() {
@@ -155,8 +156,10 @@ void Scheduler::RealizeDeferredClear() {
}
const u32 color_discard_mask =
dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u;
const bool depth_stencil_discard =
dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil();
const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant(
dc.color_clear_mask, dc.depth_stencil, color_discard_mask);
dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard);
EndRenderPass();
BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
}
@@ -190,6 +193,14 @@ bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkC
return true;
}
void Scheduler::FlushDeferredClear() {
if (deferred_clear.framebuffer == nullptr) {
return;
}
RealizeDeferredClear();
EndRenderPass();
}
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
if (deferred_clear.framebuffer == framebuffer) {
RealizeDeferredClear();
@@ -65,6 +65,9 @@ public:
/// Defers a full depth/stencil clear so it becomes the next render pass.
bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
/// Realizes any pending deferred clear before its framebuffer can be moved or freed.
void FlushDeferredClear();
/// Requests the current execution context to be able to execute operations only allowed outside
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
@@ -33,10 +33,10 @@ constexpr VkDeviceSize MAX_ALIGNMENT = 256;
// Windows ones however, can intake bigger buffers and generally do not OOM.
// - GTX 960 on Windows will not OOM with 256mib
// - GT 1030 on ^NIX will OOM with 256mib
#if defined(_WIN32) || defined(__ANDROID__)
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
#else
#if defined(__FreeBSD__)
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
#else
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
#endif
size_t GetStreamBufferSize(const Device& device) {
File diff suppressed because it is too large Load Diff
@@ -60,6 +60,8 @@ public:
void TickFrame();
void FlushDeferredClear();
u64 GetDeviceLocalMemory() const;
u64 GetDeviceMemoryUsage() const;
@@ -68,6 +70,12 @@ public:
std::optional<size_t> GetSamplerHeapBudget() const;
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
void ReleaseMsaaScratchImage(VkImage image);
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
const Region2D& dst_region, const Region2D& src_region,
Tegra::Engines::Fermi2D::Filter filter,
@@ -117,16 +125,20 @@ public:
VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent{};
u32 layers = 0;
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
bool up_to_date = false;
};
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers);
VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask);
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
void InvalidateResolveShadow(VkImage msaa_image);
void MarkResolveShadowUpToDate(VkImage msaa_image);
void EraseResolveShadow(VkImage msaa_image);
std::span<const VkFormat> ViewFormats(PixelFormat format) {
@@ -154,8 +166,30 @@ public:
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
struct MsaaScratchKey {
VkFormat format;
VkImageType type;
u32 width;
u32 height;
u32 depth;
u32 levels;
u32 layers;
VkImageUsageFlags usage;
VkImageCreateFlags flags;
bool operator==(const MsaaScratchKey&) const noexcept = default;
};
struct MsaaScratchImage {
MsaaScratchKey key;
vk::Image image;
u64 tick;
u32 unused_frames;
};
std::vector<MsaaScratchImage> msaa_scratch_images;
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
};
class Framebuffer {
@@ -191,7 +225,8 @@ public:
}
[[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
u32 color_discard_mask) const;
u32 color_discard_mask,
bool depth_stencil_discard) const;
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
return render_area;
@@ -233,19 +268,21 @@ public:
return is_rescaled;
}
[[nodiscard]] bool HasResolveColor() const noexcept {
return !resolve_images.empty();
}
[[nodiscard]] VkImage ResolveColorImage(size_t index) const noexcept {
return index < resolve_images.size() ? *resolve_images[index] : VK_NULL_HANDLE;
}
[[nodiscard]] bool DiscardsMsaaColor() const noexcept {
return discard_msaa_color;
}
[[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept {
return discard_msaa_depth_stencil;
}
/// Records that a render pass has begun, so its resolve attachments will hold valid contents
/// once it ends.
void MarkResolveShadowsUpToDate() const;
private:
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
VkExtent2D render_area{};
@@ -258,11 +295,16 @@ private:
bool has_depth{};
bool has_stencil{};
bool is_rescaled{};
std::vector<vk::Image> resolve_images;
std::vector<vk::ImageView> resolve_image_views;
std::array<VkImage, 9> resolve_shadow_images{};
u32 num_resolve_shadows = 0;
TextureCacheRuntime* runtime_ptr{nullptr};
RenderPassKey render_pass_key{};
RenderPassCache* render_pass_cache{nullptr};
bool discard_msaa_color{};
bool discard_msaa_depth_stencil{};
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
mutable u32 num_memoized_variants{};
};
class Image : public VideoCommon::ImageBase {
@@ -486,6 +528,7 @@ struct TextureCacheParams {
static constexpr bool HAS_EMULATED_COPIES = false;
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool HAS_MSAA_DOWNLOADS = true;
using Runtime = Vulkan::TextureCacheRuntime;
using Image = Vulkan::Image;
+2 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -121,7 +121,7 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie
image_view_ids.push_back(image_view_id);
}
bool ImageBase::IsSafeDownload() const noexcept {
bool ImageBase::IsSafeGpuCopy() const noexcept {
// Skip images that were not modified from the GPU
if (False(flags & ImageFlagBits::GpuModified)) {
return false;
@@ -131,10 +131,6 @@ bool ImageBase::IsSafeDownload() const noexcept {
if (True(flags & ImageFlagBits::CpuModified)) {
return false;
}
if (info.num_samples > 1) {
LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
return false;
}
return true;
}
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -64,7 +67,7 @@ struct ImageBase {
void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id);
[[nodiscard]] bool IsSafeDownload() const noexcept;
[[nodiscard]] bool IsSafeGpuCopy() const noexcept;
[[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
const VAddr overlap_end = overlap_cpu_addr + overlap_size;
+27 -7
View File
@@ -134,7 +134,7 @@ void TextureCache<P>::RunGarbageCollector() {
if (True(image.flags & ImageFlagBits::IsDecoding)) {
return false;
}
const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
return false;
}
@@ -577,6 +577,7 @@ FramebufferId TextureCache<P>::GetFramebufferId(const RenderTargets& key) {
return id ? &slot_image_views[id] : nullptr;
});
ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr;
runtime.FlushDeferredClear();
framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key);
return framebuffer_id;
}
@@ -594,11 +595,26 @@ void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
});
}
template <class P>
bool TextureCache<P>::IsDownloadable(const ImageBase& image) const noexcept {
if (!image.IsSafeGpuCopy()) {
return false;
}
if (image.info.num_samples == 1) {
return true;
}
if constexpr (P::HAS_MSAA_DOWNLOADS) {
return runtime.CanDownloadMsaa(image.info);
} else {
return false;
}
}
template <class P>
void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
boost::container::small_vector<ImageId, 16> images;
ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
if (!image.IsSafeDownload()) {
ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
if (!IsDownloadable(image)) {
return;
}
image.flags &= ~ImageFlagBits::GpuModified;
@@ -1476,12 +1492,12 @@ template <class P>
bool TextureCache<P>::ScaleUp(Image& image) {
const bool has_copy = image.HasScaled();
const bool rescaled = image.ScaleUp();
if (!has_copy && image.HasScaled()) {
total_used_memory += GetScaledImageSizeBytes(image);
}
if (!rescaled) {
return false;
}
if (!has_copy) {
total_used_memory += GetScaledImageSizeBytes(image);
}
InvalidateScale(image);
return true;
}
@@ -1691,7 +1707,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
for (const auto& copy_object : join_copies_to_do) {
Image& overlap = slot_images[copy_object.id];
if (copy_object.is_alias) {
if (!overlap.IsSafeDownload()) {
if (!overlap.IsSafeGpuCopy()) {
continue;
}
if (overlap.info.num_samples != new_image.info.num_samples) {
continue;
}
const auto alias_pointer = join_alias_indices.find(copy_object.id);
@@ -2461,6 +2480,7 @@ void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> rem
template <class P>
void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> removed_views) {
runtime.FlushDeferredClear();
auto it = framebuffers.begin();
while (it != framebuffers.end()) {
if (it->first.Contains(removed_views)) {
@@ -303,6 +303,8 @@ private:
FramebufferId GetFramebufferId(const RenderTargets& key);
/// Refresh the contents (pixel data) of an image
[[nodiscard]] bool IsDownloadable(const ImageBase& image) const noexcept;
void RefreshContents(Image& image, ImageId image_id);
/// Upload data from guest to an image

Some files were not shown because too many files have changed in this diff Show More