Răsfoiți Sursa

GPU: Make use of RegisterSet.

Tony Wasserka 12 ani în urmă
părinte
comite
75775e9ef4

+ 28 - 21
src/core/hle/service/gsp.cpp

@@ -139,8 +139,8 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
 
     Kernel::SetEventLocked(g_event, false);
 
-    // Hack - This function will permanently set the state of the GSP event such that GPU command 
-    // synchronization barriers always passthrough. Correct solution would be to set this after the 
+    // Hack - This function will permanently set the state of the GSP event such that GPU command
+    // synchronization barriers always passthrough. Correct solution would be to set this after the
     // GPU as processed all queued up commands, but due to the emulator being single-threaded they
     // will always be ready.
     Kernel::SetPermanentLock(g_event, true);
@@ -153,6 +153,12 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
 
 /// This triggers handling of the GX command written to the command buffer in shared memory.
 void TriggerCmdReqQueue(Service::Interface* self) {
+
+    // Utility function to convert register ID to address
+    auto WriteGPURegister = [](u32 id, u32 data) {
+        GPU::Write<u32>(0x1EF00000 + 4 * id, data);
+    };
+
     GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id);
     u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20));
 
@@ -164,9 +170,9 @@ void TriggerCmdReqQueue(Service::Interface* self) {
         break;
 
     case GXCommandId::SET_COMMAND_LIST_LAST:
-        GPU::Write<u32>(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3);
-        GPU::Write<u32>(GPU::Registers::CommandListSize, cmd_buff[2] >> 3);
-        GPU::Write<u32>(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this
+        WriteGPURegister(GPU::Regs::CommandProcessor + 2, cmd_buff[1] >> 3); // command list data address
+        WriteGPURegister(GPU::Regs::CommandProcessor, cmd_buff[2] >> 3);     // command list address
+        WriteGPURegister(GPU::Regs::CommandProcessor + 4, 1);                // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
 
         // TODO: Move this to GPU
         // TODO: Not sure what units the size is measured in
@@ -174,27 +180,28 @@ void TriggerCmdReqQueue(Service::Interface* self) {
         break;
 
     case GXCommandId::SET_MEMORY_FILL:
-        GPU::Write<u32>(GPU::Registers::MemoryFillStart1, cmd_buff[1] >> 3);
-        GPU::Write<u32>(GPU::Registers::MemoryFillEnd1, cmd_buff[3] >> 3);
-        GPU::Write<u32>(GPU::Registers::MemoryFillSize1, cmd_buff[3] - cmd_buff[1]);
-        GPU::Write<u32>(GPU::Registers::MemoryFillValue1, cmd_buff[2]);
-        GPU::Write<u32>(GPU::Registers::MemoryFillStart2, cmd_buff[4] >> 3);
-        GPU::Write<u32>(GPU::Registers::MemoryFillEnd2, cmd_buff[6] >> 3);
-        GPU::Write<u32>(GPU::Registers::MemoryFillSize2, cmd_buff[6] - cmd_buff[4]);
-        GPU::Write<u32>(GPU::Registers::MemoryFillValue2, cmd_buff[5]);
+        WriteGPURegister(GPU::Regs::MemoryFill, cmd_buff[1] >> 3);              // Start 1
+        WriteGPURegister(GPU::Regs::MemoryFill + 1, cmd_buff[3] >> 3);          // End 1
+        WriteGPURegister(GPU::Regs::MemoryFill + 2, cmd_buff[3] - cmd_buff[1]); // Size 1
+        WriteGPURegister(GPU::Regs::MemoryFill + 3, cmd_buff[2]);               // Value 1
+
+        WriteGPURegister(GPU::Regs::MemoryFill + 4, cmd_buff[4] >> 3);          // Start 2
+        WriteGPURegister(GPU::Regs::MemoryFill + 5, cmd_buff[6] >> 3);          // End 2
+        WriteGPURegister(GPU::Regs::MemoryFill + 6, cmd_buff[6] - cmd_buff[4]); // Size 2
+        WriteGPURegister(GPU::Regs::MemoryFill + 7, cmd_buff[5]);               // Value 2
         break;
 
     // TODO: Check if texture copies are implemented correctly..
     case GXCommandId::SET_DISPLAY_TRANSFER:
     case GXCommandId::SET_TEXTURE_COPY:
-        GPU::Write<u32>(GPU::Registers::DisplayInputBufferAddr, cmd_buff[1] >> 3);
-        GPU::Write<u32>(GPU::Registers::DisplayOutputBufferAddr, cmd_buff[2] >> 3);
-        GPU::Write<u32>(GPU::Registers::DisplayInputBufferSize, cmd_buff[3]);
-        GPU::Write<u32>(GPU::Registers::DisplayOutputBufferSize, cmd_buff[4]);
-        GPU::Write<u32>(GPU::Registers::DisplayTransferFlags, cmd_buff[5]);
-
-        // TODO: GPU::Registers::DisplayTriggerTransfer should be ORed with 1 for texture copies?
-        GPU::Write<u32>(GPU::Registers::DisplayTriggerTransfer, 1);
+        WriteGPURegister(GPU::Regs::DisplayTransfer, cmd_buff[1] >> 3);     // input buffer address
+        WriteGPURegister(GPU::Regs::DisplayTransfer + 1, cmd_buff[2] >> 3); // output buffer address
+        WriteGPURegister(GPU::Regs::DisplayTransfer + 3, cmd_buff[3]);      // input buffer size
+        WriteGPURegister(GPU::Regs::DisplayTransfer + 2, cmd_buff[4]);      // output buffer size
+        WriteGPURegister(GPU::Regs::DisplayTransfer + 4, cmd_buff[5]);      // transfer flags
+
+        // TODO: Should this only be ORed with 1 for texture copies?
+        WriteGPURegister(GPU::Regs::DisplayTransfer + 6, 1);                // trigger transfer
         break;
 
     case GXCommandId::SET_COMMAND_LIST_FIRST:

+ 101 - 241
src/core/hw/gpu.cpp

@@ -15,38 +15,48 @@
 
 namespace GPU {
 
-Registers g_regs;
+RegisterSet<u32, Regs> g_regs;
 
 u64 g_last_ticks = 0; ///< Last CPU ticks
 
 /**
  * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM
- * @param 
+ * @param
  */
 void SetFramebufferLocation(const FramebufferLocation mode) {
     switch (mode) {
     case FRAMEBUFFER_LOCATION_FCRAM:
-        g_regs.framebuffer_top_left_1   = PADDR_TOP_LEFT_FRAME1;
-        g_regs.framebuffer_top_left_2   = PADDR_TOP_LEFT_FRAME2;
-        g_regs.framebuffer_top_right_1  = PADDR_TOP_RIGHT_FRAME1;
-        g_regs.framebuffer_top_right_2  = PADDR_TOP_RIGHT_FRAME2;
-        g_regs.framebuffer_sub_left_1   = PADDR_SUB_FRAME1;
-        //g_regs.framebuffer_sub_left_2  = unknown;
-        g_regs.framebuffer_sub_right_1  = PADDR_SUB_FRAME2;
-        //g_regs.framebufferr_sub_right_2 = unknown;
+    {
+        auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
+        auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
+
+        framebuffer_top.data.address_left1  = PADDR_TOP_LEFT_FRAME1;
+        framebuffer_top.data.address_left2  = PADDR_TOP_LEFT_FRAME2;
+        framebuffer_top.data.address_right1 = PADDR_TOP_RIGHT_FRAME1;
+        framebuffer_top.data.address_right2 = PADDR_TOP_RIGHT_FRAME2;
+        framebuffer_sub.data.address_left1  = PADDR_SUB_FRAME1;
+        //framebuffer_sub.data.address_left2  = unknown;
+        framebuffer_sub.data.address_right1 = PADDR_SUB_FRAME2;
+        //framebuffer_sub.data.address_right2 = unknown;
         break;
+    }
 
     case FRAMEBUFFER_LOCATION_VRAM:
-        g_regs.framebuffer_top_left_1   = PADDR_VRAM_TOP_LEFT_FRAME1;
-        g_regs.framebuffer_top_left_2   = PADDR_VRAM_TOP_LEFT_FRAME2;
-        g_regs.framebuffer_top_right_1  = PADDR_VRAM_TOP_RIGHT_FRAME1;
-        g_regs.framebuffer_top_right_2  = PADDR_VRAM_TOP_RIGHT_FRAME2;
-        g_regs.framebuffer_sub_left_1   = PADDR_VRAM_SUB_FRAME1;
-        //g_regs.framebuffer_sub_left_2  = unknown;
-        g_regs.framebuffer_sub_right_1  = PADDR_VRAM_SUB_FRAME2;
-        //g_regs.framebufferr_sub_right_2 = unknown;
+    {
+        auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
+        auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
+
+        framebuffer_top.data.address_left1  = PADDR_VRAM_TOP_LEFT_FRAME1;
+        framebuffer_top.data.address_left2  = PADDR_VRAM_TOP_LEFT_FRAME2;
+        framebuffer_top.data.address_right1 = PADDR_VRAM_TOP_RIGHT_FRAME1;
+        framebuffer_top.data.address_right2 = PADDR_VRAM_TOP_RIGHT_FRAME2;
+        framebuffer_sub.data.address_left1  = PADDR_VRAM_SUB_FRAME1;
+        //framebuffer_sub.data.address_left2  = unknown;
+        framebuffer_sub.data.address_right1 = PADDR_VRAM_SUB_FRAME2;
+        //framebuffer_sub.data.address_right2 = unknown;
         break;
     }
+    }
 }
 
 /**
@@ -87,219 +97,73 @@ const u8* GetFramebufferPointer(const u32 address) {
 }
 
 template <typename T>
-inline void Read(T &var, const u32 addr) {
-    switch (addr) {
-    case Registers::MemoryFillStart1:
-    case Registers::MemoryFillStart2:
-        var = g_regs.memory_fill[(addr - Registers::MemoryFillStart1) / 0x10].address_start;
-        break;
-
-    case Registers::MemoryFillEnd1:
-    case Registers::MemoryFillEnd2:
-        var = g_regs.memory_fill[(addr - Registers::MemoryFillEnd1) / 0x10].address_end;
-        break;
-
-    case Registers::MemoryFillSize1:
-    case Registers::MemoryFillSize2:
-        var = g_regs.memory_fill[(addr - Registers::MemoryFillSize1) / 0x10].size;
-        break;
-
-    case Registers::MemoryFillValue1:
-    case Registers::MemoryFillValue2:
-        var = g_regs.memory_fill[(addr - Registers::MemoryFillValue1) / 0x10].value;
-        break;
-
-    case Registers::FramebufferTopSize:
-        var = g_regs.top_framebuffer.size;
-        break;
-
-    case Registers::FramebufferTopLeft1:
-        var = g_regs.framebuffer_top_left_1;
-        break;
-
-    case Registers::FramebufferTopLeft2:
-        var = g_regs.framebuffer_top_left_2;
-        break;
-
-    case Registers::FramebufferTopFormat:
-        var = g_regs.top_framebuffer.format;
-        break;
-
-    case Registers::FramebufferTopSwapBuffers:
-        var = g_regs.top_framebuffer.active_fb;
-        break;
-
-    case Registers::FramebufferTopStride:
-        var = g_regs.top_framebuffer.stride;
-        break;
-
-    case Registers::FramebufferTopRight1:
-        var = g_regs.framebuffer_top_right_1;
-        break;
-
-    case Registers::FramebufferTopRight2:
-        var = g_regs.framebuffer_top_right_2;
-        break;
-
-    case Registers::FramebufferSubSize:
-        var = g_regs.sub_framebuffer.size;
-        break;
-
-    case Registers::FramebufferSubLeft1:
-        var = g_regs.framebuffer_sub_left_1;
-        break;
-
-    case Registers::FramebufferSubRight1:
-        var = g_regs.framebuffer_sub_right_1;
-        break;
-
-    case Registers::FramebufferSubFormat:
-        var = g_regs.sub_framebuffer.format;
-        break;
-
-    case Registers::FramebufferSubSwapBuffers:
-        var = g_regs.sub_framebuffer.active_fb;
-        break;
-
-    case Registers::FramebufferSubStride:
-        var = g_regs.sub_framebuffer.stride;
-        break;
-
-    case Registers::FramebufferSubLeft2:
-        var = g_regs.framebuffer_sub_left_2;
-        break;
-
-    case Registers::FramebufferSubRight2:
-        var = g_regs.framebuffer_sub_right_2;
-        break;
-
-    case Registers::DisplayInputBufferAddr:
-        var = g_regs.display_transfer.input_address;
-        break;
+inline void Read(T &var, const u32 raw_addr) {
+    u32 addr = raw_addr - 0x1EF00000;
+    int index = addr / 4;
 
-    case Registers::DisplayOutputBufferAddr:
-        var = g_regs.display_transfer.output_address;
-        break;
-
-    case Registers::DisplayOutputBufferSize:
-        var = g_regs.display_transfer.output_size;
-        break;
-
-    case Registers::DisplayInputBufferSize:
-        var = g_regs.display_transfer.input_size;
-        break;
-
-    case Registers::DisplayTransferFlags:
-        var = g_regs.display_transfer.flags;
-        break;
-
-    // Not sure if this is supposed to be readable
-    case Registers::DisplayTriggerTransfer:
-        var = g_regs.display_transfer.trigger;
-        break;
-
-    case Registers::CommandListSize:
-        var = g_regs.command_list_size;
-        break;
-
-    case Registers::CommandListAddress:
-        var = g_regs.command_list_address;
-        break;
-
-    case Registers::ProcessCommandList:
-        var = g_regs.command_processing_enabled;
-        break;
-
-    default:
+    // Reads other than u32 are untested, so I'd rather have them abort than silently fail
+    if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
+    {
         ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
-        break;
+        return;
     }
+
+    var = g_regs[static_cast<Regs::Id>(addr / 4)];
 }
 
 template <typename T>
 inline void Write(u32 addr, const T data) {
-    switch (static_cast<Registers::Id>(addr)) {
-    case Registers::MemoryFillStart1:
-    case Registers::MemoryFillStart2:
-        g_regs.memory_fill[(addr - Registers::MemoryFillStart1) / 0x10].address_start = data;
-        break;
+    addr -= 0x1EF00000;
+    int index = addr / 4;
 
-    case Registers::MemoryFillEnd1:
-    case Registers::MemoryFillEnd2:
-        g_regs.memory_fill[(addr - Registers::MemoryFillEnd1) / 0x10].address_end = data;
-        break;
+    // Writes other than u32 are untested, so I'd rather have them abort than silently fail
+    if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
+    {
+        ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+        return;
+    }
 
-    case Registers::MemoryFillSize1:
-    case Registers::MemoryFillSize2:
-        g_regs.memory_fill[(addr - Registers::MemoryFillSize1) / 0x10].size = data;
-        break;
+    g_regs[static_cast<Regs::Id>(index)] = data;
+
+    switch (static_cast<Regs::Id>(index)) {
 
-    case Registers::MemoryFillValue1:
-    case Registers::MemoryFillValue2:
+    // Memory fills are triggered once the fill value is written.
+    // NOTE: This is not verified.
+    case Regs::MemoryFill + 3:
+    case Regs::MemoryFill + 7:
     {
-        Registers::MemoryFillConfig& config = g_regs.memory_fill[(addr - Registers::MemoryFillValue1) / 0x10];
-        config.value = data;
+        const auto& config = g_regs.Get<Regs::MemoryFill>(static_cast<Regs::Id>(index - 3));
 
         // TODO: Not sure if this check should be done at GSP level instead
-        if (config.address_start) {
+        if (config.data.address_start) {
             // TODO: Not sure if this algorithm is correct, particularly because it doesn't use the size member at all
-            u32* start = (u32*)Memory::GetPointer(config.GetStartAddress());
-            u32* end = (u32*)Memory::GetPointer(config.GetEndAddress());
+            u32* start = (u32*)Memory::GetPointer(config.data.GetStartAddress());
+            u32* end = (u32*)Memory::GetPointer(config.data.GetEndAddress());
             for (u32* ptr = start; ptr < end; ++ptr)
-                *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
+                *ptr = bswap32(config.data.value); // TODO: This is just a workaround to missing framebuffer format emulation
 
-            DEBUG_LOG(GPU, "MemoryFill from %x to %x", config.GetStartAddress(), config.GetEndAddress());
+            DEBUG_LOG(GPU, "MemoryFill from %x to %x", config.data.GetStartAddress(), config.data.GetEndAddress());
         }
         break;
     }
 
-    // TODO: Framebuffer registers!!
-    case Registers::FramebufferTopSwapBuffers:
-        g_regs.top_framebuffer.active_fb = data;
-        // TODO: Not sure if this should only be done upon a change!
-        break;
-
-    case Registers::FramebufferSubSwapBuffers:
-        g_regs.sub_framebuffer.active_fb = data;
-        // TODO: Not sure if this should only be done upon a change!
-        break;
-
-    case Registers::DisplayInputBufferAddr:
-        g_regs.display_transfer.input_address = data;
-        break;
-
-    case Registers::DisplayOutputBufferAddr:
-        g_regs.display_transfer.output_address = data;
-        break;
-
-    case Registers::DisplayOutputBufferSize:
-        g_regs.display_transfer.output_size = data;
-        break;
-
-    case Registers::DisplayInputBufferSize:
-        g_regs.display_transfer.input_size = data;
-        break;
-
-    case Registers::DisplayTransferFlags:
-        g_regs.display_transfer.flags = data;
-        break;
-
-    case Registers::DisplayTriggerTransfer:
-        g_regs.display_transfer.trigger = data;
-        if (g_regs.display_transfer.trigger & 1) {
-            u8* source_pointer = Memory::GetPointer(g_regs.display_transfer.GetPhysicalInputAddress());
-            u8* dest_pointer = Memory::GetPointer(g_regs.display_transfer.GetPhysicalOutputAddress());
+    case Regs::DisplayTransfer + 6:
+    {
+        const auto& config = g_regs.Get<Regs::DisplayTransfer>();
+        if (config.data.trigger & 1) {
+            u8* source_pointer = Memory::GetPointer(config.data.GetPhysicalInputAddress());
+            u8* dest_pointer = Memory::GetPointer(config.data.GetPhysicalOutputAddress());
 
-            for (int y = 0; y < g_regs.display_transfer.output_height; ++y) {
+            for (int y = 0; y < config.data.output_height; ++y) {
                 // TODO: Why does the register seem to hold twice the framebuffer width?
-                for (int x = 0; x < g_regs.display_transfer.output_width / 2; ++x) {
+                for (int x = 0; x < config.data.output_width / 2; ++x) {
                     int source[4] = { 0, 0, 0, 0}; // rgba;
 
-                    switch (g_regs.display_transfer.input_format) {
-                    case Registers::FramebufferFormat::RGBA8:
+                    switch (config.data.input_format) {
+                    case Regs::FramebufferFormat::RGBA8:
                     {
                         // TODO: Most likely got the component order messed up.
-                        u8* srcptr = source_pointer + x * 4 + y * g_regs.display_transfer.input_width * 4 / 2;
+                        u8* srcptr = source_pointer + x * 4 + y * config.data.input_width * 4 / 2;
                         source[0] = srcptr[0]; // blue
                         source[1] = srcptr[1]; // green
                         source[2] = srcptr[2]; // red
@@ -308,15 +172,15 @@ inline void Write(u32 addr, const T data) {
                     }
 
                     default:
-                        ERROR_LOG(GPU, "Unknown source framebuffer format %x", (int)g_regs.display_transfer.input_format.Value());
+                        ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.data.input_format.Value());
                         break;
                     }
 
-                    switch (g_regs.display_transfer.output_format) {
-                    /*case Registers::FramebufferFormat::RGBA8:
+                    switch (config.data.output_format) {
+                    /*case Regs::FramebufferFormat::RGBA8:
                     {
                         // TODO: Untested
-                        u8* dstptr = (u32*)(dest_pointer + x * 4 + y * g_regs.display_transfer.output_width * 4);
+                        u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.data.output_width * 4);
                         dstptr[0] = source[0];
                         dstptr[1] = source[1];
                         dstptr[2] = source[2];
@@ -324,9 +188,9 @@ inline void Write(u32 addr, const T data) {
                         break;
                     }*/
 
-                    case Registers::FramebufferFormat::RGB8:
+                    case Regs::FramebufferFormat::RGB8:
                     {
-                        u8* dstptr = dest_pointer + x * 3 + y * g_regs.display_transfer.output_width * 3 / 2;
+                        u8* dstptr = dest_pointer + x * 3 + y * config.data.output_width * 3 / 2;
                         dstptr[0] = source[0]; // blue
                         dstptr[1] = source[1]; // green
                         dstptr[2] = source[2]; // red
@@ -334,40 +198,34 @@ inline void Write(u32 addr, const T data) {
                     }
 
                     default:
-                        ERROR_LOG(GPU, "Unknown destination framebuffer format %x", static_cast<int>(g_regs.display_transfer.output_format.Value()));
+                        ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.data.output_format.Value());
                         break;
                     }
                 }
             }
 
             DEBUG_LOG(GPU, "DisplayTriggerTransfer: %x bytes from %x(%xx%x)-> %x(%xx%x), dst format %x",
-                      g_regs.display_transfer.output_height * g_regs.display_transfer.output_width * 4,
-                      g_regs.display_transfer.GetPhysicalInputAddress(), (int)g_regs.display_transfer.input_width, (int)g_regs.display_transfer.input_height,
-                      g_regs.display_transfer.GetPhysicalOutputAddress(), (int)g_regs.display_transfer.output_width, (int)g_regs.display_transfer.output_height,
-                      (int)g_regs.display_transfer.output_format.Value());
+                      config.data.output_height * config.data.output_width * 4,
+                      config.data.GetPhysicalInputAddress(), (int)config.data.input_width, (int)config.data.input_height,
+                      config.data.GetPhysicalOutputAddress(), (int)config.data.output_width, (int)config.data.output_height,
+                      config.data.output_format.Value());
         }
         break;
+    }
 
-    case Registers::CommandListSize:
-        g_regs.command_list_size = data;
-        break;
-
-    case Registers::CommandListAddress:
-        g_regs.command_list_address = data;
-        break;
-
-    case Registers::ProcessCommandList:
-        g_regs.command_processing_enabled = data;
-        if (g_regs.command_processing_enabled & 1)
+    case Regs::CommandProcessor + 4:
+    {
+        const auto& config = g_regs.Get<Regs::CommandProcessor>();
+        if (config.data.trigger & 1)
         {
-            // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3);
-            ERROR_LOG(GPU, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, g_regs.command_list_address << 3);
+            // u32* buffer = (u32*)Memory::GetPointer(config.data.address << 3);
+            ERROR_LOG(GPU, "Beginning %x bytes of commands from address %x", config.data.size, config.data.address << 3);
             // TODO: Process command list!
         }
         break;
+    }
 
     default:
-        ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
         break;
     }
 }
@@ -402,18 +260,20 @@ void Init() {
 //    SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM);
     SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM);
 
+    auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
+    auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
     // TODO: Width should be 240 instead?
-    g_regs.top_framebuffer.width = 480;
-    g_regs.top_framebuffer.height = 400;
-    g_regs.top_framebuffer.stride = 480*3;
-    g_regs.top_framebuffer.color_format = Registers::FramebufferFormat::RGB8;
-    g_regs.top_framebuffer.active_fb = 0;
-
-    g_regs.sub_framebuffer.width = 480;
-    g_regs.sub_framebuffer.height = 400;
-    g_regs.sub_framebuffer.stride = 480*3;
-    g_regs.sub_framebuffer.color_format = Registers::FramebufferFormat::RGB8;
-    g_regs.sub_framebuffer.active_fb = 0;
+    framebuffer_top.data.width = 480;
+    framebuffer_top.data.height = 400;
+    framebuffer_top.data.stride = 480*3;
+    framebuffer_top.data.color_format = Regs::FramebufferFormat::RGB8;
+    framebuffer_top.data.active_fb = 0;
+
+    framebuffer_sub.data.width = 480;
+    framebuffer_sub.data.height = 400;
+    framebuffer_sub.data.stride = 480*3;
+    framebuffer_sub.data.color_format = Regs::FramebufferFormat::RGB8;
+    framebuffer_sub.data.active_fb = 0;
 
     NOTICE_LOG(GPU, "initialized OK");
 }

+ 71 - 65
src/core/hw/gpu.h

@@ -6,54 +6,31 @@
 
 #include "common/common_types.h"
 #include "common/bit_field.h"
+#include "common/register_set.h"
 
 namespace GPU {
 
 static const u32 kFrameCycles   = 268123480 / 60;   ///< 268MHz / 60 frames per second
 static const u32 kFrameTicks    = kFrameCycles / 3; ///< Approximate number of instructions/frame
 
-struct Registers {
+// MMIO region 0x1EFxxxxx
+struct Regs {
     enum Id : u32 {
-        MemoryFillStart1          = 0x1EF00010,
-        MemoryFillEnd1            = 0x1EF00014,
-        MemoryFillSize1           = 0x1EF00018,
-        MemoryFillValue1          = 0x1EF0001C,
-        MemoryFillStart2          = 0x1EF00020,
-        MemoryFillEnd2            = 0x1EF00024,
-        MemoryFillSize2           = 0x1EF00028,
-        MemoryFillValue2          = 0x1EF0002C,
-
-        FramebufferTopSize        = 0x1EF0045C,
-        FramebufferTopLeft1       = 0x1EF00468,   // Main LCD, first framebuffer for 3D left
-        FramebufferTopLeft2       = 0x1EF0046C,   // Main LCD, second framebuffer for 3D left
-        FramebufferTopFormat      = 0x1EF00470,
-        FramebufferTopSwapBuffers = 0x1EF00478,
-        FramebufferTopStride      = 0x1EF00490,   // framebuffer row stride?
-        FramebufferTopRight1      = 0x1EF00494,   // Main LCD, first framebuffer for 3D right
-        FramebufferTopRight2      = 0x1EF00498,   // Main LCD, second framebuffer for 3D right
-
-        FramebufferSubSize        = 0x1EF0055C,
-        FramebufferSubLeft1       = 0x1EF00568,   // Sub LCD, first framebuffer
-        FramebufferSubLeft2       = 0x1EF0056C,   // Sub LCD, second framebuffer
-        FramebufferSubFormat      = 0x1EF00570,
-        FramebufferSubSwapBuffers = 0x1EF00578,
-        FramebufferSubStride      = 0x1EF00590,   // framebuffer row stride?
-        FramebufferSubRight1      = 0x1EF00594,   // Sub LCD, unused first framebuffer
-        FramebufferSubRight2      = 0x1EF00598,   // Sub LCD, unused second framebuffer
-
-        DisplayInputBufferAddr  = 0x1EF00C00,
-        DisplayOutputBufferAddr = 0x1EF00C04,
-        DisplayOutputBufferSize = 0x1EF00C08,
-        DisplayInputBufferSize  = 0x1EF00C0C,
-        DisplayTransferFlags    = 0x1EF00C10,
-        // Unknown??
-        DisplayTriggerTransfer  = 0x1EF00C18,
-
-        CommandListSize         = 0x1EF018E0,
-        CommandListAddress      = 0x1EF018E8,
-        ProcessCommandList      = 0x1EF018F0,
+        MemoryFill                = 0x00004, // + 5,6,7; second block at 8-11
+
+        FramebufferTop            = 0x00117, // + 11a,11b,11c,11d(?),11e...126
+        FramebufferBottom         = 0x00157, // + 15a,15b,15c,15d(?),15e...166
+
+        DisplayTransfer           = 0x00300, // + 301,302,303,304,305,306
+
+        CommandProcessor          = 0x00638, // + 63a,63c
+
+        NumIds                    = 0x01000
     };
 
+    template<Id id>
+    union Struct;
+
     enum class FramebufferFormat : u32 {
         RGBA8  = 0,
         RGB8   = 1,
@@ -62,7 +39,11 @@ struct Registers {
         RGBA4  = 4,
     };
 
-    struct MemoryFillConfig {
+};
+
+template<>
+union Regs::Struct<Regs::MemoryFill> {
+    struct {
         u32 address_start;
         u32 address_end; // ?
         u32 size;
@@ -75,21 +56,15 @@ struct Registers {
         inline u32 GetEndAddress() const {
             return address_end * 8;
         }
-    };
-
-    MemoryFillConfig memory_fill[2];
+    } data;
+};
+static_assert(sizeof(Regs::Struct<Regs::MemoryFill>) == 0x10, "Structure size and register block length don't match");
 
-    // TODO: Move these into the framebuffer struct
-    u32 framebuffer_top_left_1;
-    u32 framebuffer_top_left_2;
-    u32 framebuffer_top_right_1;
-    u32 framebuffer_top_right_2;
-    u32 framebuffer_sub_left_1;
-    u32 framebuffer_sub_left_2;
-    u32 framebuffer_sub_right_1;
-    u32 framebuffer_sub_right_2;
+template<>
+union Regs::Struct<Regs::FramebufferTop> {
+    using Format = Regs::FramebufferFormat;
 
-    struct FrameBufferConfig {
+    struct {
         union {
             u32 size;
 
@@ -97,22 +72,43 @@ struct Registers {
             BitField<16, 16, u32> height;
         };
 
+        u32 pad0[2];
+
+        u32 address_left1;
+        u32 address_left2;
+
         union {
             u32 format;
 
-            BitField< 0, 3, FramebufferFormat> color_format;
+            BitField< 0, 3, Format> color_format;
         };
 
+        u32 pad1;
+
         union {
             u32 active_fb;
 
             BitField<0, 1, u32> second_fb_active;
         };
 
+        u32 pad2[5];
+
         u32 stride;
-    };
-    FrameBufferConfig top_framebuffer;
-    FrameBufferConfig sub_framebuffer;
+
+        u32 address_right1;
+        u32 address_right2;
+    } data;
+};
+template<>
+union Regs::Struct<Regs::FramebufferBottom> {
+    using Type = decltype(Regs::Struct<Regs::FramebufferTop>::data);
+    Type data;
+};
+static_assert(sizeof(Regs::Struct<Regs::FramebufferTop>) == 0x40, "Structure size and register block length don't match");
+
+template<>
+union Regs::Struct<Regs::DisplayTransfer> {
+    using Format = Regs::FramebufferFormat;
 
     struct {
         u32 input_address;
@@ -144,21 +140,31 @@ struct Registers {
             u32 flags;
 
             BitField< 0, 1, u32> flip_data;
-            BitField< 8, 3, FramebufferFormat> input_format;
-            BitField<12, 3, FramebufferFormat> output_format;
+            BitField< 8, 3, Format> input_format;
+            BitField<12, 3, Format> output_format;
             BitField<16, 1, u32> output_tiled;
         };
 
         u32 unknown;
         u32 trigger;
-    } display_transfer;
+    } data;
+};
+static_assert(sizeof(Regs::Struct<Regs::DisplayTransfer>) == 0x1C, "Structure size and register block length don't match");
 
-    u32 command_list_size;
-    u32 command_list_address;
-    u32 command_processing_enabled;
+template<>
+union Regs::Struct<Regs::CommandProcessor> {
+    struct {
+        u32 size;
+        u32 pad0;
+        u32 address;
+        u32 pad1;
+        u32 trigger;
+    } data;
 };
+static_assert(sizeof(Regs::Struct<Regs::CommandProcessor>) == 0x14, "Structure size and register block length don't match");
+
 
-extern Registers g_regs;
+extern RegisterSet<u32, Regs> g_regs;
 
 enum {
     TOP_ASPECT_X        = 0x5,
@@ -208,7 +214,7 @@ enum FramebufferLocation {
 
 /**
  * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM
- * @param 
+ * @param
  */
 void SetFramebufferLocation(const FramebufferLocation mode);
 

+ 28 - 26
src/video_core/renderer_opengl/renderer_opengl.cpp

@@ -12,8 +12,8 @@
 
 /// RendererOpenGL constructor
 RendererOpenGL::RendererOpenGL() {
-    memset(m_fbo, 0, sizeof(m_fbo));  
-    memset(m_fbo_rbo, 0, sizeof(m_fbo_rbo));  
+    memset(m_fbo, 0, sizeof(m_fbo));
+    memset(m_fbo_rbo, 0, sizeof(m_fbo_rbo));
     memset(m_fbo_depth_buffers, 0, sizeof(m_fbo_depth_buffers));
 
     m_resolution_width = max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
@@ -35,7 +35,7 @@ void RendererOpenGL::SwapBuffers() {
     m_render_window->MakeCurrent();
 
     // EFB->XFB copy
-    // TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some 
+    // TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some
     // register write We're also treating both framebuffers as a single one in OpenGL.
     common::Rect framebuffer_size(0, 0, m_resolution_width, m_resolution_height);
     RenderXFB(framebuffer_size, framebuffer_size);
@@ -71,24 +71,26 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) {
     }
 }
 
-/** 
+/**
  * Renders external framebuffer (XFB)
  * @param src_rect Source rectangle in XFB to copy
  * @param dst_rect Destination rectangle in output framebuffer to copy to
  */
 void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) {
 
-    const u32 active_fb_top = (GPU::g_regs.top_framebuffer.active_fb == 1)
-                                ? GPU::g_regs.framebuffer_top_left_2
-                                : GPU::g_regs.framebuffer_top_left_1;
-    const u32 active_fb_sub = (GPU::g_regs.sub_framebuffer.active_fb == 1)
-                                ? GPU::g_regs.framebuffer_sub_left_2
-                                : GPU::g_regs.framebuffer_sub_left_1;
+    const auto& framebuffer_top = GPU::g_regs.Get<GPU::Regs::FramebufferTop>();
+    const auto& framebuffer_sub = GPU::g_regs.Get<GPU::Regs::FramebufferBottom>();
+    const u32 active_fb_top = (framebuffer_top.data.active_fb == 1)
+                                ? framebuffer_top.data.address_left2
+                                : framebuffer_top.data.address_left1;
+    const u32 active_fb_sub = (framebuffer_sub.data.active_fb == 1)
+                                ? framebuffer_sub.data.address_left2
+                                : framebuffer_sub.data.address_left1;
 
     DEBUG_LOG(GPU, "RenderXFB: %x bytes from %x(%xx%x), fmt %x",
-              GPU::g_regs.top_framebuffer.stride * GPU::g_regs.top_framebuffer.height,
-              GPU::GetFramebufferAddr(GPU::g_regs.framebuffer_top_left_1), (int)GPU::g_regs.top_framebuffer.width,
-              (int)GPU::g_regs.top_framebuffer.height, (int)GPU::g_regs.top_framebuffer.format);
+              framebuffer_top.data.stride * framebuffer_top.data.height,
+              GPU::GetFramebufferAddr(active_fb_top), (int)framebuffer_top.data.width,
+              (int)framebuffer_top.data.height, (int)framebuffer_top.data.format);
 
     // TODO: This should consider the GPU registers for framebuffer width, height and stride.
     FlipFramebuffer(GPU::GetFramebufferPointer(active_fb_top), m_xfb_top_flipped);
@@ -112,7 +114,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
     glReadBuffer(GL_COLOR_ATTACHMENT0);
 
     // Blit
-    glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_, 
+    glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_,
                       dst_rect.x0_, dst_rect.y1_, dst_rect.x1_, dst_rect.y0_,
                       GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
@@ -138,7 +140,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
 
     // Blit
     int offset = (VideoCore::kScreenTopWidth - VideoCore::kScreenBottomWidth) / 2;
-    glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight, 
+    glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight,
                       offset, VideoCore::kScreenBottomHeight, VideoCore::kScreenBottomWidth + offset, 0,
                       GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
@@ -147,7 +149,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
 
 /// Initialize the FBO
 void RendererOpenGL::InitFramebuffer() {
-    // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as 
+    // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
     // separate framebuffers
 
     // Init the FBOs
@@ -160,12 +162,12 @@ void RendererOpenGL::InitFramebuffer() {
     for (int i = 0; i < kMaxFramebuffers; i++) {
         // Generate color buffer storage
         glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]);
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth, 
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth,
             VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
 
         // Generate depth buffer storage
         glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth, 
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth,
             VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
 
         // Attach the buffers
@@ -181,7 +183,7 @@ void RendererOpenGL::InitFramebuffer() {
         } else {
             ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer");
             exit(1);
-        } 
+        }
     }
     glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s)
 
@@ -189,8 +191,8 @@ void RendererOpenGL::InitFramebuffer() {
     // -------------------------------
 
     // Create XFB textures
-    glGenTextures(1, &m_xfb_texture_top);  
-    glGenTextures(1, &m_xfb_texture_bottom);  
+    glGenTextures(1, &m_xfb_texture_top);
+    glGenTextures(1, &m_xfb_texture_bottom);
 
     // Alocate video memorry for XFB textures
     glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
@@ -206,13 +208,13 @@ void RendererOpenGL::InitFramebuffer() {
     // Create the FBO and attach color/depth textures
     glGenFramebuffers(1, &m_xfb_top); // Generate framebuffer
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_top);
-    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 
+    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
         m_xfb_texture_top, 0);
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
     glGenFramebuffers(1, &m_xfb_bottom); // Generate framebuffer
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_bottom);
-    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 
+    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
         m_xfb_texture_bottom, 0);
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
@@ -228,7 +230,7 @@ void RendererOpenGL::RenderFramebuffer() {
     glReadBuffer(GL_COLOR_ATTACHMENT0);
 
     // Blit
-    glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width, 
+    glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width,
         m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
     // Update the FPS count
@@ -244,7 +246,7 @@ void RendererOpenGL::RenderFramebuffer() {
 void RendererOpenGL::UpdateFramerate() {
 }
 
-/** 
+/**
  * Set the emulator window to use for renderer
  * @param window EmuWindow handle to emulator window to use for rendering
  */
@@ -278,7 +280,7 @@ void RendererOpenGL::Init() {
 
     GLenum err = glewInit();
     if (GLEW_OK != err) {
-        ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...", 
+        ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...",
             glewGetErrorString(err));
         exit(-1);
     }