early-access version 4123

This commit is contained in:
pineappleEA 2024-02-08 19:42:31 +01:00
parent fc7fbbdf6e
commit 3308f3bac8
25 changed files with 750 additions and 414 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 4122. This is the source code for early-access 4123.
## Legal Notice ## Legal Notice

View file

@ -548,8 +548,6 @@ add_library(core STATIC
hle/service/es/es.h hle/service/es/es.h
hle/service/eupld/eupld.cpp hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp hle/service/fatal/fatal_p.cpp
@ -676,8 +674,6 @@ add_library(core STATIC
hle/service/mm/mm_u.h hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h hle/service/mnpp/mnpp_app.h
hle/service/mutex.cpp
hle/service/mutex.h
hle/service/ncm/ncm.cpp hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp hle/service/nfc/common/amiibo_crypto.cpp
@ -790,6 +786,15 @@ add_library(core STATIC
hle/service/nvnflinger/window.h hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h hle/service/olsc/olsc.h
hle/service/os/event.cpp
hle/service/os/event.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h hle/service/pcie/pcie.h
hle/service/pctl/pctl.cpp hle/service/pctl/pctl.cpp

View file

@ -9,8 +9,8 @@
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"

View file

@ -7,8 +7,8 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
union Result; union Result;

View file

@ -7,6 +7,7 @@
#include "core/hle/service/glue/time/file_timestamp_worker.h" #include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h" #include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h" #include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/os/multi_wait_utils.h"
#include "core/hle/service/psc/time/common.h" #include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/service_manager.h" #include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h" #include "core/hle/service/psc/time/static.h"
@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker"); Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
enum class EventType {
Exit = 0,
IpmModuleService_GetEvent = 1,
PowerStateChange = 2,
SignalAlarms = 3,
UpdateLocalSystemClock = 4,
UpdateNetworkSystemClock = 5,
UpdateEphemeralSystemClock = 6,
UpdateSteadyClock = 7,
UpdateFileTimestamp = 8,
AutoCorrect = 9,
Max = 10,
};
s32 num_objs{};
std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
const auto AddWaiter{
[&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
// Open a new reference to the object.
synchronization_object->Open();
// Insert into the list.
wait_indices[num_objs] = type;
wait_objs[num_objs++] = synchronization_object;
}};
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
SCOPE_EXIT({ enum class EventType : s32 {
for (s32 i = 0; i < num_objs; i++) { Exit = 0,
wait_objs[i]->Close(); PowerStateChange = 1,
} SignalAlarms = 2,
}); UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
s32 index{};
num_objs = {};
wait_objs = {};
if (m_pm_state_change_handler.m_priority != 0) { if (m_pm_state_change_handler.m_priority != 0) {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); // TODO: gIPmModuleService::GetEvent() 1
// TODO index = WaitAny(m_system.Kernel(),
// AddWaiter(gIPmModuleService::GetEvent(), 1); &m_event->GetReadableEvent(), // 0
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); &m_alarm_worker.GetEvent() // 1
);
} else { } else {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); // TODO: gIPmModuleService::GetEvent() 1
// TODO index = WaitAny(m_system.Kernel(),
// AddWaiter(gIPmModuleService::GetEvent(), 1); &m_event->GetReadableEvent(), // 0
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); &m_alarm_worker.GetEvent(), // 1
AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock); m_local_clock_event, // 3
AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock); m_network_clock_event, // 4
AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock); m_ephemeral_clock_event, // 5
AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); &m_timer_steady_clock->GetReadableEvent(), // 6
AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); &m_timer_file_system->GetReadableEvent(), // 7
AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect); m_standard_user_auto_correct_clock_event // 8
);
} }
s32 out_index{-1}; switch (static_cast<EventType>(index)) {
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
num_objs, -1);
ASSERT(out_index >= 0 && out_index < num_objs);
if (stop_token.stop_requested()) {
return;
}
switch (wait_indices[out_index]) {
case EventType::Exit: case EventType::Exit:
return; return;
case EventType::IpmModuleService_GetEvent:
// TODO
// IPmModuleService::GetEvent()
// clear the event
// Handle power state change event
break;
case EventType::PowerStateChange: case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear(); m_alarm_worker.GetEvent().Clear();
if (m_pm_state_change_handler.m_priority <= 1) { if (m_pm_state_change_handler.m_priority <= 1) {
@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_local_clock_event->Clear(); m_local_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{}; Service::PSC::Time::SystemClockContext context{};
auto res = m_local_clock->GetSystemClockContext(&context); R_ASSERT(m_local_clock->GetSystemClockContext(&context));
ASSERT(res == ResultSuccess);
m_set_sys->SetUserSystemClockContext(context); m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime(); m_file_timestamp_worker.SetFilesystemPosixTime();
} break; break;
}
case EventType::UpdateNetworkSystemClock: { case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear(); m_network_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{}; Service::PSC::Time::SystemClockContext context{};
auto res = m_network_clock->GetSystemClockContext(&context); R_ASSERT(m_network_clock->GetSystemClockContext(&context));
ASSERT(res == ResultSuccess);
m_set_sys->SetNetworkSystemClockContext(context); m_set_sys->SetNetworkSystemClockContext(context);
s64 time{}; s64 time{};
@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
} }
m_file_timestamp_worker.SetFilesystemPosixTime(); m_file_timestamp_worker.SetFilesystemPosixTime();
} break; break;
}
case EventType::UpdateEphemeralSystemClock: { case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear(); m_ephemeral_clock_event->Clear();
@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
if (!g_ig_report_ephemeral_clock_context_set) { if (!g_ig_report_ephemeral_clock_context_set) {
g_ig_report_ephemeral_clock_context_set = true; g_ig_report_ephemeral_clock_context_set = true;
} }
} break; break;
}
case EventType::UpdateSteadyClock: case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear(); m_timer_steady_clock->Clear();
@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_standard_user_auto_correct_clock_event->Clear(); m_standard_user_auto_correct_clock_event->Clear();
bool automatic_correction{}; bool automatic_correction{};
auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction); &automatic_correction));
ASSERT(res == ResultSuccess);
Service::PSC::Time::SteadyClockTimePoint time_point{}; Service::PSC::Time::SteadyClockTimePoint time_point{};
res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point); R_ASSERT(
ASSERT(res == ResultSuccess); m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
} break; break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
break;
} }
} }
} }

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
namespace Service {
Event::Event(KernelHelpers::ServiceContext& ctx) {
m_event = ctx.CreateEvent("Event");
}
Event::~Event() {
m_event->GetReadableEvent().Close();
m_event->Close();
}
void Event::Signal() {
m_event->Signal();
}
void Event::Clear() {
m_event->Clear();
}
Kernel::KReadableEvent* Event::GetHandle() {
return &m_event->GetReadableEvent();
}
} // namespace Service

31
src/core/hle/service/os/event.h Executable file
View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service {
namespace KernelHelpers {
class ServiceContext;
}
class Event {
public:
explicit Event(KernelHelpers::ServiceContext& ctx);
~Event();
void Signal();
void Clear();
Kernel::KReadableEvent* GetHandle();
private:
Kernel::KEvent* m_event;
};
} // namespace Service

View file

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/service/os/multi_wait.h"
namespace Service {
MultiWait::MultiWait() = default;
MultiWait::~MultiWait() = default;
MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) {
return this->TimedWaitImpl(kernel, -1);
}
MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) {
return this->TimedWaitImpl(kernel, 0);
}
MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) {
return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns);
}
MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) {
std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{};
std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{};
s32 out_index = -1;
s32 num_objects = 0;
for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) {
ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax);
holders[num_objects] = std::addressof(*it);
objects[num_objects] = it->GetNativeHandle();
num_objects++;
}
Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(),
num_objects, timeout_tick);
if (out_index == -1) {
return nullptr;
} else {
return holders[out_index];
}
}
void MultiWait::MoveAll(MultiWait* other) {
while (!other->m_wait_list.empty()) {
MultiWaitHolder& holder = other->m_wait_list.front();
holder.UnlinkFromMultiWait();
holder.LinkToMultiWait(this);
}
}
} // namespace Service

View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/os/multi_wait_holder.h"
namespace Kernel {
class KernelCore;
}
namespace Service {
class MultiWait final {
public:
explicit MultiWait();
~MultiWait();
public:
MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel);
MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel);
MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns);
// TODO: SdkReplyAndReceive?
void MoveAll(MultiWait* other);
private:
MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick);
private:
friend class MultiWaitHolder;
using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType;
ListType m_wait_list{};
};
} // namespace Service

View file

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/os/multi_wait.h"
#include "core/hle/service/os/multi_wait_holder.h"
namespace Service {
void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) {
if (m_multi_wait != nullptr) {
UNREACHABLE();
}
m_multi_wait = multi_wait;
m_multi_wait->m_wait_list.push_back(*this);
}
void MultiWaitHolder::UnlinkFromMultiWait() {
if (m_multi_wait) {
m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this));
m_multi_wait = nullptr;
}
}
} // namespace Service

View file

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/intrusive_list.h"
namespace Kernel {
class KSynchronizationObject;
} // namespace Kernel
namespace Service {
class MultiWait;
class MultiWaitHolder {
public:
explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle)
: m_native_handle(native_handle) {}
void LinkToMultiWait(MultiWait* multi_wait);
void UnlinkFromMultiWait();
void SetUserData(uintptr_t user_data) {
m_user_data = user_data;
}
uintptr_t GetUserData() const {
return m_user_data;
}
Kernel::KSynchronizationObject* GetNativeHandle() const {
return m_native_handle;
}
private:
friend class MultiWait;
Common::IntrusiveListNode m_list_node{};
MultiWait* m_multi_wait{};
Kernel::KSynchronizationObject* m_native_handle{};
uintptr_t m_user_data{};
};
} // namespace Service

View file

@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/os/multi_wait.h"
namespace Service {
namespace impl {
class AutoMultiWaitHolder {
private:
MultiWaitHolder m_holder;
public:
template <typename T>
explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) {
m_holder.LinkToMultiWait(multi_wait);
}
~AutoMultiWaitHolder() {
m_holder.UnlinkFromMultiWait();
}
std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result,
int index) {
if (result.first == std::addressof(m_holder)) {
return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index);
} else {
return result;
}
}
};
using WaitAnyFunction = decltype(&MultiWait::WaitAny);
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
int) {
return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1);
}
template <typename T, typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
int index, T&& x, Args&&... args) {
AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x));
return holder.ConvertResult(
WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index);
}
template <typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
MultiWait* multi_wait, WaitAnyFunction func,
Args&&... args) {
return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...);
}
template <typename... Args>
inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
WaitAnyFunction func, Args&&... args) {
MultiWait temp_multi_wait;
return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0,
std::forward<Args>(args)...);
}
class NotBoolButInt {
public:
constexpr NotBoolButInt(int v) : m_value(v) {}
constexpr operator int() const {
return m_value;
}
explicit operator bool() const = delete;
private:
int m_value;
};
} // namespace impl
template <typename... Args>
requires(sizeof...(Args) > 0)
inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait,
Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...);
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second;
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel,
MultiWait* multi_wait, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait,
std::forward<Args>(args)...);
}
template <typename... Args>
requires(sizeof...(Args) > 0)
inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) {
return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second;
}
} // namespace Service

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/service/os/mutex.h"
namespace Service {
Mutex::Mutex(Core::System& system) : m_system(system) {
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
// Register the event.
Kernel::KEvent::Register(system.Kernel(), m_event);
ASSERT(R_SUCCEEDED(m_event->Signal()));
}
Mutex::~Mutex() {
m_event->GetReadableEvent().Close();
m_event->Close();
}
void Mutex::lock() {
// Infinitely retry until we successfully clear the event.
while (R_FAILED(m_event->GetReadableEvent().Reset())) {
s32 index;
Kernel::KSynchronizationObject* obj = &m_event->GetReadableEvent();
// The event was already cleared!
// Wait for it to become signaled again.
ASSERT(R_SUCCEEDED(
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &index, &obj, 1, -1)));
}
// We successfully cleared the event, and now have exclusive ownership.
}
void Mutex::unlock() {
// Unlock.
ASSERT(R_SUCCEEDED(m_event->Signal()));
}
} // namespace Service

31
src/core/hle/service/os/mutex.h Executable file
View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
}
namespace Service {
class Mutex {
public:
explicit Mutex(Core::System& system);
~Mutex();
void lock();
void unlock();
private:
Core::System& m_system;
Kernel::KEvent* m_event{};
};
} // namespace Service

View file

@ -20,50 +20,91 @@
namespace Service { namespace Service {
constexpr size_t MaximumWaitObjects = 0x40; enum class UserDataTag {
enum HandleType {
Port, Port,
Session, Session,
DeferEvent, DeferEvent,
Event,
}; };
ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} { class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> {
public:
explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory)
: MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) {
this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port));
}
~Port() {
this->GetNativeHandle()->Close();
}
SessionRequestHandlerPtr CreateHandler() {
return m_handler_factory();
}
private:
const SessionRequestHandlerFactory m_handler_factory;
};
class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> {
public:
explicit Session(Kernel::KServerSession* server_session,
std::shared_ptr<SessionRequestManager>&& manager)
: MultiWaitHolder(server_session), m_manager(std::move(manager)) {
this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
}
~Session() {
this->GetNativeHandle()->Close();
}
std::shared_ptr<SessionRequestManager>& GetManager() {
return m_manager;
}
std::shared_ptr<HLERequestContext>& GetContext() {
return m_context;
}
private:
std::shared_ptr<SessionRequestManager> m_manager;
std::shared_ptr<HLERequestContext> m_context;
};
ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} {
// Initialize event. // Initialize event.
m_event = Kernel::KEvent::Create(system.Kernel()); m_wakeup_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr); m_wakeup_event->Initialize(nullptr);
// Register event. // Register event.
Kernel::KEvent::Register(system.Kernel(), m_event); Kernel::KEvent::Register(system.Kernel(), m_wakeup_event);
// Link to holder.
m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent()));
m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list));
} }
ServerManager::~ServerManager() { ServerManager::~ServerManager() {
// Signal stop. // Signal stop.
m_stop_source.request_stop(); m_stop_source.request_stop();
m_event->Signal(); m_wakeup_event->Signal();
// Wait for processing to stop. // Wait for processing to stop.
m_stopped.Wait(); m_stopped.Wait();
m_threads.clear(); m_threads.clear();
// Clean up server ports. // Clean up ports.
for (const auto& [port, handler] : m_ports) { for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) {
port->Close(); delete std::addressof(*it);
} }
// Clean up sessions. // Clean up sessions.
for (const auto& [session, manager] : m_sessions) { for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) {
session->Close(); delete std::addressof(*it);
} }
for (const auto& request : m_deferrals) { // Close wakeup event.
request.session->Close(); m_wakeup_event->GetReadableEvent().Close();
} m_wakeup_event->Close();
// Close event.
m_event->GetReadableEvent().Close();
m_event->Close();
if (m_deferral_event) { if (m_deferral_event) {
m_deferral_event->GetReadableEvent().Close(); m_deferral_event->GetReadableEvent().Close();
@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager)); server_manager->m_system.RunServer(std::move(server_manager));
} }
Result ServerManager::RegisterSession(Kernel::KServerSession* session, Result ServerManager::RegisterSession(Kernel::KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager) { std::shared_ptr<SessionRequestManager> manager) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// We are taking ownership of the server session, so don't open it. // We are taking ownership of the server session, so don't open it.
auto* session = new Session(server_session, std::move(manager));
// Begin tracking the server session. // Begin tracking the server session.
{ {
std::scoped_lock ll{m_list_mutex}; std::scoped_lock ll{m_deferred_list_mutex};
m_sessions.emplace(session, std::move(manager)); m_sessions.push_back(*session);
} }
// Signal the wakeup event. // Register to wait on the session.
m_event->Signal(); this->LinkToDeferredList(session);
R_SUCCEED(); R_SUCCEED();
} }
@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session,
Result ServerManager::RegisterNamedService(const std::string& service_name, Result ServerManager::RegisterNamedService(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory, SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) { u32 max_sessions) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// Add the new server to sm: and get the moved server port. // Add the new server to sm: and get the moved server port.
Kernel::KServerPort* server_port{}; Kernel::KServerPort* server_port{};
R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
max_sessions, handler_factory)); max_sessions, handler_factory));
// We are taking ownership of the server port, so don't open it.
auto* server = new Port(server_port, std::move(handler_factory));
// Begin tracking the server port. // Begin tracking the server port.
{ {
std::scoped_lock ll{m_list_mutex}; std::scoped_lock ll{m_deferred_list_mutex};
m_ports.emplace(server_port, std::move(handler_factory)); m_servers.push_back(*server);
} }
// Signal the wakeup event. // Register to wait on the server port.
m_event->Signal(); this->LinkToDeferredList(server);
R_SUCCEED(); R_SUCCEED();
} }
@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
Result ServerManager::ManageNamedPort(const std::string& service_name, Result ServerManager::ManageNamedPort(const std::string& service_name,
SessionRequestHandlerFactory&& handler_factory, SessionRequestHandlerFactory&& handler_factory,
u32 max_sessions) { u32 max_sessions) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
// Create a new port. // Create a new port.
auto* port = Kernel::KPort::Create(m_system.Kernel()); auto* port = Kernel::KPort::Create(m_system.Kernel());
port->Initialize(max_sessions, false, 0); port->Initialize(max_sessions, false, 0);
@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
// Open a new reference to the server port. // Open a new reference to the server port.
port->GetServerPort().Open(); port->GetServerPort().Open();
// Begin tracking the server port. // Transfer ownership into a new port object.
auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory));
// Begin tracking the port.
{ {
std::scoped_lock ll{m_list_mutex}; std::scoped_lock ll{m_deferred_list_mutex};
m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); m_servers.push_back(*server);
} }
// Register to wait on the port.
this->LinkToDeferredList(server);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
// Set the output. // Set the output.
*out_event = m_deferral_event; *out_event = m_deferral_event;
// Register to wait on the event.
m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent()));
m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent));
this->LinkToDeferredList(std::addressof(*m_deferral_holder));
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() {
R_RETURN(this->LoopProcessImpl()); R_RETURN(this->LoopProcessImpl());
} }
void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) {
// Link.
{
std::scoped_lock lk{m_deferred_list_mutex};
holder->LinkToMultiWait(std::addressof(m_deferred_list));
}
// Signal the wakeup event.
m_wakeup_event->Signal();
}
void ServerManager::LinkDeferred() {
std::scoped_lock lk{m_deferred_list_mutex};
m_multi_wait.MoveAll(std::addressof(m_deferred_list));
}
MultiWaitHolder* ServerManager::WaitSignaled() {
// Ensure we are the only thread waiting for this server.
std::scoped_lock lk{m_selection_mutex};
while (true) {
this->LinkDeferred();
// If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) {
return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) {
// Clear and restart if we were woken up.
m_wakeup_event->Clear();
} else {
// Unlink and handle the event.
selected->UnlinkFromMultiWait();
return selected;
}
}
}
Result ServerManager::Process(MultiWaitHolder* holder) {
switch (static_cast<UserDataTag>(holder->GetUserData())) {
case UserDataTag::Session:
R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder)));
case UserDataTag::Port:
R_RETURN(this->OnPortEvent(static_cast<Port*>(holder)));
case UserDataTag::DeferEvent:
R_RETURN(this->OnDeferralEvent());
default:
UNREACHABLE();
}
}
bool ServerManager::WaitAndProcessImpl() {
if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) {
R_ASSERT(this->Process(signaled_holder));
return true;
} else {
return false;
}
}
Result ServerManager::LoopProcessImpl() { Result ServerManager::LoopProcessImpl() {
while (!m_stop_source.stop_requested()) { while (!m_stop_source.stop_requested()) {
R_TRY(this->WaitAndProcessImpl()); this->WaitAndProcessImpl();
} }
R_SUCCEED(); R_SUCCEED();
} }
Result ServerManager::WaitAndProcessImpl() { Result ServerManager::OnPortEvent(Port* server) {
Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj;
HandleType wait_type{};
// Ensure we are the only thread waiting for this server.
std::unique_lock sl{m_serve_mutex};
// If we're done, return before we start waiting.
R_SUCCEED_IF(m_stop_source.stop_requested());
// Wait for a tracked object to become signaled.
{
s32 num_objs{};
std::array<HandleType, MaximumWaitObjects> wait_types{};
std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{};
const auto AddWaiter{
[&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) {
// Open a new reference to the object.
synchronization_object->Open();
// Insert into the list.
wait_types[num_objs] = type;
wait_objs[num_objs++] = synchronization_object;
}};
{
std::scoped_lock ll{m_list_mutex};
// Add all of our ports.
for (const auto& [port, handler] : m_ports) {
AddWaiter(port, HandleType::Port);
}
// Add all of our sessions.
for (const auto& [session, manager] : m_sessions) {
AddWaiter(session, HandleType::Session);
}
}
// Add the deferral wakeup event.
if (m_deferral_event != nullptr) {
AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent);
}
// Add the wakeup event.
AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
// Clean up extra references on exit.
SCOPE_EXIT({
for (s32 i = 0; i < num_objs; i++) {
wait_objs[i]->Close();
}
});
// Wait for a signal.
s32 out_index{-1};
R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
wait_objs.data(), num_objs, -1)) {
R_CATCH(Kernel::ResultSessionClosed) {
// On session closed, index is updated and we don't want to return an error.
}
}
R_END_TRY_CATCH;
ASSERT(out_index >= 0 && out_index < num_objs);
// Set the output index.
wait_obj = wait_objs[out_index];
wait_type = wait_types[out_index];
}
// Process what we just received, temporarily removing the object so it is
// not processed concurrently by another thread.
{
switch (wait_type) {
case HandleType::Port: {
// Port signaled.
auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>();
SessionRequestHandlerFactory handler_factory;
// Remove from tracking.
{
std::scoped_lock ll{m_list_mutex};
ASSERT(m_ports.contains(port));
m_ports.at(port).swap(handler_factory);
m_ports.erase(port);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnPortEvent(port, std::move(handler_factory)));
}
case HandleType::Session: {
// Session signaled.
auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>();
std::shared_ptr<SessionRequestManager> manager;
// Remove from tracking.
{
std::scoped_lock ll{m_list_mutex};
ASSERT(m_sessions.contains(session));
m_sessions.at(session).swap(manager);
m_sessions.erase(session);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnSessionEvent(session, std::move(manager)));
}
case HandleType::DeferEvent: {
// Clear event.
ASSERT(R_SUCCEEDED(m_deferral_event->Clear()));
// Drain the list of deferrals while we process.
std::list<RequestState> deferrals;
{
std::scoped_lock ll{m_list_mutex};
m_deferrals.swap(deferrals);
}
// Allow other threads to serve.
sl.unlock();
// Finish.
R_RETURN(this->OnDeferralEvent(std::move(deferrals)));
}
case HandleType::Event: {
// Clear event and finish.
R_RETURN(m_event->Clear());
}
default: {
UNREACHABLE();
}
}
}
}
Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
SessionRequestHandlerFactory&& handler_factory) {
// Accept a new server session. // Accept a new server session.
Kernel::KServerSession* session = port->AcceptSession(); auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle());
ASSERT(session != nullptr); Kernel::KServerSession* server_session = server_port->AcceptSession();
ASSERT(server_session != nullptr);
// Create the session manager and install the handler. // Create the session manager and install the handler.
auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this);
manager->SetSessionHandler(handler_factory()); manager->SetSessionHandler(server->CreateHandler());
// Track the server session. // Create and register the new session.
{ this->RegisterSession(server_session, std::move(manager));
std::scoped_lock ll{m_list_mutex};
m_ports.emplace(port, std::move(handler_factory));
m_sessions.emplace(session, std::move(manager));
}
// Signal the wakeup event. // Resume tracking the port.
m_event->Signal(); this->LinkToDeferredList(server);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, Result ServerManager::OnSessionEvent(Session* session) {
std::shared_ptr<SessionRequestManager>&& manager) { Result res = ResultSuccess;
Result rc{ResultSuccess};
// Try to receive a message. // Try to receive a message.
std::shared_ptr<HLERequestContext> context; auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
rc = session->ReceiveRequestHLE(&context, manager); res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager());
// If the session has been closed, we're done. // If the session has been closed, we're done.
if (rc == Kernel::ResultSessionClosed) { if (res == Kernel::ResultSessionClosed) {
// Close the session. this->DestroySession(session);
session->Close();
// Finish.
R_SUCCEED(); R_SUCCEED();
} }
ASSERT(R_SUCCEEDED(rc));
RequestState request{ R_ASSERT(res);
.session = session,
.context = std::move(context),
.manager = std::move(manager),
};
// Complete the sync request with deferral handling. // Complete the sync request with deferral handling.
R_RETURN(this->CompleteSyncRequest(std::move(request))); R_RETURN(this->CompleteSyncRequest(session));
} }
Result ServerManager::CompleteSyncRequest(RequestState&& request) { Result ServerManager::CompleteSyncRequest(Session* session) {
Result rc{ResultSuccess}; Result res = ResultSuccess;
Result service_rc{ResultSuccess}; Result service_res = ResultSuccess;
// Mark the request as not deferred. // Mark the request as not deferred.
request.context->SetIsDeferred(false); session->GetContext()->SetIsDeferred(false);
// Complete the request. We have exclusive access to this session. // Complete the request. We have exclusive access to this session.
service_rc = request.manager->CompleteSyncRequest(request.session, *request.context); auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
service_res =
session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext());
// If we've been deferred, we're done. // If we've been deferred, we're done.
if (request.context->GetIsDeferred()) { if (session->GetContext()->GetIsDeferred()) {
// Insert into deferral list. // Insert into deferred session list.
std::scoped_lock ll{m_list_mutex}; std::scoped_lock ll{m_deferred_list_mutex};
m_deferrals.emplace_back(std::move(request)); m_deferred_sessions.push_back(session);
// Finish. // Finish.
R_SUCCEED(); R_SUCCEED();
} }
// Send the reply. // Send the reply.
rc = request.session->SendReplyHLE(); res = server_session->SendReplyHLE();
// If the session has been closed, we're done. // If the session has been closed, we're done.
if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) { if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
// Close the session. this->DestroySession(session);
request.session->Close();
// Finish.
R_SUCCEED(); R_SUCCEED();
} }
ASSERT(R_SUCCEEDED(rc)); R_ASSERT(res);
ASSERT(R_SUCCEEDED(service_rc)); R_ASSERT(service_res);
// Reinsert the session. // We succeeded, so we can process future messages on this session.
{ this->LinkToDeferredList(session);
std::scoped_lock ll{m_list_mutex};
m_sessions.emplace(request.session, std::move(request.manager));
}
// Signal the wakeup event.
m_event->Signal();
// We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) { Result ServerManager::OnDeferralEvent() {
ON_RESULT_FAILURE { // Clear event before grabbing the list.
std::scoped_lock ll{m_list_mutex}; m_deferral_event->Clear();
m_deferrals.splice(m_deferrals.end(), deferrals);
};
while (!deferrals.empty()) { // Get and clear list.
RequestState request = deferrals.front(); const auto deferrals = [&] {
deferrals.pop_front(); std::scoped_lock lk{m_deferred_list_mutex};
return std::move(m_deferred_sessions);
}();
// Try again to complete the request. // Relink deferral event.
R_TRY(this->CompleteSyncRequest(std::move(request))); this->LinkToDeferredList(std::addressof(*m_deferral_holder));
// For each session, try again to complete the request.
for (auto* session : deferrals) {
R_ASSERT(this->CompleteSyncRequest(session));
} }
R_SUCCEED(); R_SUCCEED();
} }
void ServerManager::DestroySession(Session* session) {
// Unlink.
{
std::scoped_lock lk{m_deferred_list_mutex};
m_sessions.erase(m_sessions.iterator_to(*session));
}
// Free the session.
delete session;
}
} // namespace Service } // namespace Service

View file

@ -3,18 +3,17 @@
#pragma once #pragma once
#include <functional>
#include <list> #include <list>
#include <map>
#include <mutex> #include <mutex>
#include <string_view> #include <optional>
#include <vector> #include <vector>
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/hle_ipc.h" #include "core/hle/service/hle_ipc.h"
#include "core/hle/service/mutex.h" #include "core/hle/service/os/multi_wait.h"
#include "core/hle/service/os/mutex.h"
namespace Core { namespace Core {
class System; class System;
@ -24,11 +23,13 @@ namespace Kernel {
class KEvent; class KEvent;
class KServerPort; class KServerPort;
class KServerSession; class KServerSession;
class KSynchronizationObject;
} // namespace Kernel } // namespace Kernel
namespace Service { namespace Service {
class Port;
class Session;
class ServerManager { class ServerManager {
public: public:
explicit ServerManager(Core::System& system); explicit ServerManager(Core::System& system);
@ -52,34 +53,40 @@ public:
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
private: private:
struct RequestState; void LinkToDeferredList(MultiWaitHolder* holder);
void LinkDeferred();
MultiWaitHolder* WaitSignaled();
Result Process(MultiWaitHolder* holder);
bool WaitAndProcessImpl();
Result LoopProcessImpl(); Result LoopProcessImpl();
Result WaitAndProcessImpl();
Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory); Result OnPortEvent(Port* port);
Result OnSessionEvent(Kernel::KServerSession* session, Result OnSessionEvent(Session* session);
std::shared_ptr<SessionRequestManager>&& manager); Result OnDeferralEvent();
Result OnDeferralEvent(std::list<RequestState>&& deferrals); Result CompleteSyncRequest(Session* session);
Result CompleteSyncRequest(RequestState&& state);
private:
void DestroySession(Session* session);
private: private:
Core::System& m_system; Core::System& m_system;
Mutex m_serve_mutex; Mutex m_selection_mutex;
std::mutex m_list_mutex;
// Guest state tracking // Events
std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{}; Kernel::KEvent* m_wakeup_event{};
std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
Kernel::KEvent* m_event{};
Kernel::KEvent* m_deferral_event{}; Kernel::KEvent* m_deferral_event{};
// Deferral tracking // Deferred wait list
struct RequestState { std::mutex m_deferred_list_mutex{};
Kernel::KServerSession* session; MultiWait m_deferred_list{};
std::shared_ptr<HLERequestContext> context;
std::shared_ptr<SessionRequestManager> manager; // Guest state tracking
}; MultiWait m_multi_wait{};
std::list<RequestState> m_deferrals{}; Common::IntrusiveListBaseTraits<Port>::ListType m_servers{};
Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{};
std::list<Session*> m_deferred_sessions{};
std::optional<MultiWaitHolder> m_wakeup_holder{};
std::optional<MultiWaitHolder> m_deferral_holder{};
// Host state tracking // Host state tracking
Common::Event m_stopped{}; Common::Event m_stopped{};

View file

@ -60,10 +60,11 @@ public:
Add(spv::ImageOperandsMask::ConstOffsets, offsets); Add(spv::ImageOperandsMask::ConstOffsets, offsets);
} }
explicit ImageOperands(Id lod, Id ms) { explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) {
if (Sirit::ValidId(lod)) { if (Sirit::ValidId(lod)) {
Add(spv::ImageOperandsMask::Lod, lod); Add(spv::ImageOperandsMask::Lod, lod);
} }
AddOffset(ctx, offset, ImageFetchOffsetAllowed);
if (Sirit::ValidId(ms)) { if (Sirit::ValidId(ms)) {
Add(spv::ImageOperandsMask::Sample, ms); Add(spv::ImageOperandsMask::Sample, ms);
} }
@ -311,37 +312,6 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
return coords; return coords;
} }
} }
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
Id offset) {
if (!Sirit::ValidId(offset)) {
return;
}
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D:
case TextureType::ColorArray1D: {
result_type = ctx.U32[1];
break;
}
case TextureType::Color2D:
case TextureType::Color2DRect:
case TextureType::ColorArray2D: {
result_type = ctx.U32[2];
break;
}
case TextureType::Color3D: {
result_type = ctx.U32[3];
break;
}
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
return;
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
} // Anonymous namespace } // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) { Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@ -524,10 +494,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
operands.Span()); operands.Span());
} }
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, Id ms) { const IR::Value& offset, Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
lod = Id{}; lod = Id{};
} }
@ -535,7 +504,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
// This image is multisampled, lod must be implicit // This image is multisampled, lod must be implicit
lod = Id{}; lod = Id{};
} }
const ImageOperands operands(lod, ms); const ImageOperands operands(ctx, offset, lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
} }

View file

@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2); const IR::Value& offset, const IR::Value& offset2);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref); const IR::Value& offset, const IR::Value& offset2, Id dref);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, Id ms); const IR::Value& offset, Id lod, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
const IR::Value& skip_mips); const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 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 "common/assert.h" #include "common/assert.h"
@ -40,7 +40,8 @@ void Decoder::Decode() {
auto frame_copy = frame; auto frame_copy = frame;
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode interlaced frame for top 0x{:X} bottom 0x{:X}", LOG_ERROR(HW_GPU,
"Nvdec {} dailed to decode interlaced frame for top 0x{:X} bottom 0x{:X}", id,
luma_top, luma_bottom); luma_top, luma_bottom);
} }
@ -55,7 +56,8 @@ void Decoder::Decode() {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets(); auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode progressive frame for luma 0x{:X}", luma_offset); LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma 0x{:X}", id,
luma_offset);
} }
if (UsingDecodeOrder()) { if (UsingDecodeOrder()) {

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 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
#pragma once #pragma once

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include <bit> #include <bit>

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View file

@ -66,12 +66,18 @@ public:
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_presentation_order.find(fd); auto map = m_presentation_order.find(fd);
if (map == m_presentation_order.end()) {
return;
}
map->second.emplace_back(offset, std::move(frame)); map->second.emplace_back(offset, std::move(frame));
} }
void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_decode_order.find(fd); auto map = m_decode_order.find(fd);
if (map == m_decode_order.end()) {
return;
}
map->second.insert_or_assign(offset, std::move(frame)); map->second.insert_or_assign(offset, std::move(frame));
} }
@ -82,12 +88,12 @@ public:
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto present_map = m_presentation_order.find(fd); auto present_map = m_presentation_order.find(fd);
if (present_map->second.size() > 0) { if (present_map != m_presentation_order.end() && present_map->second.size() > 0) {
return GetPresentOrderLocked(fd); return GetPresentOrderLocked(fd);
} }
auto decode_map = m_decode_order.find(fd); auto decode_map = m_decode_order.find(fd);
if (decode_map->second.size() > 0) { if (decode_map != m_decode_order.end() && decode_map->second.size() > 0) {
return GetDecodeOrderLocked(fd, offset); return GetDecodeOrderLocked(fd, offset);
} }
@ -97,7 +103,7 @@ public:
private: private:
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) { std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
auto map = m_presentation_order.find(fd); auto map = m_presentation_order.find(fd);
if (map->second.size() == 0) { if (map == m_presentation_order.end() || map->second.size() == 0) {
return {}; return {};
} }
auto frame = std::move(map->second.front().second); auto frame = std::move(map->second.front().second);
@ -107,6 +113,9 @@ private:
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) { std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
auto map = m_decode_order.find(fd); auto map = m_decode_order.find(fd);
if (map == m_decode_order.end() || map->second.size() == 0) {
return {};
}
auto it = map->second.find(offset); auto it = map->second.find(offset);
if (it == map->second.end()) { if (it == map->second.end()) {
return {}; return {};

View file

@ -24,7 +24,6 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_)
Nvdec::~Nvdec() { Nvdec::~Nvdec() {
LOG_INFO(HW_GPU, "Destroying nvdec {}", id); LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
frame_queue.Close(id);
} }
void Nvdec::ProcessMethod(u32 method, u32 argument) { void Nvdec::ProcessMethod(u32 method, u32 argument) {

View file

@ -100,10 +100,11 @@ Vic::Vic(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_)
Vic::~Vic() { Vic::~Vic() {
LOG_INFO(HW_GPU, "Destroying vic {}", id); LOG_INFO(HW_GPU, "Destroying vic {}", id);
frame_queue.Close(id);
} }
void Vic::ProcessMethod(u32 method, u32 arg) { void Vic::ProcessMethod(u32 method, u32 arg) {
LOG_TRACE(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method)); LOG_TRACE(HW_GPU, "Vic {} method 0x{:X}", id, static_cast<u32>(method));
regs.reg_array[method] = arg; regs.reg_array[method] = arg;
switch (static_cast<Method>(method * sizeof(u32))) { switch (static_cast<Method>(method * sizeof(u32))) {
@ -140,7 +141,7 @@ void Vic::Execute() {
auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); auto frame = frame_queue.GetFrame(nvdec_id, luma_offset);
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Vic failed to get frame with offset 0x{:X}", luma_offset); LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset 0x{:X}", id, luma_offset);
continue; continue;
} }