summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp144
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.hxx42
-rw-r--r--src/citra_qt/main.cpp4
-rw-r--r--src/core/hle/service/gsp.cpp5
-rw-r--r--src/video_core/command_processor.cpp2
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp55
-rw-r--r--src/video_core/debug_utils/debug_utils.h21
-rw-r--r--src/video_core/gpu_debugger.h63
8 files changed, 142 insertions, 194 deletions
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index e98560a19..71dd166cd 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -2,53 +2,21 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "graphics_cmdlists.hxx" 5#include <QListView>
6#include <QPushButton>
7#include <QVBoxLayout>
6#include <QTreeView> 8#include <QTreeView>
7 9
8extern GraphicsDebugger g_debugger; 10#include "graphics_cmdlists.hxx"
9
10GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractItemModel(parent)
11{
12 root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this);
13
14 connect(this, SIGNAL(CommandListCalled()), this, SLOT(OnCommandListCalledInternal()), Qt::UniqueConnection);
15}
16
17QModelIndex GPUCommandListModel::index(int row, int column, const QModelIndex& parent) const
18{
19 TreeItem* item;
20
21 if (!parent.isValid()) {
22 item = root_item;
23 } else {
24 item = (TreeItem*)parent.internalPointer();
25 }
26
27 return createIndex(row, column, item->children[row]);
28}
29 11
30QModelIndex GPUCommandListModel::parent(const QModelIndex& child) const 12GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent)
31{ 13{
32 if (!child.isValid())
33 return QModelIndex();
34
35 TreeItem* item = (TreeItem*)child.internalPointer();
36
37 if (item->parent == NULL)
38 return QModelIndex();
39 14
40 return createIndex(item->parent->index, 0, item->parent);
41} 15}
42 16
43int GPUCommandListModel::rowCount(const QModelIndex& parent) const 17int GPUCommandListModel::rowCount(const QModelIndex& parent) const
44{ 18{
45 TreeItem* item; 19 return pica_trace.writes.size();
46 if (!parent.isValid()) {
47 item = root_item;
48 } else {
49 item = (TreeItem*)parent.internalPointer();
50 }
51 return item->children.size();
52} 20}
53 21
54int GPUCommandListModel::columnCount(const QModelIndex& parent) const 22int GPUCommandListModel::columnCount(const QModelIndex& parent) const
@@ -61,79 +29,67 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
61 if (!index.isValid()) 29 if (!index.isValid())
62 return QVariant(); 30 return QVariant();
63 31
64 const TreeItem* item = (const TreeItem*)index.internalPointer(); 32 const auto& writes = pica_trace.writes;
65 33 const Pica::CommandProcessor::CommandHeader cmd{writes[index.row()].Id()};
66 if (item->type == TreeItem::COMMAND_LIST) 34 const u32 val{writes[index.row()].Value()};
67 { 35
68 const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->index].second; 36 if (role == Qt::DisplayRole) {
69 u32 address = command_lists[item->index].first; 37 QString content;
70 38 if (index.column() == 0) {
71 if (role == Qt::DisplayRole && index.column() == 0) 39 content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
72 { 40 content.append(" ");
73 return QVariant(QString("0x%1 bytes at 0x%2").arg(cmdlist.size(), 0, 16).arg(address, 8, 16, QLatin1Char('0'))); 41 } else if (index.column() == 1) {
74 } 42 content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')));
75 } 43 content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0')));
76 else
77 {
78 // index refers to a specific command
79 const GraphicsDebugger::PicaCommandList& cmdlist = command_lists[item->parent->index].second;
80 const GraphicsDebugger::PicaCommand& cmd = cmdlist[item->index];
81 const Pica::CommandProcessor::CommandHeader& header = cmd.GetHeader();
82
83 if (role == Qt::DisplayRole) {
84 QString content;
85 if (index.column() == 0) {
86 content = QString::fromLatin1(Pica::Regs::GetCommandName(header.cmd_id).c_str());
87 content.append(" ");
88 } else if (index.column() == 1) {
89 for (int j = 0; j < cmd.size(); ++j)
90 content.append(QString("%1 ").arg(cmd[j], 8, 16, QLatin1Char('0')));
91 }
92
93 return QVariant(content);
94 } 44 }
45
46 return QVariant(content);
95 } 47 }
96 48
97 return QVariant(); 49 return QVariant();
98} 50}
99 51
100void GPUCommandListModel::OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new) 52void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace)
101{ 53{
102 emit CommandListCalled(); 54 beginResetModel();
55
56 pica_trace = trace;
57
58 endResetModel();
103} 59}
104 60
105 61
106void GPUCommandListModel::OnCommandListCalledInternal() 62GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent)
107{ 63{
108 beginResetModel(); 64 GPUCommandListModel* model = new GPUCommandListModel(this);
109 65
110 command_lists = GetDebugger()->GetCommandLists(); 66 QWidget* main_widget = new QWidget;
111 67
112 // delete root item and rebuild tree 68 QTreeView* list_widget = new QTreeView;
113 delete root_item; 69 list_widget->setModel(model);
114 root_item = new TreeItem(TreeItem::ROOT, 0, NULL, this); 70 list_widget->setFont(QFont("monospace"));
71 list_widget->setRootIsDecorated(false);
115 72
116 for (int command_list_idx = 0; command_list_idx < command_lists.size(); ++command_list_idx) { 73 QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing"));
117 TreeItem* command_list_item = new TreeItem(TreeItem::COMMAND_LIST, command_list_idx, root_item, root_item);
118 root_item->children.push_back(command_list_item);
119 74
120 const GraphicsDebugger::PicaCommandList& command_list = command_lists[command_list_idx].second; 75 connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
121 for (int command_idx = 0; command_idx < command_list.size(); ++command_idx) { 76 connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
122 TreeItem* command_item = new TreeItem(TreeItem::COMMAND, command_idx, command_list_item, command_list_item); 77 model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
123 command_list_item->children.push_back(command_item);
124 }
125 }
126 78
127 endResetModel(); 79 QVBoxLayout* main_layout = new QVBoxLayout;
80 main_layout->addWidget(list_widget);
81 main_layout->addWidget(toggle_tracing);
82 main_widget->setLayout(main_layout);
83
84 setWidget(main_widget);
128} 85}
129 86
130GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) 87void GPUCommandListWidget::OnToggleTracing()
131{ 88{
132 GPUCommandListModel* model = new GPUCommandListModel(this); 89 if (!Pica::DebugUtils::IsPicaTracing()) {
133 g_debugger.RegisterObserver(model); 90 Pica::DebugUtils::StartPicaTracing();
134 91 } else {
135 QTreeView* tree_widget = new QTreeView; 92 pica_trace = Pica::DebugUtils::FinishPicaTracing();
136 tree_widget->setModel(model); 93 emit TracingFinished(*pica_trace);
137 tree_widget->setFont(QFont("monospace")); 94 }
138 setWidget(tree_widget);
139} 95}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx
index b4e6e3c8a..479ef0326 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.hxx
+++ b/src/citra_qt/debugger/graphics_cmdlists.hxx
@@ -4,53 +4,28 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QAbstractItemModel> 7#include <QAbstractListModel>
8#include <QDockWidget> 8#include <QDockWidget>
9 9
10#include "video_core/gpu_debugger.h" 10#include "video_core/gpu_debugger.h"
11#include "video_core/debug_utils/debug_utils.h"
11 12
12// TODO: Rename class, since it's not actually a list model anymore... 13class GPUCommandListModel : public QAbstractListModel
13class GPUCommandListModel : public QAbstractItemModel, public GraphicsDebugger::DebuggerObserver
14{ 14{
15 Q_OBJECT 15 Q_OBJECT
16 16
17public: 17public:
18 GPUCommandListModel(QObject* parent); 18 GPUCommandListModel(QObject* parent);
19 19
20 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
21 QModelIndex parent(const QModelIndex& child) const;
22 int columnCount(const QModelIndex& parent = QModelIndex()) const; 20 int columnCount(const QModelIndex& parent = QModelIndex()) const;
23 int rowCount(const QModelIndex& parent = QModelIndex()) const override; 21 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
24 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 22 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
25 23
26public:
27 void OnCommandListCalled(const GraphicsDebugger::PicaCommandList& lst, bool is_new) override;
28
29public slots: 24public slots:
30 void OnCommandListCalledInternal(); 25 void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
31
32signals:
33 void CommandListCalled();
34 26
35private: 27private:
36 struct TreeItem : public QObject 28 Pica::DebugUtils::PicaTrace pica_trace;
37 {
38 enum Type {
39 ROOT,
40 COMMAND_LIST,
41 COMMAND
42 };
43
44 TreeItem(Type type, int index, TreeItem* item_parent, QObject* parent) : QObject(parent), type(type), index(index), parent(item_parent) {}
45
46 Type type;
47 int index;
48 std::vector<TreeItem*> children;
49 TreeItem* parent;
50 };
51
52 std::vector<std::pair<u32,GraphicsDebugger::PicaCommandList>> command_lists;
53 TreeItem* root_item;
54}; 29};
55 30
56class GPUCommandListWidget : public QDockWidget 31class GPUCommandListWidget : public QDockWidget
@@ -60,5 +35,12 @@ class GPUCommandListWidget : public QDockWidget
60public: 35public:
61 GPUCommandListWidget(QWidget* parent = 0); 36 GPUCommandListWidget(QWidget* parent = 0);
62 37
38public slots:
39 void OnToggleTracing();
40
41signals:
42 void TracingFinished(const Pica::DebugUtils::PicaTrace&);
43
63private: 44private:
45 std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
64}; 46};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 9a16cf92d..a6b87f781 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -52,11 +52,11 @@ GMainWindow::GMainWindow()
52 52
53 graphicsWidget = new GPUCommandStreamWidget(this); 53 graphicsWidget = new GPUCommandStreamWidget(this);
54 addDockWidget(Qt::RightDockWidgetArea, graphicsWidget); 54 addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
55 callstackWidget->hide(); 55 graphicsWidget ->hide();
56 56
57 graphicsCommandsWidget = new GPUCommandListWidget(this); 57 graphicsCommandsWidget = new GPUCommandListWidget(this);
58 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); 58 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
59 callstackWidget->hide(); 59 graphicsCommandsWidget->hide();
60 60
61 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); 61 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
62 debug_menu->addAction(disasmWidget->toggleViewAction()); 62 debug_menu->addAction(disasmWidget->toggleViewAction());
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index 027ba5a37..46c5a8ddd 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -230,11 +230,6 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
230 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though 230 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
231 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); 231 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
232 232
233 // TODO: Move this to GPU
234 // TODO: Not sure what units the size is measured in
235 g_debugger.CommandListCalled(params.address,
236 (u32*)Memory::GetPointer(params.address),
237 params.size);
238 SignalInterrupt(InterruptId::P3D); 233 SignalInterrupt(InterruptId::P3D);
239 break; 234 break;
240 } 235 }
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index f7a412bc1..76fdb4e4a 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -33,6 +33,8 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
33 u32 old_value = registers[id]; 33 u32 old_value = registers[id];
34 registers[id] = (old_value & ~mask) | (value & mask); 34 registers[id] = (old_value & ~mask) | (value & mask);
35 35
36 DebugUtils::OnPicaRegWrite(id, registers[id]);
37
36 switch(id) { 38 switch(id) {
37 // It seems like these trigger vertex rendering 39 // It seems like these trigger vertex rendering
38 case PICA_REG_INDEX(trigger_draw): 40 case PICA_REG_INDEX(trigger_draw):
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index f41249eac..1bbc0330c 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <fstream> 6#include <fstream>
7#include <mutex>
7#include <string> 8#include <string>
8 9
9#include "video_core/pica.h" 10#include "video_core/pica.h"
@@ -260,6 +261,60 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data
260 } 261 }
261} 262}
262 263
264static std::unique_ptr<PicaTrace> pica_trace;
265static std::mutex pica_trace_mutex;
266static int is_pica_tracing = false;
267
268void StartPicaTracing()
269{
270 if (is_pica_tracing) {
271 ERROR_LOG(GPU, "StartPicaTracing called even though tracing already running!");
272 return;
273 }
274
275 pica_trace_mutex.lock();
276 pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace);
277
278 is_pica_tracing = true;
279 pica_trace_mutex.unlock();
280}
281
282bool IsPicaTracing()
283{
284 return is_pica_tracing;
285}
286
287void OnPicaRegWrite(u32 id, u32 value)
288{
289 // Double check for is_pica_tracing to avoid pointless locking overhead
290 if (!is_pica_tracing)
291 return;
292
293 std::unique_lock<std::mutex> lock(pica_trace_mutex);
294
295 if (!is_pica_tracing)
296 return;
297
298 pica_trace->writes.push_back({id, value});
299}
300
301std::unique_ptr<PicaTrace> FinishPicaTracing()
302{
303 if (!is_pica_tracing) {
304 ERROR_LOG(GPU, "FinishPicaTracing called even though tracing already running!");
305 return {};
306 }
307
308 // signalize that no further tracing should be performed
309 is_pica_tracing = false;
310
311 // Wait until running tracing is finished
312 pica_trace_mutex.lock();
313 std::unique_ptr<PicaTrace> ret(std::move(pica_trace));
314 pica_trace_mutex.unlock();
315 return std::move(ret);
316}
317
263} // namespace 318} // namespace
264 319
265} // namespace 320} // namespace
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index bd7a0a89b..023500066 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
8#include <vector> 9#include <vector>
9 10
10#include "video_core/pica.h" 11#include "video_core/pica.h"
@@ -38,6 +39,26 @@ private:
38void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, 39void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size,
39 u32 main_offset, const Regs::VSOutputAttributes* output_attributes); 40 u32 main_offset, const Regs::VSOutputAttributes* output_attributes);
40 41
42
43// Utility class to log Pica commands.
44struct PicaTrace {
45 struct Write : public std::pair<u32,u32> {
46 Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {}
47
48 u32& Id() { return first; }
49 const u32& Id() const { return first; }
50
51 u32& Value() { return second; }
52 const u32& Value() const { return second; }
53 };
54 std::vector<Write> writes;
55};
56
57void StartPicaTracing();
58bool IsPicaTracing();
59void OnPicaRegWrite(u32 id, u32 value);
60std::unique_ptr<PicaTrace> FinishPicaTracing();
61
41} // namespace 62} // namespace
42 63
43} // namespace 64} // namespace
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h
index 2ba873457..5a81fcfcb 100644
--- a/src/video_core/gpu_debugger.h
+++ b/src/video_core/gpu_debugger.h
@@ -18,19 +18,6 @@
18class GraphicsDebugger 18class GraphicsDebugger
19{ 19{
20public: 20public:
21 // A few utility structs used to expose data
22 // A vector of commands represented by their raw byte sequence
23 struct PicaCommand : public std::vector<u32>
24 {
25 const Pica::CommandProcessor::CommandHeader& GetHeader() const
26 {
27 const u32& val = at(1);
28 return *(Pica::CommandProcessor::CommandHeader*)&val;
29 }
30 };
31
32 typedef std::vector<PicaCommand> PicaCommandList;
33
34 // Base class for all objects which need to be notified about GPU events 21 // Base class for all objects which need to be notified about GPU events
35 class DebuggerObserver 22 class DebuggerObserver
36 { 23 {
@@ -55,16 +42,6 @@ public:
55 ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value()); 42 ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value());
56 } 43 }
57 44
58 /**
59 * @param lst command list which triggered this call
60 * @param is_new true if the command list was called for the first time
61 * @todo figure out how to make sure called functions don't keep references around beyond their life time
62 */
63 virtual void OnCommandListCalled(const PicaCommandList& lst, bool is_new)
64 {
65 ERROR_LOG(GSP, "Command list called: %d", (int)is_new);
66 }
67
68 protected: 45 protected:
69 const GraphicsDebugger* GetDebugger() const 46 const GraphicsDebugger* GetDebugger() const
70 { 47 {
@@ -93,49 +70,12 @@ public:
93 } ); 70 } );
94 } 71 }
95 72
96 void CommandListCalled(u32 address, u32* command_list, u32 size_in_words)
97 {
98 if (observers.empty())
99 return;
100
101 PicaCommandList cmdlist;
102 for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;)
103 {
104 const Pica::CommandProcessor::CommandHeader& header = *(Pica::CommandProcessor::CommandHeader*)(&parse_pointer[1]);
105
106 cmdlist.push_back(PicaCommand());
107 auto& cmd = cmdlist.back();
108
109 size_t size = 2 + header.extra_data_length;
110 size = (size + 1) / 2 * 2; // align to 8 bytes
111 cmd.reserve(size);
112 std::copy(parse_pointer, parse_pointer + size, std::back_inserter(cmd));
113
114 parse_pointer += size;
115 }
116
117 auto obj = std::pair<u32,PicaCommandList>(address, cmdlist);
118 auto it = std::find(command_lists.begin(), command_lists.end(), obj);
119 bool is_new = (it == command_lists.end());
120 if (is_new)
121 command_lists.push_back(obj);
122
123 ForEachObserver([&](DebuggerObserver* observer) {
124 observer->OnCommandListCalled(obj.second, is_new);
125 } );
126 }
127
128 const GSP_GPU::Command& ReadGXCommandHistory(int index) const 73 const GSP_GPU::Command& ReadGXCommandHistory(int index) const
129 { 74 {
130 // TODO: Is this thread-safe? 75 // TODO: Is this thread-safe?
131 return gx_command_history[index]; 76 return gx_command_history[index];
132 } 77 }
133 78
134 const std::vector<std::pair<u32,PicaCommandList>>& GetCommandLists() const
135 {
136 return command_lists;
137 }
138
139 void RegisterObserver(DebuggerObserver* observer) 79 void RegisterObserver(DebuggerObserver* observer)
140 { 80 {
141 // TODO: Check for duplicates 81 // TODO: Check for duplicates
@@ -158,7 +98,4 @@ private:
158 std::vector<DebuggerObserver*> observers; 98 std::vector<DebuggerObserver*> observers;
159 99
160 std::vector<GSP_GPU::Command> gx_command_history; 100 std::vector<GSP_GPU::Command> gx_command_history;
161
162 // vector of pairs of command lists and their storage address
163 std::vector<std::pair<u32,PicaCommandList>> command_lists;
164}; 101};