early-access version 3130
This commit is contained in:
parent
dba1deb4a5
commit
cd9434a867
7 changed files with 165 additions and 54 deletions
|
@ -1,7 +1,7 @@
|
|||
yuzu emulator early access
|
||||
=============
|
||||
|
||||
This is the source code for early-access 3129.
|
||||
This is the source code for early-access 3130.
|
||||
|
||||
## Legal Notice
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,41 +1,133 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/assert.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>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 512 * 1024;
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||
|
||||
VirtualBuffer<u8> stack;
|
||||
VirtualBuffer<u8> rewind_stack;
|
||||
|
||||
std::mutex guard;
|
||||
SpinLock guard{};
|
||||
std::function<void()> entry_point;
|
||||
std::function<void()> rewind_point;
|
||||
void* rewind_parameter{};
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
|
||||
u8* stack_limit{};
|
||||
u8* rewind_stack_limit{};
|
||||
boost::context::detail::fcontext_t context{};
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
LPVOID handle = nullptr;
|
||||
LPVOID rewind_handle = nullptr;
|
||||
#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) {
|
||||
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) {
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->context = transfer.fctx;
|
||||
|
@ -107,22 +199,16 @@ void Fiber::Rewind() {
|
|||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
to.impl->guard.lock();
|
||||
to.impl->previous_fiber = weak_from.lock();
|
||||
|
||||
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
if (from->impl->previous_fiber == nullptr) {
|
||||
ASSERT_MSG(false, "previous_fiber is nullptr!");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
|
@ -132,4 +218,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
|||
return fiber;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace Common
|
|
@ -6,9 +6,11 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
namespace boost::context::detail {
|
||||
struct transfer_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
|
@ -40,7 +42,7 @@ public:
|
|||
|
||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||
/// 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();
|
||||
|
||||
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||
|
@ -50,13 +52,23 @@ public:
|
|||
/// Only call from main thread's fiber
|
||||
void Exit();
|
||||
|
||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
||||
void SetStartParameter(void* new_parameter);
|
||||
|
||||
private:
|
||||
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 Start(boost::context::detail::transfer_t& transfer);
|
||||
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
|
||||
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
|
||||
#endif
|
||||
|
||||
struct FiberImpl;
|
||||
std::unique_ptr<FiberImpl> impl;
|
||||
|
|
|
@ -180,7 +180,7 @@ void CpuManager::ShutdownThread() {
|
|||
auto* thread = kernel.GetCurrentEmuThread();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ void CpuManager::RunThread(std::size_t core) {
|
|||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
Common::Fiber::YieldTo(data.host_context, thread->GetHostContext());
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -116,7 +116,7 @@ void KScheduler::PreemptSingleCore() {
|
|||
auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
|
||||
previous_scheduler.Unload(thread);
|
||||
|
||||
Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
|
||||
Common::Fiber::YieldTo(thread->GetHostContext(), m_switch_fiber);
|
||||
|
||||
GetCurrentThread(kernel).EnableDispatch();
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ void KScheduler::ScheduleImpl() {
|
|||
m_switch_cur_thread = cur_thread;
|
||||
m_switch_highest_priority_thread = highest_priority_thread;
|
||||
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.
|
||||
}
|
||||
|
@ -489,7 +489,7 @@ void KScheduler::ScheduleImplFiber() {
|
|||
Reload(highest_priority_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) {
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
value++;
|
||||
}
|
||||
results[id] = value;
|
||||
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
|
||||
Fiber::YieldTo(work_fibers[id], thread_fibers[id]);
|
||||
}
|
||||
|
||||
void ExecuteThread(u32 id);
|
||||
|
@ -68,7 +68,7 @@ void TestControl1::ExecuteThread(u32 id) {
|
|||
thread_fibers[id] = thread_fiber;
|
||||
work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); });
|
||||
items[id] = rand() % 256;
|
||||
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
|
||||
Fiber::YieldTo(thread_fibers[id], work_fibers[id]);
|
||||
thread_fibers[id]->Exit();
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ public:
|
|||
for (u32 i = 0; i < 12000; i++) {
|
||||
value1 += i;
|
||||
}
|
||||
Fiber::YieldTo(fiber1, *fiber3);
|
||||
Fiber::YieldTo(fiber1, fiber3);
|
||||
const u32 id = thread_ids.Get();
|
||||
assert1 = id == 1;
|
||||
value2 += 5000;
|
||||
Fiber::YieldTo(fiber1, *thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber1, thread_fibers[id]);
|
||||
}
|
||||
|
||||
void DoWork2() {
|
||||
|
@ -117,7 +117,7 @@ public:
|
|||
;
|
||||
value2 = 2000;
|
||||
trap = false;
|
||||
Fiber::YieldTo(fiber2, *fiber1);
|
||||
Fiber::YieldTo(fiber2, fiber1);
|
||||
assert3 = false;
|
||||
}
|
||||
|
||||
|
@ -125,19 +125,19 @@ public:
|
|||
const u32 id = thread_ids.Get();
|
||||
assert2 = id == 0;
|
||||
value1 += 1000;
|
||||
Fiber::YieldTo(fiber3, *thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber3, thread_fibers[id]);
|
||||
}
|
||||
|
||||
void ExecuteThread(u32 id);
|
||||
|
||||
void CallFiber1() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber1);
|
||||
Fiber::YieldTo(thread_fibers[id], fiber1);
|
||||
}
|
||||
|
||||
void CallFiber2() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber2);
|
||||
Fiber::YieldTo(thread_fibers[id], fiber2);
|
||||
}
|
||||
|
||||
void Exit();
|
||||
|
@ -207,23 +207,23 @@ public:
|
|||
|
||||
void DoWork1() {
|
||||
value1 += 1;
|
||||
Fiber::YieldTo(fiber1, *fiber2);
|
||||
Fiber::YieldTo(fiber1, fiber2);
|
||||
const u32 id = thread_ids.Get();
|
||||
value3 += 1;
|
||||
Fiber::YieldTo(fiber1, *thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber1, thread_fibers[id]);
|
||||
}
|
||||
|
||||
void DoWork2() {
|
||||
value2 += 1;
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(fiber2, *thread_fibers[id]);
|
||||
Fiber::YieldTo(fiber2, thread_fibers[id]);
|
||||
}
|
||||
|
||||
void ExecuteThread(u32 id);
|
||||
|
||||
void CallFiber1() {
|
||||
const u32 id = thread_ids.Get();
|
||||
Fiber::YieldTo(thread_fibers[id], *fiber1);
|
||||
Fiber::YieldTo(thread_fibers[id], fiber1);
|
||||
}
|
||||
|
||||
void Exit();
|
||||
|
@ -283,7 +283,7 @@ public:
|
|||
|
||||
void Execute() {
|
||||
thread_fiber = Fiber::ThreadToFiber();
|
||||
Fiber::YieldTo(thread_fiber, *fiber1);
|
||||
Fiber::YieldTo(thread_fiber, fiber1);
|
||||
thread_fiber->Exit();
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ public:
|
|||
fiber1->SetRewindPoint([this] { DoWork(); });
|
||||
if (rewinded) {
|
||||
goal_reached = true;
|
||||
Fiber::YieldTo(fiber1, *thread_fiber);
|
||||
Fiber::YieldTo(fiber1, thread_fiber);
|
||||
}
|
||||
rewinded = true;
|
||||
fiber1->Rewind();
|
||||
|
|
Loading…
Reference in a new issue