mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-27 18:02:16 +00:00
- [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: amicuchu Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> - Fixes Mario Strikers Intro Freezing from legacy. - Microsleep no longer needed for Absolum Video freezing and other videos freezing. - Revert `[service, nvhost] added machinery to allow microsleep between nvdec read requests to avoid guest panic in some games #4316` - Tested with CPU/GPU decoding on Linux/Windows. Special thanks to xbzk <video src="/attachments/9f4a4494-78ee-4b65-ad26-628e83d5c83b" title="strikers" controls></video> Co-authored-by: Your Name <John@John.localdomain> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4525 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
This commit is contained in:
+6
-34
@@ -4,7 +4,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
void SetNVDECActive(bool is_nvdec_active) {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
nvdec_active = is_nvdec_active;
|
||||||
++nvdec_active_channels[process_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
const auto it = nvdec_active_channels.find(process_id);
|
|
||||||
if (it == nvdec_active_channels.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (--it->second == 0) {
|
|
||||||
nvdec_active_channels.erase(it);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
return nvdec_active;
|
||||||
return !nvdec_active_channels.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
return nvdec_active_channels.contains(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -528,8 +509,6 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
std::mutex general_channel_mutex;
|
||||||
std::mutex nvdec_active_mutex;
|
|
||||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
|
||||||
std::atomic_bool is_paused{};
|
std::atomic_bool is_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -537,6 +516,7 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
|
bool nvdec_active : 1 = false;
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -600,22 +580,14 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||||
impl->NotifyNVDECChannelOpen(process_id);
|
impl->SetNVDECActive(is_nvdec_active);
|
||||||
}
|
|
||||||
|
|
||||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
impl->NotifyNVDECChannelClose(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
return impl->IsNVDECActiveForProcess(process_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -191,10 +191,8 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id);
|
void SetNVDECActive(bool is_nvdec_active);
|
||||||
void NotifyNVDECChannelClose(u64 process_id);
|
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
|
#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
|
||||||
@@ -31,6 +35,14 @@ Result IStorage::Read(
|
|||||||
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
|
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
|
||||||
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
|
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
if (now < next_read_tick) {
|
||||||
|
std::this_thread::sleep_for(next_read_tick - now);
|
||||||
|
}
|
||||||
|
next_read_tick = std::chrono::steady_clock::now() +
|
||||||
|
std::chrono::nanoseconds{std::min(length, MaxChargedReadSize) *
|
||||||
|
1'000'000'000 / ReadBytesPerSecond};
|
||||||
|
|
||||||
// Read the data from the Storage backend
|
// Read the data from the Storage backend
|
||||||
backend->Read(out_bytes.data(), length, offset);
|
backend->Read(out_bytes.data(), length, offset);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/service/cmif_types.h"
|
#include "core/hle/service/cmif_types.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
@@ -20,6 +22,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
FileSys::VirtualFile backend;
|
FileSys::VirtualFile backend;
|
||||||
|
|
||||||
|
static constexpr s64 ReadBytesPerSecond = 40'000'000;
|
||||||
|
static constexpr s64 MaxChargedReadSize = 64 * 1024;
|
||||||
|
std::chrono::steady_clock::time_point next_read_tick{};
|
||||||
|
|
||||||
Result Read(
|
Result Read(
|
||||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
|
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
|
||||||
s64 offset, s64 length);
|
s64 offset, s64 length);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
|
||||||
#include "core/hle/service/nvdrv/core/container.h"
|
#include "core/hle/service/nvdrv/core/container.h"
|
||||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||||
@@ -72,23 +71,17 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
|
|
||||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||||
|
system.SetNVDECActive(true);
|
||||||
sessions[fd] = session_id;
|
sessions[fd] = session_id;
|
||||||
if (const auto* session = core.GetSession(session_id);
|
|
||||||
session != nullptr && session->process != nullptr) {
|
|
||||||
system.NotifyNVDECChannelOpen(session->process->GetId());
|
|
||||||
}
|
|
||||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||||
|
system.SetNVDECActive(false);
|
||||||
auto it = sessions.find(fd);
|
auto it = sessions.find(fd);
|
||||||
if (it != sessions.end()) {
|
if (it != sessions.end()) {
|
||||||
if (const auto* session = core.GetSession(it->second);
|
|
||||||
session != nullptr && session->process != nullptr) {
|
|
||||||
system.NotifyNVDECChannelClose(session->process->GetId());
|
|
||||||
}
|
|
||||||
sessions.erase(it);
|
sessions.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
#include <string_view>
|
|
||||||
#include <thread>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
@@ -37,7 +33,6 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
|||||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||||
, system{system_}
|
, system{system_}
|
||||||
, service_name{service_name_}
|
, service_name{service_name_}
|
||||||
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
|
||||||
, handler_invoker{handler_invoker_}
|
, handler_invoker{handler_invoker_}
|
||||||
, max_sessions{max_sessions_}
|
, max_sessions{max_sessions_}
|
||||||
{}
|
{}
|
||||||
@@ -82,22 +77,13 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||||
const auto command = ctx.GetCommand();
|
auto it = handlers.find(ctx.GetCommand());
|
||||||
auto it = handlers.find(command);
|
|
||||||
const bool is_cmd_read = command == 0;
|
|
||||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||||
if (info == nullptr || info->handler_callback == nullptr)
|
if (info == nullptr || info->handler_callback == nullptr)
|
||||||
return ReportUnimplementedFunction(ctx, info);
|
return ReportUnimplementedFunction(ctx, info);
|
||||||
|
|
||||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||||
handler_invoker(this, info->handler_callback, ctx);
|
handler_invoker(this, info->handler_callback, ctx);
|
||||||
|
|
||||||
if (is_i_storage && is_cmd_read) {
|
|
||||||
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
|
||||||
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ protected:
|
|||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// Identifier string used to connect to the service.
|
/// Identifier string used to connect to the service.
|
||||||
const char* service_name;
|
const char* service_name;
|
||||||
/// Whether this is the IStorage service.
|
|
||||||
const bool is_i_storage;
|
|
||||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||||
InvokerFn* handler_invoker;
|
InvokerFn* handler_invoker;
|
||||||
/// Maximum number of concurrent sessions that this service can handle.
|
/// Maximum number of concurrent sessions that this service can handle.
|
||||||
|
|||||||
Reference in New Issue
Block a user