aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/citra_qt/debugger/callstack.cpp3
-rw-r--r--src/common/thread_queue_list.h218
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp6
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h3
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp25
-rw-r--r--src/core/core.cpp19
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp63
-rw-r--r--src/core/hle/kernel/thread.h18
-rw-r--r--src/core/hle/service/gsp_gpu.cpp5
-rw-r--r--src/core/hle/svc.cpp4
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp5
13 files changed, 191 insertions, 184 deletions
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index bcc5d214..274c5ccc 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -38,6 +38,9 @@ void CallstackWidget::OnCPUStepped()
{
ret_addr = Memory::Read32(addr);
call_addr = ret_addr - 4; //get call address???
+
+ if (Memory::GetPointer(call_addr) == nullptr)
+ break;
/* TODO (mattvail) clean me, move to debugger interface */
u32 insn = Memory::Read32(call_addr);
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 4e1c0a21..444abf11 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -4,213 +4,143 @@
#pragma once
+#include <array>
+#include <deque>
+
+#include <boost/range/algorithm_ext/erase.hpp>
+
#include "common/common.h"
namespace Common {
-template<class IdType>
+template<class T, unsigned int N>
struct ThreadQueueList {
- // Number of queues (number of priority levels starting at 0.)
- static const int NUM_QUEUES = 128;
+ // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
+ // (dynamically resizable) circular buffers to remove their overhead when
+ // inserting and popping.
- // Initial number of threads a single queue can handle.
- static const int INITIAL_CAPACITY = 32;
+ typedef unsigned int Priority;
- struct Queue {
- // Next ever-been-used queue (worse priority.)
- Queue *next;
- // First valid item in data.
- int first;
- // One after last valid item in data.
- int end;
- // A too-large array with room on the front and end.
- IdType *data;
- // Size of data array.
- int capacity;
- };
+ // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
+ static const Priority NUM_QUEUES = N;
ThreadQueueList() {
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
-
- ~ThreadQueueList() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
+ first = nullptr;
}
// Only for debugging, returns priority level.
- int contains(const IdType uid) {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data == nullptr)
- continue;
-
- Queue *cur = &queues[i];
- for (int j = cur->first; j < cur->end; ++j)
- {
- if (cur->data[j] == uid)
- return i;
+ Priority contains(const T& uid) {
+ for (Priority i = 0; i < NUM_QUEUES; ++i) {
+ Queue& cur = queues[i];
+ if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
+ return i;
}
}
return -1;
}
- inline IdType pop_first() {
+ T pop_first() {
Queue *cur = first;
- while (cur != invalid())
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
- return 0;
+ return T();
}
- inline IdType pop_first_better(u32 priority) {
+ T pop_first_better(Priority priority) {
Queue *cur = first;
Queue *stop = &queues[priority];
- while (cur < stop)
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur < stop) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- return 0;
+ return T();
}
- inline void push_front(u32 priority, const IdType threadID) {
+ void push_front(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[--cur->first] = threadID;
- if (cur->first == 0)
- rebalance(priority);
+ cur->data.push_front(thread_id);
}
- inline void push_back(u32 priority, const IdType threadID) {
+ void push_back(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[cur->end++] = threadID;
- if (cur->end == cur->capacity)
- rebalance(priority);
+ cur->data.push_back(thread_id);
}
- inline void remove(u32 priority, const IdType threadID) {
+ void remove(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
-
- for (int i = cur->first; i < cur->end; ++i)
- {
- if (cur->data[i] == threadID)
- {
- int remaining = --cur->end - i;
- if (remaining > 0)
- memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType));
- return;
- }
- }
-
- // Wasn't there.
+ boost::remove_erase(cur->data, thread_id);
}
- inline void rotate(u32 priority) {
+ void rotate(Priority priority) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
- if (cur->end - cur->first > 1)
- {
- cur->data[cur->end++] = cur->data[cur->first++];
- if (cur->end == cur->capacity)
- rebalance(priority);
+ if (cur->data.size() > 1) {
+ cur->data.push_back(std::move(cur->data.front()));
+ cur->data.pop_front();
}
}
- inline void clear() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
- memset(queues, 0, sizeof(queues));
- first = invalid();
+ void clear() {
+ queues.fill(Queue());
+ first = nullptr;
}
- inline bool empty(u32 priority) const {
+ bool empty(Priority priority) const {
const Queue *cur = &queues[priority];
- return cur->first == cur->end;
+ return cur->data.empty();
}
- inline void prepare(u32 priority) {
- Queue *cur = &queues[priority];
- if (cur->next == nullptr)
- link(priority, INITIAL_CAPACITY);
+ void prepare(Priority priority) {
+ Queue* cur = &queues[priority];
+ if (cur->next_nonempty == UnlinkedTag())
+ link(priority);
}
private:
- Queue *invalid() const {
- return (Queue *) -1;
+ struct Queue {
+ // Points to the next active priority, skipping over ones that have never been used.
+ Queue* next_nonempty = UnlinkedTag();
+ // Double-ended queue of threads in this priority level
+ std::deque<T> data;
+ };
+
+ /// Special tag used to mark priority levels that have never been used.
+ static Queue* UnlinkedTag() {
+ return reinterpret_cast<Queue*>(1);
}
- void link(u32 priority, int size) {
- //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
-
- if (size <= INITIAL_CAPACITY)
- size = INITIAL_CAPACITY;
- else
- {
- int goal = size;
- size = INITIAL_CAPACITY;
- while (size < goal)
- size *= 2;
- }
+ void link(Priority priority) {
Queue *cur = &queues[priority];
- cur->data = (IdType *) malloc(sizeof(IdType) * size);
- cur->capacity = size;
- cur->first = size / 2;
- cur->end = size / 2;
-
- for (int i = (int) priority - 1; i >= 0; --i)
- {
- if (queues[i].next != nullptr)
- {
- cur->next = queues[i].next;
- queues[i].next = cur;
+
+ for (int i = priority - 1; i >= 0; --i) {
+ if (queues[i].next_nonempty != UnlinkedTag()) {
+ cur->next_nonempty = queues[i].next_nonempty;
+ queues[i].next_nonempty = cur;
return;
}
}
- cur->next = first;
+ cur->next_nonempty = first;
first = cur;
}
- void rebalance(u32 priority) {
- Queue *cur = &queues[priority];
- int size = cur->end - cur->first;
- if (size >= cur->capacity - 2) {
- IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
- if (new_data != nullptr) {
- cur->capacity *= 2;
- cur->data = new_data;
- }
- }
-
- int newFirst = (cur->capacity - size) / 2;
- if (newFirst != cur->first) {
- memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType));
- cur->first = newFirst;
- cur->end = newFirst + size;
- }
- }
-
// The first queue that's ever been used.
- Queue *first;
+ Queue* first;
// The priority level queues of thread ids.
- Queue queues[NUM_QUEUES];
+ std::array<Queue, NUM_QUEUES> queues;
};
} // namespace
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index c779e3fd..31eb879a 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -15,7 +15,7 @@ const static cpu_config_t s_arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
-ARM_DynCom::ARM_DynCom() : ticks(0) {
+ARM_DynCom::ARM_DynCom() {
state = std::unique_ptr<ARMul_State>(new ARMul_State);
ARMul_EmulateInit();
@@ -74,11 +74,11 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
}
u64 ARM_DynCom::GetTicks() const {
- return ticks;
+ // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
+ return CoreTiming::GetTicks();
}
void ARM_DynCom::AddTicks(u64 ticks) {
- this->ticks += ticks;
down_count -= ticks;
if (down_count < 0)
CoreTiming::Advance();
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 7284dcd0..9e102a46 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -89,8 +89,5 @@ public:
void ExecuteInstructions(int num_instructions) override;
private:
-
std::unique_ptr<ARMul_State> state;
- u64 ticks;
-
};
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 426fc647..e3ca02e9 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -5891,16 +5891,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SMULW_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- smlad_inst *inst_cream = (smlad_inst *)inst_base->component;
- int64_t rm = RM;
- int64_t rn = RN;
- if (inst_cream->m)
- rm = BITS(rm, 16, 31);
- else
- rm = BITS(rm, 0, 15);
- int64_t rst = rm * rn;
- RD = BITS(rst, 16, 47);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
+
+ s64 result = (s64)rm * (s64)(s32)RN;
+ RD = BITS(result, 16, 47);
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(smlad_inst));
@@ -6699,10 +6696,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
- const u32 rm = RM;
- const u32 rn = RN;
- const u32 rd_lo = RDLO;
- const u32 rd_hi = RDHI;
+ const u64 rm = RM;
+ const u64 rn = RN;
+ const u64 rd_lo = RDLO;
+ const u64 rd_hi = RDHI;
const u64 result = (rm * rn) + rd_lo + rd_hi;
RDLO = (result & 0xFFFFFFFF);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8ac4481c..ff506d67 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -5,6 +5,7 @@
#include "common/common_types.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/settings.h"
#include "core/arm/disassembler/arm_disasm.h"
@@ -16,14 +17,22 @@
namespace Core {
-static u64 last_ticks = 0; ///< Last CPU ticks
-static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
- g_app_core->Run(tight_loop);
+ // If the current thread is an idle thread, then don't execute instructions,
+ // instead advance to the next event and try to yield to the next thread
+ if (Kernel::IsIdleThread(Kernel::GetCurrentThreadHandle())) {
+ LOG_TRACE(Core_ARM11, "Idling");
+ CoreTiming::Idle();
+ CoreTiming::Advance();
+ HLE::Reschedule(__func__);
+ } else {
+ g_app_core->Run(tight_loop);
+ }
+
HW::Update();
if (HLE::g_reschedule) {
Kernel::Reschedule();
@@ -49,7 +58,6 @@ void Stop() {
int Init() {
LOG_DEBUG(Core, "initialized OK");
- disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -62,13 +70,10 @@ int Init() {
break;
}
- last_ticks = Core::g_app_core->GetTicks();
-
return 0;
}
void Shutdown() {
- delete disasm;
delete g_app_core;
delete g_sys_core;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 084fd03a..391e833c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -126,6 +126,8 @@ bool LoadExec(u32 entry_point) {
// 0x30 is the typical main thread priority I've seen used so far
g_main_thread = Kernel::SetupMainThread(0x30);
+ // Setup the idle thread
+ Kernel::SetupIdleThread();
return true;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 872df2d1..954bd09a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -11,6 +11,7 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
@@ -34,6 +35,7 @@ public:
inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ inline bool IsIdle() const { return idle; }
ResultVal<bool> WaitSynchronization() override {
const bool wait = status != THREADSTATUS_DORMANT;
@@ -69,13 +71,16 @@ public:
std::vector<Handle> waiting_threads;
std::string name;
+
+ /// Whether this thread is intended to never actually be executed, i.e. always idle
+ bool idle = false;
};
// Lists all thread ids that aren't deleted/etc.
static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-static Common::ThreadQueueList<Handle> thread_ready_queue;
+static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue;
static Handle current_thread_handle;
static Thread* current_thread;
@@ -303,6 +308,37 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_addres
GetCurrentThread()->wait_address = wait_address;
}
+/// Event type for the thread wake up event
+static int ThreadWakeupEventType = -1;
+
+/// Callback that will wake up the thread it was scheduled for
+static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
+ Handle handle = static_cast<Handle>(parameter);
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ Kernel::ResumeThreadFromWait(handle);
+}
+
+
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) {
+ // Don't schedule a wakeup if the thread wants to wait forever
+ if (nanoseconds == -1)
+ return;
+
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ u64 microseconds = nanoseconds / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle);
+}
+
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
@@ -444,7 +480,14 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
return RESULT_SUCCESS;
}
-/// Sets up the primary application thread
+Handle SetupIdleThread() {
+ Handle handle;
+ Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0);
+ thread->idle = true;
+ CallThread(thread);
+ return handle;
+}
+
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
@@ -487,14 +530,15 @@ void Reschedule() {
thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
}
}
+}
- // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
- // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
- // switch has occurred. This results in the current thread yielding on a sleep once, and then it
- // will immediately be placed back in the queue for execution.
-
- if (CheckWaitType(prev, WAITTYPE_SLEEP))
- ResumeThreadFromWait(prev->GetHandle());
+bool IsIdleThread(Handle handle) {
+ Thread* thread = g_handle_table.Get<Thread>(handle);
+ if (!thread) {
+ LOG_ERROR(Kernel, "Thread not found %u", handle);
+ return false;
+ }
+ return thread->IsIdle();
}
ResultCode GetThreadId(u32* thread_id, Handle handle) {
@@ -512,6 +556,7 @@ ResultCode GetThreadId(u32* thread_id, Handle handle) {
void ThreadingInit() {
next_thread_id = INITIAL_THREAD_ID;
+ ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 81736a86..58bd85ac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -89,6 +89,13 @@ Handle GetCurrentThreadHandle();
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
/**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param handle The thread handle.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
+ */
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds);
+
+/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
* @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
@@ -105,6 +112,17 @@ ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
ResultCode SetThreadPriority(Handle handle, s32 priority);
+/**
+ * Sets up the idle thread, this is a thread that is intended to never execute instructions,
+ * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
+ * and will try to yield on every call.
+ * @returns The handle of the idle thread
+ */
+Handle SetupIdleThread();
+
+/// Whether the current thread is an idle thread
+bool IsIdleThread(Handle thread);
+
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 0127d4ee..26a43217 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -291,8 +291,11 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
- if (info->is_dirty)
+
+ if (info->is_dirty) {
SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
+ }
info->is_dirty = false;
}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 0cf3de75..cdcdea36 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -372,6 +372,10 @@ static void SleepThread(s64 nanoseconds) {
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThreadHandle(), nanoseconds);
+
HLE::Reschedule(__func__);
}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index d6188f05..f4c2df1c 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -21,11 +21,11 @@ void UpdateState(State state) {
void Init(EmuWindow* emu_window) {
Core::Init();
+ CoreTiming::Init();
Memory::Init();
HW::Init();
Kernel::Init();
HLE::Init();
- CoreTiming::Init();
VideoCore::Init(emu_window);
}
@@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) {
void Shutdown() {
VideoCore::Shutdown();
- CoreTiming::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
Memory::Shutdown();
+ CoreTiming::Shutdown();
Core::Shutdown();
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4df3a5e2..29d220e8 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -87,8 +87,11 @@ void RendererOpenGL::SwapBuffers() {
*/
void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture) {
+
+ // TODO: Why are active_fb and the valid framebuffer flipped compared to 3dbrew documentation
+ // and GSP definitions?
const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
- framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1);
+ framebuffer.active_fb == 0 ? framebuffer.address_left2 : framebuffer.address_left1);
LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer.stride * framebuffer.height,