From 66754b121f021d03028b75fca4ab8b31c65fe726 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 22:07:06 +0200 Subject: [PATCH 01/12] Pica: Add command list registers. --- src/core/hw/lcd.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/core/hw/lcd.h | 12 ++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index b57563a73..61ee99c1f 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -7,11 +7,11 @@ #include "core/core.h" #include "core/mem_map.h" +#include "core/hle/kernel/thread.h" #include "core/hw/lcd.h" #include "video_core/video_core.h" -#include "core/hle/kernel/thread.h" namespace LCD { @@ -89,31 +89,70 @@ inline void Read(T &var, const u32 addr) { case REG_FRAMEBUFFER_TOP_LEFT_1: var = g_regs.framebuffer_top_left_1; break; + case REG_FRAMEBUFFER_TOP_LEFT_2: var = g_regs.framebuffer_top_left_2; break; + case REG_FRAMEBUFFER_TOP_RIGHT_1: var = g_regs.framebuffer_top_right_1; break; + case REG_FRAMEBUFFER_TOP_RIGHT_2: var = g_regs.framebuffer_top_right_2; break; + case REG_FRAMEBUFFER_SUB_LEFT_1: var = g_regs.framebuffer_sub_left_1; break; + case REG_FRAMEBUFFER_SUB_RIGHT_1: var = g_regs.framebuffer_sub_right_1; break; + + case CommandListSize: + var = g_regs.command_list_size; + break; + + case CommandListAddress: + var = g_regs.command_list_address; + break; + + case ProcessCommandList: + var = g_regs.command_processing_enabled; + break; + default: ERROR_LOG(LCD, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); break; } - } template inline void Write(u32 addr, const T data) { - ERROR_LOG(LCD, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); + switch (addr) { + case CommandListSize: + g_regs.command_list_size = data; + break; + + case CommandListAddress: + g_regs.command_list_address = data; + break; + + case ProcessCommandList: + g_regs.command_processing_enabled = data; + if (g_regs.command_processing_enabled & 1) + { + // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3); + ERROR_LOG(LCD, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, g_regs.command_list_address << 3); + // TODO: Process command list! + } + break; + + default: + ERROR_LOG(LCD, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); + break; + } } // Explicitly instantiate template functions because we aren't defining this in the header: diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 2dd3b4adc..2ae852d7c 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -17,6 +17,10 @@ struct Registers { u32 framebuffer_sub_left_2; u32 framebuffer_sub_right_1; u32 framebuffer_sub_right_2; + + u32 command_list_size; + u32 command_list_address; + u32 command_processing_enabled; }; extern Registers g_regs; @@ -24,7 +28,7 @@ extern Registers g_regs; enum { TOP_ASPECT_X = 0x5, TOP_ASPECT_Y = 0x3, - + TOP_HEIGHT = 240, TOP_WIDTH = 400, BOTTOM_WIDTH = 320, @@ -57,12 +61,16 @@ enum { REG_FRAMEBUFFER_SUB_LEFT_2 = 0x1EF0056C, // Sub LCD, second framebuffer REG_FRAMEBUFFER_SUB_RIGHT_1 = 0x1EF00594, // Sub LCD, unused first framebuffer REG_FRAMEBUFFER_SUB_RIGHT_2 = 0x1EF00598, // Sub LCD, unused second framebuffer + + CommandListSize = 0x1EF018E0, + CommandListAddress = 0x1EF018E8, + ProcessCommandList = 0x1EF018F0, }; /// Framebuffer location enum FramebufferLocation { FRAMEBUFFER_LOCATION_UNKNOWN, ///< Framebuffer location is unknown - FRAMEBUFFER_LOCATION_FCRAM, ///< Framebuffer is in the GSP heap + FRAMEBUFFER_LOCATION_FCRAM, ///< Framebuffer is in the GSP heap FRAMEBUFFER_LOCATION_VRAM, ///< Framebuffer is in VRAM }; From 89410903896645c360a452ec94d1d899f1774902 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 22:26:45 +0200 Subject: [PATCH 02/12] GSP: Define more GX commands. --- src/core/hle/service/gsp.cpp | 51 ++++++++++++++++++++++++++---------- src/core/hle/service/gsp.h | 17 ++++++++++++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index 50cee2c41..3dda4c934 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -12,23 +12,24 @@ #include "core/hw/lcd.h" +#include "video_core/gpu_debugger.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// /// GSP shared memory GX command buffer header union GX_CmdBufferHeader { u32 hex; - // Current command index. This index is updated by GSP module after loading the command data, - // right before the command is processed. When this index is updated by GSP module, the total + // Current command index. This index is updated by GSP module after loading the command data, + // right before the command is processed. When this index is updated by GSP module, the total // commands field is decreased by one as well. BitField<0,8,u32> index; - + // Total commands to process, must not be value 0 when GSP module handles commands. This must be - // <=15 when writing a command to shared memory. This is incremented by the application when - // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only + // <=15 when writing a command to shared memory. This is incremented by the application when + // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only // used if this field is value 1. BitField<8,8,u32> number_commands; - }; /// Gets the address of the start (header) of a command buffer in GSP shared memory @@ -45,6 +46,7 @@ static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) { void GX_FinishCommand(u32 thread_id) { GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(thread_id); header->number_commands = header->number_commands - 1; + // TODO: Increment header->index? } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -54,10 +56,6 @@ namespace GSP_GPU { u32 g_thread_id = 0; -enum { - CMD_GX_REQUEST_DMA = 0x00000000, -}; - enum { REG_FRAMEBUFFER_1 = 0x00400468, REG_FRAMEBUFFER_2 = 0x00400494, @@ -72,7 +70,7 @@ void ReadHWRegs(Service::Interface* self) { u32 reg_addr = cmd_buff[1]; u32 size = cmd_buff[2]; u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]); - + switch (reg_addr) { // NOTE: Calling SetFramebufferLocation here is a hack... Not sure the correct way yet to set @@ -101,25 +99,50 @@ void RegisterInterruptRelayQueue(Service::Interface* self) { u32* cmd_buff = Service::GetCommandBuffer(); u32 flags = cmd_buff[1]; u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling + cmd_buff[2] = g_thread_id; // ThreadID } + /// This triggers handling of the GX command written to the command buffer in shared memory. void TriggerCmdReqQueue(Service::Interface* self) { GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id); u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20)); - switch (cmd_buff[0]) { + switch (static_cast(cmd_buff[0])) { // GX request DMA - typically used for copying memory from GSP heap to VRAM - case CMD_GX_REQUEST_DMA: + case GXCommandId::REQUEST_DMA: memcpy(Memory::GetPointer(cmd_buff[2]), Memory::GetPointer(cmd_buff[1]), cmd_buff[3]); break; + case GXCommandId::SET_COMMAND_LIST_LAST: + LCD::Write(LCD::CommandListAddress, cmd_buff[1] >> 3); + LCD::Write(LCD::CommandListSize, cmd_buff[2] >> 3); + LCD::Write(LCD::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this + break; + + case GXCommandId::SET_MEMORY_FILL: + break; + + case GXCommandId::SET_DISPLAY_TRANSFER: + break; + + case GXCommandId::SET_TEXTURE_COPY: + break; + + case GXCommandId::SET_COMMAND_LIST_FIRST: + { + //u32* buf0_data = (u32*)Memory::GetPointer(cmd_buff[1]); + //u32* buf1_data = (u32*)Memory::GetPointer(cmd_buff[3]); + //u32* buf2_data = (u32*)Memory::GetPointer(cmd_buff[5]); + break; + } + default: ERROR_LOG(GSP, "TriggerCmdReqQueue unknown command 0x%08X", cmd_buff[0]); } - + GX_FinishCommand(g_thread_id); } diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h index eb5786cd1..214de140f 100644 --- a/src/core/hle/service/gsp.h +++ b/src/core/hle/service/gsp.h @@ -11,6 +11,23 @@ namespace GSP_GPU { +enum class GXCommandId : u32 { + REQUEST_DMA = 0x00000000, + SET_COMMAND_LIST_LAST = 0x00000001, + SET_MEMORY_FILL = 0x00000002, // TODO: Confirm? (lictru uses 0x01000102) + SET_DISPLAY_TRANSFER = 0x00000003, + SET_TEXTURE_COPY = 0x00000004, + SET_COMMAND_LIST_FIRST = 0x00000005, +}; + +union GXCommand { + struct { + GXCommandId id; + }; + + u32 data[0x20]; +}; + /// Interface to "srv:" service class Interface : public Service::Interface { public: From 944dfe2f865631fb1402b4be99f6d3e7f81f189c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 22:34:55 +0200 Subject: [PATCH 03/12] Add initial graphics debugger interface. --- src/core/hle/service/gsp.cpp | 6 ++ src/video_core/gpu_debugger.h | 97 +++++++++++++++++++++++ src/video_core/video_core.vcxproj | 5 +- src/video_core/video_core.vcxproj.filters | 3 +- 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/video_core/gpu_debugger.h diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index 3dda4c934..a42759053 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -16,6 +16,9 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +// Main graphics debugger object - TODO: Here is probably not the best place for this +GraphicsDebugger g_debugger; + /// GSP shared memory GX command buffer header union GX_CmdBufferHeader { u32 hex; @@ -45,6 +48,9 @@ static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) { /// Finishes execution of a GSP command void GX_FinishCommand(u32 thread_id) { GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(thread_id); + + g_debugger.GXCommandProcessed(GX_GetCmdBufferPointer(thread_id, 0x20 + (header->index * 0x20))); + header->number_commands = header->number_commands - 1; // TODO: Increment header->index? } diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h new file mode 100644 index 000000000..ace9de95f --- /dev/null +++ b/src/video_core/gpu_debugger.h @@ -0,0 +1,97 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/log.h" + +#include "core/hle/service/gsp.h" + + +class GraphicsDebugger +{ +public: + // Base class for all objects which need to be notified about GPU events + class DebuggerObserver + { + public: + DebuggerObserver() : observed(nullptr) { } + + virtual ~DebuggerObserver() + { + if (observed) + observed->UnregisterObserver(this); + } + + /** + * Called when a GX command has been processed and is ready for being + * read via GraphicsDebugger::ReadGXCommandHistory. + * @param total_command_count Total number of commands in the GX history + * @note All methods in this class are called from the GSP thread + */ + virtual void GXCommandProcessed(int total_command_count) + { + const GSP_GPU::GXCommand& cmd = observed->ReadGXCommandHistory(total_command_count-1); + ERROR_LOG(GSP, "Received command: id=%x", cmd.id); + } + + protected: + GraphicsDebugger* GetDebugger() + { + return observed; + } + + private: + GraphicsDebugger* observed; + bool in_destruction; + + friend class GraphicsDebugger; + }; + + void GXCommandProcessed(u8* command_data) + { + gx_command_history.push_back(GSP_GPU::GXCommand()); + GSP_GPU::GXCommand& cmd = gx_command_history[gx_command_history.size()-1]; + + const int cmd_length = sizeof(GSP_GPU::GXCommand); + memcpy(cmd.data, command_data, cmd_length); + + ForEachObserver([this](DebuggerObserver* observer) { + observer->GXCommandProcessed(this->gx_command_history.size()); + } ); + } + + const GSP_GPU::GXCommand& ReadGXCommandHistory(int index) const + { + // TODO: Is this thread-safe? + return gx_command_history[index]; + } + + void RegisterObserver(DebuggerObserver* observer) + { + // TODO: Check for duplicates + observers.push_back(observer); + observer->observed = this; + } + + void UnregisterObserver(DebuggerObserver* observer) + { + std::remove(observers.begin(), observers.end(), observer); + observer->observed = nullptr; + } + +private: + void ForEachObserver(std::function func) + { + std::for_each(observers.begin(),observers.end(), func); + } + + std::vector observers; + + std::vector gx_command_history; +}; diff --git a/src/video_core/video_core.vcxproj b/src/video_core/video_core.vcxproj index d8c53271e..a4df92386 100644 --- a/src/video_core/video_core.vcxproj +++ b/src/video_core/video_core.vcxproj @@ -24,10 +24,11 @@ + - + @@ -128,4 +129,4 @@ - \ No newline at end of file + diff --git a/src/video_core/video_core.vcxproj.filters b/src/video_core/video_core.vcxproj.filters index 4eb2ef0a4..cc173a718 100644 --- a/src/video_core/video_core.vcxproj.filters +++ b/src/video_core/video_core.vcxproj.filters @@ -16,6 +16,7 @@ renderer_opengl + @@ -23,4 +24,4 @@ - \ No newline at end of file + From 5cc0497f8e9ecd20bf38a5e493ca171ac8105159 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 22:38:10 +0200 Subject: [PATCH 04/12] citra-qt: Add GX command history viewer. --- src/citra_qt/CMakeLists.txt | 2 + src/citra_qt/citra_qt.vcxproj | 6 +- src/citra_qt/citra_qt.vcxproj.filters | 16 ++++-- src/citra_qt/debugger/graphics.cpp | 83 +++++++++++++++++++++++++++ src/citra_qt/debugger/graphics.hxx | 43 ++++++++++++++ src/citra_qt/main.cpp | 6 ++ src/citra_qt/main.hxx | 2 + 7 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 src/citra_qt/debugger/graphics.cpp create mode 100644 src/citra_qt/debugger/graphics.hxx diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 549f69217..1968e34d3 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -2,6 +2,7 @@ set(SRCS bootmanager.cpp debugger/callstack.cpp debugger/disassembler.cpp + debugger/graphics.cpp debugger/ramview.cpp debugger/registers.cpp hotkeys.cpp @@ -38,6 +39,7 @@ qt4_wrap_cpp(MOC_SRCS bootmanager.hxx debugger/callstack.hxx debugger/disassembler.hxx + debugger/graphics.hxx debugger/registers.hxx debugger/ramview.hxx hotkeys.hxx diff --git a/src/citra_qt/citra_qt.vcxproj b/src/citra_qt/citra_qt.vcxproj index 3f24bbfbf..a1b24f676 100644 --- a/src/citra_qt/citra_qt.vcxproj +++ b/src/citra_qt/citra_qt.vcxproj @@ -130,6 +130,7 @@ + @@ -143,9 +144,10 @@ - + + @@ -182,4 +184,4 @@ - \ No newline at end of file + diff --git a/src/citra_qt/citra_qt.vcxproj.filters b/src/citra_qt/citra_qt.vcxproj.filters index 2b3838e29..faa4d9f52 100644 --- a/src/citra_qt/citra_qt.vcxproj.filters +++ b/src/citra_qt/citra_qt.vcxproj.filters @@ -36,10 +36,13 @@ debugger - + debugger - + + debugger + + debugger @@ -65,10 +68,13 @@ debugger - + debugger - + + debugger + + debugger @@ -106,4 +112,4 @@ - \ No newline at end of file + diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp new file mode 100644 index 000000000..9aaade8f9 --- /dev/null +++ b/src/citra_qt/debugger/graphics.cpp @@ -0,0 +1,83 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "graphics.hxx" +#include +#include +#include + +extern GraphicsDebugger g_debugger; + +GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0) +{ + connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int))); +} + +int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const +{ + return command_count; +} + +QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int command_index = index.row(); + const GSP_GPU::GXCommand& command = GetDebugger()->ReadGXCommandHistory(command_index); + if (role == Qt::DisplayRole) + { + std::map command_names; + command_names[GSP_GPU::GXCommandId::REQUEST_DMA] = "REQUEST_DMA"; + command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_FIRST] = "SET_COMMAND_LIST_FIRST"; + command_names[GSP_GPU::GXCommandId::SET_MEMORY_FILL] = "SET_MEMORY_FILL"; + command_names[GSP_GPU::GXCommandId::SET_DISPLAY_TRANSFER] = "SET_DISPLAY_TRANSFER"; + command_names[GSP_GPU::GXCommandId::SET_TEXTURE_COPY] = "SET_TEXTURE_COPY"; + command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_LAST] = "SET_COMMAND_LIST_LAST"; + QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[static_cast(command.id)]) + .arg(command.data[0], 8, 16, QLatin1Char('0')) + .arg(command.data[1], 8, 16, QLatin1Char('0')) + .arg(command.data[2], 8, 16, QLatin1Char('0')) + .arg(command.data[3], 8, 16, QLatin1Char('0')) + .arg(command.data[4], 8, 16, QLatin1Char('0')) + .arg(command.data[5], 8, 16, QLatin1Char('0')) + .arg(command.data[6], 8, 16, QLatin1Char('0')) + .arg(command.data[7], 8, 16, QLatin1Char('0')); + return QVariant(str); + } + else + { + return QVariant(); + } +} + +void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) +{ + emit GXCommandFinished(total_command_count); +} + +void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) +{ + if (total_command_count == 0) + return; + + int prev_command_count = command_count; + command_count = total_command_count; + emit dataChanged(index(prev_command_count,0), index(total_command_count-1,0)); +} + + +GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent) +{ + // TODO: set objectName! + + GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); + g_debugger.RegisterObserver(command_model); + + QListView* command_list = new QListView; + command_list->setModel(command_model); + command_list->setFont(QFont("monospace")); + + setWidget(command_list); +} diff --git a/src/citra_qt/debugger/graphics.hxx b/src/citra_qt/debugger/graphics.hxx new file mode 100644 index 000000000..72656f93c --- /dev/null +++ b/src/citra_qt/debugger/graphics.hxx @@ -0,0 +1,43 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "video_core/gpu_debugger.h" + +class GPUCommandStreamItemModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver +{ + Q_OBJECT + +public: + GPUCommandStreamItemModel(QObject* parent); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + +public: + void GXCommandProcessed(int total_command_count) override; + +public slots: + void OnGXCommandFinishedInternal(int total_command_count); + +signals: + void GXCommandFinished(int total_command_count); + +private: + int command_count; +}; + +class GPUCommandStreamWidget : public QDockWidget +{ + Q_OBJECT + +public: + GPUCommandStreamWidget(QWidget* parent = 0); + +private: +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 9be982909..79367c3ed 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -19,6 +19,7 @@ #include "debugger/registers.hxx" #include "debugger/callstack.hxx" #include "debugger/ramview.hxx" +#include "debugger/graphics.hxx" #include "core/system.h" #include "core/loader.h" @@ -47,10 +48,15 @@ GMainWindow::GMainWindow() addDockWidget(Qt::RightDockWidgetArea, callstackWidget); callstackWidget->hide(); + graphicsWidget = new GPUCommandStreamWidget(this); + addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); + callstackWidget->hide(); + QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(disasmWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction()); debug_menu->addAction(callstackWidget->toggleViewAction()); + debug_menu->addAction(graphicsWidget->toggleViewAction()); // Set default UI state // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half diff --git a/src/citra_qt/main.hxx b/src/citra_qt/main.hxx index fa122f76e..100bdbd00 100644 --- a/src/citra_qt/main.hxx +++ b/src/citra_qt/main.hxx @@ -10,6 +10,7 @@ class GRenderWindow; class DisassemblerWidget; class RegistersWidget; class CallstackWidget; +class GPUCommandStreamWidget; class GMainWindow : public QMainWindow { @@ -50,6 +51,7 @@ private: DisassemblerWidget* disasmWidget; RegistersWidget* registersWidget; CallstackWidget* callstackWidget; + GPUCommandStreamWidget* graphicsWidget; }; #endif // _CITRA_QT_MAIN_HXX_ From 7c5f1647be2e31cc469d9e1919d31d627f0a75da Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 22:50:33 +0200 Subject: [PATCH 05/12] Rename LCD to GPU. --- src/common/log.h | 2 +- src/common/log_manager.cpp | 2 +- src/core/CMakeLists.txt | 4 ++-- src/core/core.vcxproj | 6 +++--- src/core/core.vcxproj.filters | 6 +++--- src/core/hle/service/gsp.cpp | 16 ++++++++-------- src/core/hw/{lcd.cpp => gpu.cpp} | 18 +++++++++--------- src/core/hw/{lcd.h => gpu.h} | 2 +- src/core/hw/hw.cpp | 16 ++++++++-------- src/core/hw/ndma.cpp | 4 ++-- .../renderer_opengl/renderer_opengl.cpp | 6 +++--- 11 files changed, 41 insertions(+), 41 deletions(-) rename src/core/hw/{lcd.cpp => gpu.cpp} (92%) rename src/core/hw/{lcd.h => gpu.h} (99%) diff --git a/src/common/log.h b/src/common/log.h index 8b39b03a1..d0da68aad 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -60,7 +60,7 @@ enum LOG_TYPE { NDMA, HLE, RENDER, - LCD, + GPU, HW, TIME, NETPLAY, diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 146472888..b4a761c75 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp @@ -65,7 +65,7 @@ LogManager::LogManager() m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); - m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD"); + m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4086b415b..1c1a2eeb3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -42,8 +42,8 @@ set(SRCS core.cpp hle/service/hid.cpp hle/service/service.cpp hle/service/srv.cpp + hw/gpu.cpp hw/hw.cpp - hw/lcd.cpp hw/ndma.cpp) set(HEADERS core.h @@ -88,8 +88,8 @@ set(HEADERS core.h hle/service/hid.h hle/service/service.h hle/service/srv.h + hw/gpu.h hw/hw.h - hw/lcd.h hw/ndma.h) add_library(core STATIC ${SRCS} ${HEADERS}) diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index f271d336e..8a3ad83ea 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -177,8 +177,8 @@ + - @@ -226,8 +226,8 @@ + - @@ -239,4 +239,4 @@ - \ No newline at end of file + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index b6c1d5b93..f7b342f98 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -102,7 +102,7 @@ hw - + hw @@ -244,7 +244,7 @@ hw - + hw @@ -299,4 +299,4 @@ - \ No newline at end of file + diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index a42759053..d51e6c66d 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -10,7 +10,7 @@ #include "core/hle/hle.h" #include "core/hle/service/gsp.h" -#include "core/hw/lcd.h" +#include "core/hw/gpu.h" #include "video_core/gpu_debugger.h" @@ -69,8 +69,8 @@ enum { /// Read a GSP GPU hardware register void ReadHWRegs(Service::Interface* self) { - static const u32 framebuffer_1[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME1, LCD::PADDR_VRAM_TOP_RIGHT_FRAME1}; - static const u32 framebuffer_2[] = {LCD::PADDR_VRAM_TOP_LEFT_FRAME2, LCD::PADDR_VRAM_TOP_RIGHT_FRAME2}; + static const u32 framebuffer_1[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME1, GPU::PADDR_VRAM_TOP_RIGHT_FRAME1}; + static const u32 framebuffer_2[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME2, GPU::PADDR_VRAM_TOP_RIGHT_FRAME2}; u32* cmd_buff = Service::GetCommandBuffer(); u32 reg_addr = cmd_buff[1]; @@ -85,13 +85,13 @@ void ReadHWRegs(Service::Interface* self) { // Top framebuffer 1 addresses case REG_FRAMEBUFFER_1: - LCD::SetFramebufferLocation(LCD::FRAMEBUFFER_LOCATION_VRAM); + GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM); memcpy(dst, framebuffer_1, size); break; // Top framebuffer 2 addresses case REG_FRAMEBUFFER_2: - LCD::SetFramebufferLocation(LCD::FRAMEBUFFER_LOCATION_VRAM); + GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM); memcpy(dst, framebuffer_2, size); break; @@ -123,9 +123,9 @@ void TriggerCmdReqQueue(Service::Interface* self) { break; case GXCommandId::SET_COMMAND_LIST_LAST: - LCD::Write(LCD::CommandListAddress, cmd_buff[1] >> 3); - LCD::Write(LCD::CommandListSize, cmd_buff[2] >> 3); - LCD::Write(LCD::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this + GPU::Write(GPU::CommandListAddress, cmd_buff[1] >> 3); + GPU::Write(GPU::CommandListSize, cmd_buff[2] >> 3); + GPU::Write(GPU::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this break; case GXCommandId::SET_MEMORY_FILL: diff --git a/src/core/hw/lcd.cpp b/src/core/hw/gpu.cpp similarity index 92% rename from src/core/hw/lcd.cpp rename to src/core/hw/gpu.cpp index 61ee99c1f..632e1aaac 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/gpu.cpp @@ -8,12 +8,12 @@ #include "core/core.h" #include "core/mem_map.h" #include "core/hle/kernel/thread.h" -#include "core/hw/lcd.h" +#include "core/hw/gpu.h" #include "video_core/video_core.h" -namespace LCD { +namespace GPU { Registers g_regs; @@ -61,7 +61,7 @@ const FramebufferLocation GetFramebufferLocation() { } else if ((g_regs.framebuffer_top_right_1 & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) { return FRAMEBUFFER_LOCATION_FCRAM; } else { - ERROR_LOG(LCD, "unknown framebuffer location!"); + ERROR_LOG(GPU, "unknown framebuffer location!"); } return FRAMEBUFFER_LOCATION_UNKNOWN; } @@ -78,7 +78,7 @@ const u8* GetFramebufferPointer(const u32 address) { case FRAMEBUFFER_LOCATION_VRAM: return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_VRAM(address)); default: - ERROR_LOG(LCD, "unknown framebuffer location"); + ERROR_LOG(GPU, "unknown framebuffer location"); } return NULL; } @@ -123,7 +123,7 @@ inline void Read(T &var, const u32 addr) { break; default: - ERROR_LOG(LCD, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); + ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); break; } } @@ -144,13 +144,13 @@ inline void Write(u32 addr, const T data) { if (g_regs.command_processing_enabled & 1) { // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3); - ERROR_LOG(LCD, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, 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); // TODO: Process command list! } break; default: - ERROR_LOG(LCD, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); + ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); break; } } @@ -183,12 +183,12 @@ void Update() { void Init() { g_last_ticks = Core::g_app_core->GetTicks(); SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); - NOTICE_LOG(LCD, "initialized OK"); + NOTICE_LOG(GPU, "initialized OK"); } /// Shutdown hardware void Shutdown() { - NOTICE_LOG(LCD, "shutdown OK"); + NOTICE_LOG(GPU, "shutdown OK"); } } // namespace diff --git a/src/core/hw/lcd.h b/src/core/hw/gpu.h similarity index 99% rename from src/core/hw/lcd.h rename to src/core/hw/gpu.h index 2ae852d7c..c81b1bb3f 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/gpu.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -namespace LCD { +namespace GPU { struct Registers { u32 framebuffer_top_left_1; diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index 85669ae7f..ed70486e6 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -6,7 +6,7 @@ #include "common/log.h" #include "core/hw/hw.h" -#include "core/hw/lcd.h" +#include "core/hw/gpu.h" #include "core/hw/ndma.h" namespace HW { @@ -34,7 +34,7 @@ enum { VADDR_CDMA = 0xFFFDA000, // CoreLink DMA-330? Info VADDR_DSP_2 = 0x1ED03000, VADDR_HASH_2 = 0x1EE01000, - VADDR_LCD = 0x1EF00000, + VADDR_GPU = 0x1EF00000, }; template @@ -46,8 +46,8 @@ inline void Read(T &var, const u32 addr) { // NDMA::Read(var, addr); // break; - case VADDR_LCD: - LCD::Read(var, addr); + case VADDR_GPU: + GPU::Read(var, addr); break; default: @@ -64,8 +64,8 @@ inline void Write(u32 addr, const T data) { // NDMA::Write(addr, data); // break; - case VADDR_LCD: - LCD::Write(addr, data); + case VADDR_GPU: + GPU::Write(addr, data); break; default: @@ -87,13 +87,13 @@ template void Write(u32 addr, const u8 data); /// Update hardware void Update() { - LCD::Update(); + GPU::Update(); NDMA::Update(); } /// Initialize hardware void Init() { - LCD::Init(); + GPU::Init(); NDMA::Init(); NOTICE_LOG(HW, "initialized OK"); } diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp index 52e459ebd..f6aa72d16 100644 --- a/src/core/hw/ndma.cpp +++ b/src/core/hw/ndma.cpp @@ -37,12 +37,12 @@ void Update() { /// Initialize hardware void Init() { - NOTICE_LOG(LCD, "initialized OK"); + NOTICE_LOG(GPU, "initialized OK"); } /// Shutdown hardware void Shutdown() { - NOTICE_LOG(LCD, "shutdown OK"); + NOTICE_LOG(GPU, "shutdown OK"); } } // namespace diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index bb5eb34aa..70af47c59 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 // Refer to the license.txt file included. -#include "core/hw/lcd.h" +#include "core/hw/gpu.h" #include "video_core/video_core.h" #include "video_core/renderer_opengl/renderer_opengl.h" @@ -77,8 +77,8 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) { */ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { - FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_top_left_1), m_xfb_top_flipped); - FlipFramebuffer(LCD::GetFramebufferPointer(LCD::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped); + FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_top_left_1), m_xfb_top_flipped); + FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped); // Blit the top framebuffer // ------------------------ From 3240b6ef31ffe60d0f6f6f4b93a6476dd62f2399 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 23:01:58 +0200 Subject: [PATCH 06/12] GPU: Cleanup register definitions. --- src/core/hle/service/gsp.cpp | 6 +++--- src/core/hw/gpu.cpp | 26 +++++++++++++------------- src/core/hw/gpu.h | 30 +++++++++++++++--------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index d51e6c66d..15e9d19a5 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -123,9 +123,9 @@ void TriggerCmdReqQueue(Service::Interface* self) { break; case GXCommandId::SET_COMMAND_LIST_LAST: - GPU::Write(GPU::CommandListAddress, cmd_buff[1] >> 3); - GPU::Write(GPU::CommandListSize, cmd_buff[2] >> 3); - GPU::Write(GPU::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this + GPU::Write(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3); + GPU::Write(GPU::Registers::CommandListSize, cmd_buff[2] >> 3); + GPU::Write(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this break; case GXCommandId::SET_MEMORY_FILL: diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 632e1aaac..ec2d0e156 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -86,39 +86,39 @@ const u8* GetFramebufferPointer(const u32 address) { template inline void Read(T &var, const u32 addr) { switch (addr) { - case REG_FRAMEBUFFER_TOP_LEFT_1: + case Registers::FramebufferTopLeft1: var = g_regs.framebuffer_top_left_1; break; - case REG_FRAMEBUFFER_TOP_LEFT_2: + case Registers::FramebufferTopLeft2: var = g_regs.framebuffer_top_left_2; break; - case REG_FRAMEBUFFER_TOP_RIGHT_1: + case Registers::FramebufferTopRight1: var = g_regs.framebuffer_top_right_1; break; - case REG_FRAMEBUFFER_TOP_RIGHT_2: + case Registers::FramebufferTopRight2: var = g_regs.framebuffer_top_right_2; break; - case REG_FRAMEBUFFER_SUB_LEFT_1: + case Registers::FramebufferSubLeft1: var = g_regs.framebuffer_sub_left_1; break; - case REG_FRAMEBUFFER_SUB_RIGHT_1: + case Registers::FramebufferSubRight1: var = g_regs.framebuffer_sub_right_1; break; - case CommandListSize: + case Registers::CommandListSize: var = g_regs.command_list_size; break; - case CommandListAddress: + case Registers::CommandListAddress: var = g_regs.command_list_address; break; - case ProcessCommandList: + case Registers::ProcessCommandList: var = g_regs.command_processing_enabled; break; @@ -130,16 +130,16 @@ inline void Read(T &var, const u32 addr) { template inline void Write(u32 addr, const T data) { - switch (addr) { - case CommandListSize: + switch (static_cast(addr)) { + case Registers::CommandListSize: g_regs.command_list_size = data; break; - case CommandListAddress: + case Registers::CommandListAddress: g_regs.command_list_address = data; break; - case ProcessCommandList: + case Registers::ProcessCommandList: g_regs.command_processing_enabled = data; if (g_regs.command_processing_enabled & 1) { diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index c81b1bb3f..f26f25e98 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -9,6 +9,21 @@ namespace GPU { struct Registers { + enum Id : u32 { + FramebufferTopLeft1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left + FramebufferTopLeft2 = 0x1EF0046C, // Main LCD, second framebuffer for 3D left + FramebufferTopRight1 = 0x1EF00494, // Main LCD, first framebuffer for 3D right + FramebufferTopRight2 = 0x1EF00498, // Main LCD, second framebuffer for 3D right + FramebufferSubLeft1 = 0x1EF00568, // Sub LCD, first framebuffer + FramebufferSubLeft2 = 0x1EF0056C, // Sub LCD, second framebuffer + FramebufferSubRight1 = 0x1EF00594, // Sub LCD, unused first framebuffer + FramebufferSubRight2 = 0x1EF00598, // Sub LCD, unused second framebuffer + + CommandListSize = 0x1EF018E0, + CommandListAddress = 0x1EF018E8, + ProcessCommandList = 0x1EF018F0, + }; + u32 framebuffer_top_left_1; u32 framebuffer_top_left_2; u32 framebuffer_top_right_1; @@ -52,21 +67,6 @@ enum { PADDR_VRAM_SUB_FRAME2 = 0x18249CF0, }; -enum { - REG_FRAMEBUFFER_TOP_LEFT_1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left - REG_FRAMEBUFFER_TOP_LEFT_2 = 0x1EF0046C, // Main LCD, second framebuffer for 3D left - REG_FRAMEBUFFER_TOP_RIGHT_1 = 0x1EF00494, // Main LCD, first framebuffer for 3D right - REG_FRAMEBUFFER_TOP_RIGHT_2 = 0x1EF00498, // Main LCD, second framebuffer for 3D right - REG_FRAMEBUFFER_SUB_LEFT_1 = 0x1EF00568, // Sub LCD, first framebuffer - REG_FRAMEBUFFER_SUB_LEFT_2 = 0x1EF0056C, // Sub LCD, second framebuffer - REG_FRAMEBUFFER_SUB_RIGHT_1 = 0x1EF00594, // Sub LCD, unused first framebuffer - REG_FRAMEBUFFER_SUB_RIGHT_2 = 0x1EF00598, // Sub LCD, unused second framebuffer - - CommandListSize = 0x1EF018E0, - CommandListAddress = 0x1EF018E8, - ProcessCommandList = 0x1EF018F0, -}; - /// Framebuffer location enum FramebufferLocation { FRAMEBUFFER_LOCATION_UNKNOWN, ///< Framebuffer location is unknown From 930a4a3f9e07ce9f3d085442742f13d84f00e2bf Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 17 May 2014 23:07:51 +0200 Subject: [PATCH 07/12] video core: added PICA definitions file. --- src/video_core/pica.h | 35 +++++++++++++++++++++++ src/video_core/video_core.vcxproj | 1 + src/video_core/video_core.vcxproj.filters | 1 + 3 files changed, 37 insertions(+) create mode 100644 src/video_core/pica.h diff --git a/src/video_core/pica.h b/src/video_core/pica.h new file mode 100644 index 000000000..dab861408 --- /dev/null +++ b/src/video_core/pica.h @@ -0,0 +1,35 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" + +namespace Pica { + +enum class CommandId : u32 +{ + ViewportSizeX = 0x41, + ViewportInvSizeX = 0x42, + ViewportSizeY = 0x43, + ViewportInvSizeY = 0x44, + ViewportCorner = 0x68, + DepthBufferFormat = 0x116, + ColorBufferFormat = 0x117, + DepthBufferAddress = 0x11C, + ColorBufferAddress = 0x11D, + ColorBufferSize = 0x11E, +}; + +union CommandHeader { + u32 hex; + + BitField< 0, 16, CommandId> cmd_id; + BitField<16, 4, u32> parameter_mask; + BitField<20, 11, u32> extra_data_length; + BitField<31, 1, u32> group_commands; +}; + +} diff --git a/src/video_core/video_core.vcxproj b/src/video_core/video_core.vcxproj index a4df92386..d77be2bef 100644 --- a/src/video_core/video_core.vcxproj +++ b/src/video_core/video_core.vcxproj @@ -25,6 +25,7 @@ + diff --git a/src/video_core/video_core.vcxproj.filters b/src/video_core/video_core.vcxproj.filters index cc173a718..b89ac1ac4 100644 --- a/src/video_core/video_core.vcxproj.filters +++ b/src/video_core/video_core.vcxproj.filters @@ -17,6 +17,7 @@ renderer_opengl + From 90fd4e111cc74dd06beba60c653404bf08f757a0 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 18 May 2014 17:28:30 +0200 Subject: [PATCH 08/12] GPU debugger: Add functionality to inspect command lists. --- src/core/hle/service/gsp.cpp | 4 +++ src/video_core/gpu_debugger.h | 54 ++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index 15e9d19a5..aabcb48db 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -126,6 +126,10 @@ void TriggerCmdReqQueue(Service::Interface* self) { GPU::Write(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3); GPU::Write(GPU::Registers::CommandListSize, cmd_buff[2] >> 3); GPU::Write(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this + + // TODO: Move this to GPU + // TODO: Not sure what units the size is measured in + g_debugger.CommandListCalled(cmd_buff[1], (u32*)Memory::GetPointer(cmd_buff[1]), cmd_buff[2]); break; case GXCommandId::SET_MEMORY_FILL: diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index ace9de95f..4dafd3146 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -11,11 +11,23 @@ #include "common/log.h" #include "core/hle/service/gsp.h" - +#include "pica.h" class GraphicsDebugger { public: + // A few utility structs used to expose data + // A vector of commands represented by their raw byte sequence + struct PicaCommand : public std::vector + { + Pica::CommandHeader& GetHeader() + { + return *(Pica::CommandHeader*)&(front()); + } + }; + + typedef std::vector PicaCommandList; + // Base class for all objects which need to be notified about GPU events class DebuggerObserver { @@ -40,6 +52,16 @@ public: ERROR_LOG(GSP, "Received command: id=%x", cmd.id); } + /** + * @param lst command list which triggered this call + * @param is_new true if the command list was called for the first time + * @todo figure out how to make sure called functions don't keep references around beyond their life time + */ + virtual void CommandListCalled(const PicaCommandList& lst, bool is_new) + { + ERROR_LOG(GSP, "Command list called: %d", (int)is_new); + } + protected: GraphicsDebugger* GetDebugger() { @@ -66,12 +88,39 @@ public: } ); } + void CommandListCalled(u32 address, u32* command_list, u32 size_in_words) + { + // TODO: Decoding fun + + // For now, just treating the whole command list as a single command + PicaCommandList cmdlist; + cmdlist.push_back(PicaCommand()); + auto& cmd = cmdlist[0]; + cmd.reserve(size_in_words); + std::copy(command_list, command_list+size_in_words, std::back_inserter(cmd)); + + auto obj = std::pair(address, cmdlist); + auto it = std::find(command_lists.begin(), command_lists.end(), obj); + bool is_new = (it == command_lists.end()); + if (is_new) + command_lists.push_back(obj); + + ForEachObserver([&](DebuggerObserver* observer) { + observer->CommandListCalled(obj.second, is_new); + } ); + } + const GSP_GPU::GXCommand& ReadGXCommandHistory(int index) const { // TODO: Is this thread-safe? return gx_command_history[index]; } + const std::vector>& GetCommandLists() const + { + return command_lists; + } + void RegisterObserver(DebuggerObserver* observer) { // TODO: Check for duplicates @@ -94,4 +143,7 @@ private: std::vector observers; std::vector gx_command_history; + + // vector of pairs of command lists and their storage address + std::vector> command_lists; }; From 9fbfd928a1f5a1c6efd80165350a3f3dd20cadb9 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 18 May 2014 17:52:22 +0200 Subject: [PATCH 09/12] citra-qt: Add command list view. --- src/citra_qt/CMakeLists.txt | 2 + src/citra_qt/citra_qt.vcxproj | 2 + src/citra_qt/citra_qt.vcxproj.filters | 6 ++ src/citra_qt/debugger/graphics_cmdlists.cpp | 65 +++++++++++++++++++++ src/citra_qt/debugger/graphics_cmdlists.hxx | 44 ++++++++++++++ src/citra_qt/main.cpp | 6 ++ src/citra_qt/main.hxx | 2 + src/video_core/gpu_debugger.h | 4 +- 8 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/citra_qt/debugger/graphics_cmdlists.cpp create mode 100644 src/citra_qt/debugger/graphics_cmdlists.hxx diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 1968e34d3..7f880df8b 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -3,6 +3,7 @@ set(SRCS debugger/callstack.cpp debugger/disassembler.cpp debugger/graphics.cpp + debugger/graphics_cmdlists.cpp debugger/ramview.cpp debugger/registers.cpp hotkeys.cpp @@ -40,6 +41,7 @@ qt4_wrap_cpp(MOC_SRCS debugger/callstack.hxx debugger/disassembler.hxx debugger/graphics.hxx + debugger/graphics_cmdlists.hxx debugger/registers.hxx debugger/ramview.hxx hotkeys.hxx diff --git a/src/citra_qt/citra_qt.vcxproj b/src/citra_qt/citra_qt.vcxproj index a1b24f676..c99c8eeee 100644 --- a/src/citra_qt/citra_qt.vcxproj +++ b/src/citra_qt/citra_qt.vcxproj @@ -131,6 +131,7 @@ + @@ -146,6 +147,7 @@ + diff --git a/src/citra_qt/citra_qt.vcxproj.filters b/src/citra_qt/citra_qt.vcxproj.filters index faa4d9f52..903082c3c 100644 --- a/src/citra_qt/citra_qt.vcxproj.filters +++ b/src/citra_qt/citra_qt.vcxproj.filters @@ -42,6 +42,9 @@ debugger + + debugger + debugger @@ -74,6 +77,9 @@ debugger + + debugger + debugger diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp new file mode 100644 index 000000000..576882e8a --- /dev/null +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -0,0 +1,65 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "graphics_cmdlists.hxx" +#include + +extern GraphicsDebugger g_debugger; + +GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent), row_count(0) +{ + connect(this, SIGNAL(CommandListCalled()), this, SLOT(OnCommandListCalledInternal()), Qt::UniqueConnection); +} + +int GPUCommandListModel::rowCount(const QModelIndex& parent) const +{ + return row_count; +} + +QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int idx = index.row(); + if (role == Qt::DisplayRole) + { + QString content; + const GraphicsDebugger::PicaCommandList& cmdlist = command_list[idx].second; + for (int i = 0; i < cmdlist.size(); ++i) + { + const GraphicsDebugger::PicaCommand& cmd = cmdlist[i]; + for (int j = 0; j < cmd.size(); ++j) + content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0'))); + } + return QVariant(content); + } + return QVariant(); +} + +void GPUCommandListModel::OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new) +{ + emit CommandListCalled(); +} + + +void GPUCommandListModel::OnCommandListCalledInternal() +{ + beginResetModel(); + + command_list = GetDebugger()->GetCommandLists(); + row_count = command_list.size(); + + endResetModel(); +} + +GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) +{ + GPUCommandListModel* model = new GPUCommandListModel(this); + g_debugger.RegisterObserver(model); + + QListView* list_widget = new QListView; + list_widget->setModel(model); + setWidget(list_widget); +} diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx new file mode 100644 index 000000000..bac23c643 --- /dev/null +++ b/src/citra_qt/debugger/graphics_cmdlists.hxx @@ -0,0 +1,44 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "video_core/gpu_debugger.h" + +class GPUCommandListModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver +{ + Q_OBJECT + +public: + GPUCommandListModel(QObject* parent); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + +public: + void OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new) override; + +public slots: + void OnCommandListCalledInternal(); + +signals: + void CommandListCalled(); + +private: + int row_count; + std::vector> command_list; +}; + +class GPUCommandListWidget : public QDockWidget +{ + Q_OBJECT + +public: + GPUCommandListWidget(QWidget* parent = 0); + +private: +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 79367c3ed..087716c01 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -20,6 +20,7 @@ #include "debugger/callstack.hxx" #include "debugger/ramview.hxx" #include "debugger/graphics.hxx" +#include "debugger/graphics_cmdlists.hxx" #include "core/system.h" #include "core/loader.h" @@ -52,11 +53,16 @@ GMainWindow::GMainWindow() addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); callstackWidget->hide(); + graphicsCommandsWidget = new GPUCommandListWidget(this); + addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); + callstackWidget->hide(); + QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(disasmWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction()); debug_menu->addAction(callstackWidget->toggleViewAction()); debug_menu->addAction(graphicsWidget->toggleViewAction()); + debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); // Set default UI state // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half diff --git a/src/citra_qt/main.hxx b/src/citra_qt/main.hxx index 100bdbd00..6bcb37a30 100644 --- a/src/citra_qt/main.hxx +++ b/src/citra_qt/main.hxx @@ -11,6 +11,7 @@ class DisassemblerWidget; class RegistersWidget; class CallstackWidget; class GPUCommandStreamWidget; +class GPUCommandListWidget; class GMainWindow : public QMainWindow { @@ -52,6 +53,7 @@ private: RegistersWidget* registersWidget; CallstackWidget* callstackWidget; GPUCommandStreamWidget* graphicsWidget; + GPUCommandListWidget* graphicsCommandsWidget; }; #endif // _CITRA_QT_MAIN_HXX_ diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 4dafd3146..7ad595493 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -57,7 +57,7 @@ public: * @param is_new true if the command list was called for the first time * @todo figure out how to make sure called functions don't keep references around beyond their life time */ - virtual void CommandListCalled(const PicaCommandList& lst, bool is_new) + virtual void OnCommandListCalled(const PicaCommandList& lst, bool is_new) { ERROR_LOG(GSP, "Command list called: %d", (int)is_new); } @@ -106,7 +106,7 @@ public: command_lists.push_back(obj); ForEachObserver([&](DebuggerObserver* observer) { - observer->CommandListCalled(obj.second, is_new); + observer->OnCommandListCalled(obj.second, is_new); } ); } From 8f1c9984722adaa06736ef9f18d8de6ca83f4bb4 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 18 May 2014 19:59:36 +0200 Subject: [PATCH 10/12] Refine command list debugging functionality and its qt interface. --- src/citra_qt/debugger/graphics_cmdlists.cpp | 98 +++++++++++++++++---- src/citra_qt/debugger/graphics_cmdlists.hxx | 28 +++++- src/video_core/gpu_debugger.h | 23 +++-- src/video_core/pica.h | 2 + 4 files changed, 124 insertions(+), 27 deletions(-) diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 576882e8a..d07645e78 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -3,18 +3,57 @@ // Refer to the license.txt file included. #include "graphics_cmdlists.hxx" -#include +#include extern GraphicsDebugger g_debugger; -GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent), row_count(0) +GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractItemModel(parent) { + root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this); + connect(this, SIGNAL(CommandListCalled()), this, SLOT(OnCommandListCalledInternal()), Qt::UniqueConnection); } +QModelIndex GPUCommandListModel::index(int row, int column, const QModelIndex& parent) const +{ + TreeItem* item; + + if (!parent.isValid()) { + item = root_item; + } else { + item = (TreeItem*)parent.internalPointer(); + } + + return createIndex(row, column, item->children[row]); +} + +QModelIndex GPUCommandListModel::parent(const QModelIndex& child) const +{ + if (!child.isValid()) + return QModelIndex(); + + TreeItem* item = (TreeItem*)child.internalPointer(); + + if (item->parent == NULL) + return QModelIndex(); + + return createIndex(item->parent->index, 0, item->parent); +} + int GPUCommandListModel::rowCount(const QModelIndex& parent) const { - return row_count; + TreeItem* item; + if (!parent.isValid()) { + item = root_item; + } else { + item = (TreeItem*)parent.internalPointer(); + } + return item->children.size(); +} + +int GPUCommandListModel::columnCount(const QModelIndex& parent) const +{ + return 1; } QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const @@ -22,19 +61,33 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const if (!index.isValid()) return QVariant(); - int idx = index.row(); - if (role == Qt::DisplayRole) + const TreeItem* item = (const TreeItem*)index.internalPointer(); + + if (item->type == TreeItem::COMMAND_LIST) { - QString content; - const GraphicsDebugger::PicaCommandList& cmdlist = command_list[idx].second; - for (int i = 0; i < cmdlist.size(); ++i) + const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->index].second; + u32 address = command_lists[item->index].first; + + if (role == Qt::DisplayRole) { - const GraphicsDebugger::PicaCommand& cmd = cmdlist[i]; + return QVariant(QString("0x%1 bytes at 0x%2").arg(cmdlist.size(), 0, 16).arg(address, 8, 16, QLatin1Char('0'))); + } + } + else + { + // index refers to a specific command + const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->parent->index].second; + const GraphicsDebugger::PicaCommand& cmd = cmdlist[item->index]; + + if (role == Qt::DisplayRole) { + QString content; for (int j = 0; j < cmd.size(); ++j) content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0'))); + + return QVariant(content); } - return QVariant(content); } + return QVariant(); } @@ -48,8 +101,22 @@ void GPUCommandListModel::OnCommandListCalledInternal() { beginResetModel(); - command_list = GetDebugger()->GetCommandLists(); - row_count = command_list.size(); + command_lists = GetDebugger()->GetCommandLists(); + + // delete root item and rebuild tree + delete root_item; + root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this); + + for (int command_list_idx = 0; command_list_idx < command_lists.size(); ++command_list_idx) { + TreeItem* command_list_item = new TreeItem(TreeItem::COMMAND_LIST, command_list_idx, root_item, root_item); + root_item->children.push_back(command_list_item); + + const GraphicsDebugger::PicaCommandList& command_list = command_lists[command_list_idx].second; + for (int command_idx = 0; command_idx < command_list.size(); ++command_idx) { + TreeItem* command_item = new TreeItem(TreeItem::COMMAND, command_idx, command_list_item, command_list_item); + command_list_item->children.push_back(command_item); + } + } endResetModel(); } @@ -59,7 +126,8 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi GPUCommandListModel* model = new GPUCommandListModel(this); g_debugger.RegisterObserver(model); - QListView* list_widget = new QListView; - list_widget->setModel(model); - setWidget(list_widget); + QTreeView* tree_widget = new QTreeView; + tree_widget->setModel(model); + tree_widget->setFont(QFont("monospace")); + setWidget(tree_widget); } diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx index bac23c643..b4e6e3c8a 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.hxx +++ b/src/citra_qt/debugger/graphics_cmdlists.hxx @@ -4,18 +4,22 @@ #pragma once -#include +#include #include #include "video_core/gpu_debugger.h" -class GPUCommandListModel : public QAbstractListModel, public GraphicsDebugger::DebuggerObserver +// TODO: Rename class, since it's not actually a list model anymore... +class GPUCommandListModel : public QAbstractItemModel, public GraphicsDebugger::DebuggerObserver { Q_OBJECT public: GPUCommandListModel(QObject* parent); + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex& child) const; + int columnCount(const QModelIndex& parent = QModelIndex()) const; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -29,8 +33,24 @@ signals: void CommandListCalled(); private: - int row_count; - std::vector> command_list; + struct TreeItem : public QObject + { + enum Type { + ROOT, + COMMAND_LIST, + COMMAND + }; + + TreeItem(Type type, int index, TreeItem* item_parent, QObject* parent) : QObject(parent), type(type), index(index), parent(item_parent) {} + + Type type; + int index; + std::vector children; + TreeItem* parent; + }; + + std::vector> command_lists; + TreeItem* root_item; }; class GPUCommandListWidget : public QDockWidget diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 7ad595493..5ece0a8b7 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -22,7 +22,8 @@ public: { Pica::CommandHeader& GetHeader() { - return *(Pica::CommandHeader*)&(front()); + u32& val = at(1); + return *(Pica::CommandHeader*)&val; } }; @@ -90,14 +91,20 @@ public: void CommandListCalled(u32 address, u32* command_list, u32 size_in_words) { - // TODO: Decoding fun - - // For now, just treating the whole command list as a single command PicaCommandList cmdlist; - cmdlist.push_back(PicaCommand()); - auto& cmd = cmdlist[0]; - cmd.reserve(size_in_words); - std::copy(command_list, command_list+size_in_words, std::back_inserter(cmd)); + for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;) + { + const Pica::CommandHeader header = static_cast(parse_pointer[1]); + + cmdlist.push_back(PicaCommand()); + auto& cmd = cmdlist.back(); + + size_t size = 2 + header.extra_data_length; + cmd.reserve(size); + std::copy(parse_pointer, parse_pointer + size, std::back_inserter(cmd)); + + parse_pointer += size; + } auto obj = std::pair(address, cmdlist); auto it = std::find(command_lists.begin(), command_lists.end(), obj); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index dab861408..8ebe0dc3c 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -24,6 +24,8 @@ enum class CommandId : u32 }; union CommandHeader { + CommandHeader(u32 h) : hex(h) {} + u32 hex; BitField< 0, 16, CommandId> cmd_id; From 2cf87fb10a4fc10921e9e7a5f2ce1738589f2a82 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 18 May 2014 21:18:38 +0200 Subject: [PATCH 11/12] Further refine GPU command list debugging. --- src/citra_qt/debugger/graphics_cmdlists.cpp | 14 ++++++++++---- src/video_core/gpu_debugger.h | 1 + src/video_core/pica.h | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index d07645e78..195197ef5 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -53,7 +53,7 @@ int GPUCommandListModel::rowCount(const QModelIndex& parent) const int GPUCommandListModel::columnCount(const QModelIndex& parent) const { - return 1; + return 2; } QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const @@ -68,7 +68,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->index].second; u32 address = command_lists[item->index].first; - if (role == Qt::DisplayRole) + if (role == Qt::DisplayRole && index.column() == 0) { return QVariant(QString("0x%1 bytes at 0x%2").arg(cmdlist.size(), 0, 16).arg(address, 8, 16, QLatin1Char('0'))); } @@ -78,11 +78,17 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const // index refers to a specific command const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->parent->index].second; const GraphicsDebugger::PicaCommand& cmd = cmdlist[item->index]; + const Pica::CommandHeader& header = cmd.GetHeader(); if (role == Qt::DisplayRole) { QString content; - for (int j = 0; j < cmd.size(); ++j) - content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0'))); + if (index.column() == 0) { + content = Pica::command_names[header.cmd_id]; + content.append(" "); + } else if (index.column() == 1) { + for (int j = 0; j < cmd.size(); ++j) + content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0'))); + } return QVariant(content); } diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 5ece0a8b7..6a1e04244 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -100,6 +100,7 @@ public: auto& cmd = cmdlist.back(); size_t size = 2 + header.extra_data_length; + size = (size + 1) / 2 * 2; // align to 8 bytes cmd.reserve(size); std::copy(parse_pointer, parse_pointer + size, std::back_inserter(cmd)); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 8ebe0dc3c..7cbc45f98 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -4,6 +4,9 @@ #pragma once +#include +#include + #include "common/bit_field.h" #include "common/common_types.h" @@ -34,4 +37,17 @@ union CommandHeader { BitField<31, 1, u32> group_commands; }; +static std::map command_names = { + {CommandId::ViewportSizeX, "ViewportSizeX" }, + {CommandId::ViewportInvSizeX, "ViewportInvSizeX" }, + {CommandId::ViewportSizeY, "ViewportSizeY" }, + {CommandId::ViewportInvSizeY, "ViewportInvSizeY" }, + {CommandId::ViewportCorner, "ViewportCorner" }, + {CommandId::DepthBufferFormat, "DepthBufferFormat" }, + {CommandId::ColorBufferFormat, "ColorBufferFormat" }, + {CommandId::DepthBufferAddress, "DepthBufferAddress" }, + {CommandId::ColorBufferAddress, "ColorBufferAddress" }, + {CommandId::ColorBufferSize, "ColorBufferSize" }, +}; + } From d8148eaa6b9dbb81bc3d71f07663ed929e4d577e Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 18 May 2014 22:50:41 +0200 Subject: [PATCH 12/12] Pica: Use some template magic to define register structures efficiently. --- src/common/common.vcxproj | 3 +- src/common/common.vcxproj.filters | 5 +- src/common/register_set.h | 161 ++++++++++++++++++++++++++++++ src/video_core/pica.h | 127 ++++++++++++++++++----- 4 files changed, 268 insertions(+), 28 deletions(-) create mode 100644 src/common/register_set.h diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 86295a480..1f5c714c3 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -182,6 +182,7 @@ + @@ -221,4 +222,4 @@ - \ No newline at end of file + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 84cfa8837..e8c4ce360 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -4,6 +4,7 @@ + @@ -28,6 +29,7 @@ + @@ -39,7 +41,6 @@ - @@ -65,4 +66,4 @@ - \ No newline at end of file + diff --git a/src/common/register_set.h b/src/common/register_set.h new file mode 100644 index 000000000..0418551b3 --- /dev/null +++ b/src/common/register_set.h @@ -0,0 +1,161 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +// Copyright 2014 Tony Wasserka +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the owner nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + * Standardized way to define a group of registers and corresponding data structures. To define + * a new register set, first define struct containing an enumeration called "Id" containing + * all register IDs and a template union called "Struct". Specialize the Struct union for any + * register ID which needs to be accessed in a specialized way. You can then declare the object + * containing all register values using the RegisterSet type, where + * BaseType is the underlying type of each register (e.g. u32). + * Of course, you'll usually want to implement the Struct template such that they are of the same + * size as BaseType. However, it's also possible to make it larger, e.g. when you want to describe + * multiple registers with the same structure. + * + * Example: + * + * struct Regs { + * enum Id : u32 { + * Value1 = 0, + * Value2 = 1, + * Value3 = 2, + * NumIds = 3 + * }; + * + * // declare register definition structures + * template + * union Struct; + * }; + * + * // Define register set object + * RegisterSet registers; + * + * // define register definition structures + * template<> + * union Regs::Struct { + * BitField<0, 4, u32> some_field; + * BitField<4, 3, u32> some_other_field; + * }; + * + * Usage in external code (within SomeNamespace scope): + * + * For a register which maps to a single index: + * registers.Get().some_field = some_value; + * + * For a register which maps to different indices, e.g. a group of similar registers + * registers.Get(index).some_field = some_value; + * + * + * @tparam BaseType Base type used for storing individual registers, e.g. u32 + * @tparam RegDefinition Class defining an enumeration called "Id" and a template union, as described above. + * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated. + */ +template +struct RegisterSet { + // Register IDs + using Id = typename RegDefinition::Id; + + // type used for *this + using ThisType = RegisterSet; + + // Register definition structs, defined in RegDefinition + template + using Struct = typename RegDefinition::template Struct; + + + /* + * Lookup register with the given id and return it as the corresponding structure type. + * @note This just forwards the arguments to Get(Id). + */ + template + const Struct& Get() const { + return Get(id); + } + + /* + * Lookup register with the given id and return it as the corresponding structure type. + * @note This just forwards the arguments to Get(Id). + */ + template + Struct& Get() { + return Get(id); + } + + /* + * Lookup register with the given index and return it as the corresponding structure type. + * @todo Is this portable with regards to structures larger than BaseType? + * @note if index==id, you don't need to specify the function parameter. + */ + template + const Struct& Get(const Id& index) const { + const int idx = static_cast(index); + return *reinterpret_cast*>(&raw[idx]); + } + + /* + * Lookup register with the given index and return it as the corresponding structure type. + * @note This just forwards the arguments to the const version of Get(Id). + * @note if index==id, you don't need to specify the function parameter. + */ + template + Struct& Get(const Id& index) { + return const_cast&>(GetThis().Get(index)); + } + + /* + * Plain array access. + * @note If you want to have this casted to a register defininition struct, use Get() instead. + */ + const BaseType& operator[] (const Id& id) const { + return raw[static_cast(id)]; + } + + /* + * Plain array access. + * @note If you want to have this casted to a register defininition struct, use Get() instead. + * @note This operator just forwards its argument to the const version. + */ + BaseType& operator[] (const Id& id) { + return const_cast(GetThis()[id]); + } + +private: + /* + * Returns a const reference to "this". + */ + const ThisType& GetThis() const { + return static_cast(*this); + } + + BaseType raw[Id::NumIds]; +}; diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 7cbc45f98..f0fa3aba9 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -9,45 +9,122 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "common/register_set.h" namespace Pica { -enum class CommandId : u32 -{ - ViewportSizeX = 0x41, - ViewportInvSizeX = 0x42, - ViewportSizeY = 0x43, - ViewportInvSizeY = 0x44, - ViewportCorner = 0x68, - DepthBufferFormat = 0x116, - ColorBufferFormat = 0x117, - DepthBufferAddress = 0x11C, - ColorBufferAddress = 0x11D, - ColorBufferSize = 0x11E, +struct Regs { + enum Id : u32 { + ViewportSizeX = 0x41, + ViewportInvSizeX = 0x42, + ViewportSizeY = 0x43, + ViewportInvSizeY = 0x44, + ViewportCorner = 0x68, + DepthBufferFormat = 0x116, + ColorBufferFormat = 0x117, + DepthBufferAddress = 0x11C, + ColorBufferAddress = 0x11D, + ColorBufferSize = 0x11E, + + VertexArrayBaseAddr = 0x200, + VertexDescriptor = 0x201, // 0x202 + VertexAttributeOffset = 0x203, // 0x206,0x209,0x20C,0x20F,0x212,0x215,0x218,0x21B,0x21E,0x221,0x224 + VertexAttributeInfo0 = 0x204, // 0x207,0x20A,0x20D,0x210,0x213,0x216,0x219,0x21C,0x21F,0x222,0x225 + VertexAttributeInfo1 = 0x205, // 0x208,0x20B,0x20E,0x211,0x214,0x217,0x21A,0x21D,0x220,0x223,0x226 + + NumIds = 0x300, + }; + + template + union Struct; }; +static inline Regs::Id VertexAttributeOffset(int n) +{ + return static_cast(0x203 + 3*n); +} + +static inline Regs::Id VertexAttributeInfo0(int n) +{ + return static_cast(0x204 + 3*n); +} + +static inline Regs::Id VertexAttributeInfo1(int n) +{ + return static_cast(0x205 + 3*n); +} + union CommandHeader { CommandHeader(u32 h) : hex(h) {} u32 hex; - BitField< 0, 16, CommandId> cmd_id; + BitField< 0, 16, Regs::Id> cmd_id; BitField<16, 4, u32> parameter_mask; BitField<20, 11, u32> extra_data_length; BitField<31, 1, u32> group_commands; }; -static std::map command_names = { - {CommandId::ViewportSizeX, "ViewportSizeX" }, - {CommandId::ViewportInvSizeX, "ViewportInvSizeX" }, - {CommandId::ViewportSizeY, "ViewportSizeY" }, - {CommandId::ViewportInvSizeY, "ViewportInvSizeY" }, - {CommandId::ViewportCorner, "ViewportCorner" }, - {CommandId::DepthBufferFormat, "DepthBufferFormat" }, - {CommandId::ColorBufferFormat, "ColorBufferFormat" }, - {CommandId::DepthBufferAddress, "DepthBufferAddress" }, - {CommandId::ColorBufferAddress, "ColorBufferAddress" }, - {CommandId::ColorBufferSize, "ColorBufferSize" }, +static std::map command_names = { + {Regs::ViewportSizeX, "ViewportSizeX" }, + {Regs::ViewportInvSizeX, "ViewportInvSizeX" }, + {Regs::ViewportSizeY, "ViewportSizeY" }, + {Regs::ViewportInvSizeY, "ViewportInvSizeY" }, + {Regs::ViewportCorner, "ViewportCorner" }, + {Regs::DepthBufferFormat, "DepthBufferFormat" }, + {Regs::ColorBufferFormat, "ColorBufferFormat" }, + {Regs::DepthBufferAddress, "DepthBufferAddress" }, + {Regs::ColorBufferAddress, "ColorBufferAddress" }, + {Regs::ColorBufferSize, "ColorBufferSize" }, }; -} +template<> +union Regs::Struct { + BitField<0, 24, u32> value; +}; + +template<> +union Regs::Struct { + BitField<0, 24, u32> value; +}; + +template<> +union Regs::Struct { + enum class Format : u64 { + BYTE = 0, + UBYTE = 1, + SHORT = 2, + FLOAT = 3, + }; + + BitField< 0, 2, Format> format0; + BitField< 2, 2, u64> size0; // number of elements minus 1 + BitField< 4, 2, Format> format1; + BitField< 6, 2, u64> size1; + BitField< 8, 2, Format> format2; + BitField<10, 2, u64> size2; + BitField<12, 2, Format> format3; + BitField<14, 2, u64> size3; + BitField<16, 2, Format> format4; + BitField<18, 2, u64> size4; + BitField<20, 2, Format> format5; + BitField<22, 2, u64> size5; + BitField<24, 2, Format> format6; + BitField<26, 2, u64> size6; + BitField<28, 2, Format> format7; + BitField<30, 2, u64> size7; + BitField<32, 2, Format> format8; + BitField<34, 2, u64> size8; + BitField<36, 2, Format> format9; + BitField<38, 2, u64> size9; + BitField<40, 2, Format> format10; + BitField<42, 2, u64> size10; + BitField<44, 2, Format> format11; + BitField<46, 2, u64> size11; + + BitField<48, 12, u64> attribute_mask; + BitField<60, 4, u64> num_attributes; // number of total attributes minus 1 +}; + + +} // namespace