early-access version 3130

This commit is contained in:
pineappleEA 2022-11-19 12:44:30 +01:00
parent dba1deb4a5
commit cd9434a867
7 changed files with 165 additions and 54 deletions

View file

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

View file

@ -265,8 +265,20 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
} }
} }
void SinkStream::Stall() {} void SinkStream::Stall() {
if (stalled) {
return;
}
stalled = true;
system.StallProcesses();
}
void SinkStream::Unstall() {} void SinkStream::Unstall() {
if (!stalled) {
return;
}
system.UnstallProcesses();
stalled = false;
}
} // namespace AudioCore::Sink } // namespace AudioCore::Sink

View file

@ -1,41 +1,133 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/assert.h" #include "common/assert.h"
#include "common/fiber.h" #include "common/fiber.h"
#include "common/virtual_buffer.h" #include "common/spin_lock.h"
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
#else
#include <boost/context/detail/fcontext.hpp> #include <boost/context/detail/fcontext.hpp>
#endif
namespace Common { namespace Common {
constexpr std::size_t default_stack_size = 512 * 1024; constexpr std::size_t default_stack_size = 512 * 1024;
struct Fiber::FiberImpl { struct Fiber::FiberImpl {
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {} SpinLock guard{};
VirtualBuffer<u8> stack;
VirtualBuffer<u8> rewind_stack;
std::mutex guard;
std::function<void()> entry_point; std::function<void()> entry_point;
std::function<void()> rewind_point; std::function<void()> rewind_point;
void* rewind_parameter{};
std::shared_ptr<Fiber> previous_fiber; std::shared_ptr<Fiber> previous_fiber;
bool is_thread_fiber{}; bool is_thread_fiber{};
bool released{}; bool released{};
u8* stack_limit{}; #if defined(_WIN32) || defined(WIN32)
u8* rewind_stack_limit{}; LPVOID handle = nullptr;
boost::context::detail::fcontext_t context{}; LPVOID rewind_handle = nullptr;
boost::context::detail::fcontext_t rewind_context{}; #else
alignas(64) std::array<u8, default_stack_size> stack;
alignas(64) std::array<u8, default_stack_size> rewind_stack;
u8* stack_limit;
u8* rewind_stack_limit;
boost::context::detail::fcontext_t context;
boost::context::detail::fcontext_t rewind_context;
#endif
}; };
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) { void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
impl->rewind_point = std::move(rewind_func); impl->rewind_point = std::move(rewind_func);
} }
#if defined(_WIN32) || defined(WIN32)
void Fiber::Start() {
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point();
UNREACHABLE();
}
void Fiber::OnRewind() {
ASSERT(impl->handle != nullptr);
DeleteFiber(impl->handle);
impl->handle = impl->rewind_handle;
impl->rewind_handle = nullptr;
impl->rewind_point();
UNREACHABLE();
}
void Fiber::FiberStartFunc(void* fiber_parameter) {
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->Start();
}
void Fiber::RewindStartFunc(void* fiber_parameter) {
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->OnRewind();
}
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (impl->released) {
return;
}
// Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
impl->guard.unlock();
}
DeleteFiber(impl->handle);
}
void Fiber::Exit() {
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
if (!impl->is_thread_fiber) {
return;
}
ConvertFiberToThread();
impl->guard.unlock();
impl->released = true;
}
void Fiber::Rewind() {
ASSERT(impl->rewind_point);
ASSERT(impl->rewind_handle == nullptr);
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
SwitchToFiber(impl->rewind_handle);
}
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->impl->guard.lock();
to->impl->previous_fiber = from;
SwitchToFiber(to->impl->handle);
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->impl->guard.lock();
fiber->impl->handle = ConvertThreadToFiber(nullptr);
fiber->impl->is_thread_fiber = true;
return fiber;
}
#else
void Fiber::Start(boost::context::detail::transfer_t& transfer) { void Fiber::Start(boost::context::detail::transfer_t& transfer) {
ASSERT(impl->previous_fiber != nullptr); ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->context = transfer.fctx; impl->previous_fiber->impl->context = transfer.fctx;
@ -107,23 +199,17 @@ void Fiber::Rewind() {
boost::context::detail::jump_fcontext(impl->rewind_context, this); boost::context::detail::jump_fcontext(impl->rewind_context, this);
} }
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) { void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
to.impl->guard.lock(); ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
to.impl->previous_fiber = weak_from.lock(); ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->impl->guard.lock();
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to); to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
// "from" might no longer be valid if the thread was killed ASSERT(from->impl->previous_fiber != nullptr);
if (auto from = weak_from.lock()) {
if (from->impl->previous_fiber == nullptr) {
ASSERT_MSG(false, "previous_fiber is nullptr!");
return;
}
from->impl->previous_fiber->impl->context = transfer.fctx; from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock(); from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset(); from->impl->previous_fiber.reset();
} }
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() { std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
@ -132,4 +218,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
return fiber; return fiber;
} }
#endif
} // namespace Common } // namespace Common

View file

@ -6,9 +6,11 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#if !defined(_WIN32) && !defined(WIN32)
namespace boost::context::detail { namespace boost::context::detail {
struct transfer_t; struct transfer_t;
} }
#endif
namespace Common { namespace Common {
@ -40,7 +42,7 @@ public:
/// Yields control from Fiber 'from' to Fiber 'to' /// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber. /// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void()>&& rewind_func); void SetRewindPoint(std::function<void()>&& rewind_func);
@ -50,13 +52,23 @@ public:
/// Only call from main thread's fiber /// Only call from main thread's fiber
void Exit(); void Exit();
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
void SetStartParameter(void* new_parameter);
private: private:
Fiber(); Fiber();
#if defined(_WIN32) || defined(WIN32)
void OnRewind();
void Start();
static void FiberStartFunc(void* fiber_parameter);
static void RewindStartFunc(void* fiber_parameter);
#else
void OnRewind(boost::context::detail::transfer_t& transfer); void OnRewind(boost::context::detail::transfer_t& transfer);
void Start(boost::context::detail::transfer_t& transfer); void Start(boost::context::detail::transfer_t& transfer);
static void FiberStartFunc(boost::context::detail::transfer_t transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer);
static void RewindStartFunc(boost::context::detail::transfer_t transfer); static void RewindStartFunc(boost::context::detail::transfer_t transfer);
#endif
struct FiberImpl; struct FiberImpl;
std::unique_ptr<FiberImpl> impl; std::unique_ptr<FiberImpl> impl;

View file

@ -180,7 +180,7 @@ void CpuManager::ShutdownThread() {
auto* thread = kernel.GetCurrentEmuThread(); auto* thread = kernel.GetCurrentEmuThread();
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0; auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context); Common::Fiber::YieldTo(thread->GetHostContext(), core_data[core].host_context);
UNREACHABLE(); UNREACHABLE();
} }
@ -217,7 +217,7 @@ void CpuManager::RunThread(std::size_t core) {
auto* thread = scheduler.GetSchedulerCurrentThread(); auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread); Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext()); Common::Fiber::YieldTo(data.host_context, thread->GetHostContext());
} }
} // namespace Core } // namespace Core

View file

@ -116,7 +116,7 @@ void KScheduler::PreemptSingleCore() {
auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore()); auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
previous_scheduler.Unload(thread); previous_scheduler.Unload(thread);
Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber); Common::Fiber::YieldTo(thread->GetHostContext(), m_switch_fiber);
GetCurrentThread(kernel).EnableDispatch(); GetCurrentThread(kernel).EnableDispatch();
} }
@ -411,7 +411,7 @@ void KScheduler::ScheduleImpl() {
m_switch_cur_thread = cur_thread; m_switch_cur_thread = cur_thread;
m_switch_highest_priority_thread = highest_priority_thread; m_switch_highest_priority_thread = highest_priority_thread;
m_switch_from_schedule = true; m_switch_from_schedule = true;
Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber); Common::Fiber::YieldTo(cur_thread->host_context, m_switch_fiber);
// Returning from ScheduleImpl occurs after this thread has been scheduled again. // Returning from ScheduleImpl occurs after this thread has been scheduled again.
} }
@ -489,7 +489,7 @@ void KScheduler::ScheduleImplFiber() {
Reload(highest_priority_thread); Reload(highest_priority_thread);
// Reload the host thread. // Reload the host thread.
Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context); Common::Fiber::YieldTo(m_switch_fiber, highest_priority_thread->host_context);
} }
void KScheduler::Unload(KThread* thread) { void KScheduler::Unload(KThread* thread) {

View file

@ -50,7 +50,7 @@ public:
value++; value++;
} }
results[id] = value; results[id] = value;
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); Fiber::YieldTo(work_fibers[id], thread_fibers[id]);
} }
void ExecuteThread(u32 id); void ExecuteThread(u32 id);
@ -68,7 +68,7 @@ void TestControl1::ExecuteThread(u32 id) {
thread_fibers[id] = thread_fiber; thread_fibers[id] = thread_fiber;
work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); }); work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); });
items[id] = rand() % 256; items[id] = rand() % 256;
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); Fiber::YieldTo(thread_fibers[id], work_fibers[id]);
thread_fibers[id]->Exit(); thread_fibers[id]->Exit();
} }
@ -105,11 +105,11 @@ public:
for (u32 i = 0; i < 12000; i++) { for (u32 i = 0; i < 12000; i++) {
value1 += i; value1 += i;
} }
Fiber::YieldTo(fiber1, *fiber3); Fiber::YieldTo(fiber1, fiber3);
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
assert1 = id == 1; assert1 = id == 1;
value2 += 5000; value2 += 5000;
Fiber::YieldTo(fiber1, *thread_fibers[id]); Fiber::YieldTo(fiber1, thread_fibers[id]);
} }
void DoWork2() { void DoWork2() {
@ -117,7 +117,7 @@ public:
; ;
value2 = 2000; value2 = 2000;
trap = false; trap = false;
Fiber::YieldTo(fiber2, *fiber1); Fiber::YieldTo(fiber2, fiber1);
assert3 = false; assert3 = false;
} }
@ -125,19 +125,19 @@ public:
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
assert2 = id == 0; assert2 = id == 0;
value1 += 1000; value1 += 1000;
Fiber::YieldTo(fiber3, *thread_fibers[id]); Fiber::YieldTo(fiber3, thread_fibers[id]);
} }
void ExecuteThread(u32 id); void ExecuteThread(u32 id);
void CallFiber1() { void CallFiber1() {
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], *fiber1); Fiber::YieldTo(thread_fibers[id], fiber1);
} }
void CallFiber2() { void CallFiber2() {
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], *fiber2); Fiber::YieldTo(thread_fibers[id], fiber2);
} }
void Exit(); void Exit();
@ -207,23 +207,23 @@ public:
void DoWork1() { void DoWork1() {
value1 += 1; value1 += 1;
Fiber::YieldTo(fiber1, *fiber2); Fiber::YieldTo(fiber1, fiber2);
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
value3 += 1; value3 += 1;
Fiber::YieldTo(fiber1, *thread_fibers[id]); Fiber::YieldTo(fiber1, thread_fibers[id]);
} }
void DoWork2() { void DoWork2() {
value2 += 1; value2 += 1;
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
Fiber::YieldTo(fiber2, *thread_fibers[id]); Fiber::YieldTo(fiber2, thread_fibers[id]);
} }
void ExecuteThread(u32 id); void ExecuteThread(u32 id);
void CallFiber1() { void CallFiber1() {
const u32 id = thread_ids.Get(); const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], *fiber1); Fiber::YieldTo(thread_fibers[id], fiber1);
} }
void Exit(); void Exit();
@ -283,7 +283,7 @@ public:
void Execute() { void Execute() {
thread_fiber = Fiber::ThreadToFiber(); thread_fiber = Fiber::ThreadToFiber();
Fiber::YieldTo(thread_fiber, *fiber1); Fiber::YieldTo(thread_fiber, fiber1);
thread_fiber->Exit(); thread_fiber->Exit();
} }
@ -291,7 +291,7 @@ public:
fiber1->SetRewindPoint([this] { DoWork(); }); fiber1->SetRewindPoint([this] { DoWork(); });
if (rewinded) { if (rewinded) {
goal_reached = true; goal_reached = true;
Fiber::YieldTo(fiber1, *thread_fiber); Fiber::YieldTo(fiber1, thread_fiber);
} }
rewinded = true; rewinded = true;
fiber1->Rewind(); fiber1->Rewind();