summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/gsp.cpp25
-rw-r--r--src/core/hle/service/gsp.h18
2 files changed, 41 insertions, 2 deletions
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index 417c01b83..027ba5a37 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -36,6 +36,17 @@ static inline u8* GetCommandBuffer(u32 thread_id) {
0x800 + (thread_id * sizeof(CommandBuffer)));
}
+static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
+ if (0 == g_shared_memory)
+ return nullptr;
+
+ _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
+
+ // For each thread there are two FrameBufferUpdate fields
+ u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
+ return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+}
+
/// Gets a pointer to the interrupt relay queue for a given thread index
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
@@ -166,6 +177,7 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
/**
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
+ * @todo This should probably take a thread_id parameter and only signal this thread?
*/
void SignalInterrupt(InterruptId interrupt_id) {
if (0 == g_interrupt_event) {
@@ -191,7 +203,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
/// Executes the next GSP command
-void ExecuteCommand(const Command& command) {
+void ExecuteCommand(const Command& command, u32 thread_id) {
// Utility function to convert register ID to address
auto WriteGPURegister = [](u32 id, u32 data) {
GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -262,6 +274,15 @@ void ExecuteCommand(const Command& command) {
SignalInterrupt(InterruptId::PPF);
SignalInterrupt(InterruptId::P3D);
SignalInterrupt(InterruptId::DMA);
+
+ // 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)
+ SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+
+ info->is_dirty = false;
+ }
break;
}
@@ -304,7 +325,7 @@ void TriggerCmdReqQueue(Service::Interface* self) {
g_debugger.GXCommandProcessed((u8*)&command_buffer->commands[i]);
// Decode and execute command
- ExecuteCommand(command_buffer->commands[i]);
+ ExecuteCommand(command_buffer->commands[i], thread_id);
// Indicates that command has completed
command_buffer->number_commands = command_buffer->number_commands - 1;
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h
index 884cfd65a..a09d59dbb 100644
--- a/src/core/hle/service/gsp.h
+++ b/src/core/hle/service/gsp.h
@@ -4,6 +4,8 @@
#pragma once
+#include <cstddef>
+
#include "common/bit_field.h"
#include "core/hle/service/service.h"
@@ -76,6 +78,22 @@ struct FrameBufferInfo {
};
static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
+struct FrameBufferUpdate {
+ BitField<0, 1, u8> index; // Index used for GSP::SetBufferSwap
+ BitField<0, 1, u8> is_dirty; // true if GSP should update GPU framebuffer registers
+ u16 pad1;
+
+ FrameBufferInfo framebuffer_info[2];
+
+ u32 pad2;
+};
+static_assert(sizeof(FrameBufferUpdate) == 0x40, "Struct has incorrect size");
+// TODO: Not sure if this padding is correct.
+// Chances are the second block is stored at offset 0x24 rather than 0x20.
+#ifndef _MSC_VER
+static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20, "FrameBufferInfo element has incorrect alignment");
+#endif
+
/// GSP command
struct Command {
BitField<0, 8, CommandId> id;