summaryrefslogtreecommitdiff
path: root/src/citra_qt/debugger/graphics_vertex_shader.cpp
diff options
context:
space:
mode:
authorGravatar Tony Wasserka2014-12-10 19:24:56 +0100
committerGravatar Tony Wasserka2015-02-11 15:40:45 +0100
commit12a5cd1d65487124b7878fbffe43d4ad3755263e (patch)
tree28cc580ee4669f1a43567e98e440a9f2b98fc1c5 /src/citra_qt/debugger/graphics_vertex_shader.cpp
parentPica/DebugUtils: Factor out BreakPointObserverDock into its own file. (diff)
downloadyuzu-12a5cd1d65487124b7878fbffe43d4ad3755263e.tar.gz
yuzu-12a5cd1d65487124b7878fbffe43d4ad3755263e.tar.xz
yuzu-12a5cd1d65487124b7878fbffe43d4ad3755263e.zip
citra-qt: Add a vertex shader debugger.
Diffstat (limited to 'src/citra_qt/debugger/graphics_vertex_shader.cpp')
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
new file mode 100644
index 000000000..06eaf0bf0
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -0,0 +1,298 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <iomanip>
6#include <sstream>
7
8#include <QBoxLayout>
9#include <QTreeView>
10
11#include "video_core/vertex_shader.h"
12
13#include "graphics_vertex_shader.h"
14
15using nihstro::Instruction;
16using nihstro::SourceRegister;
17using nihstro::SwizzlePattern;
18
19GraphicsVertexShaderModel::GraphicsVertexShaderModel(QObject* parent): QAbstractItemModel(parent) {
20
21}
22
23QModelIndex GraphicsVertexShaderModel::index(int row, int column, const QModelIndex& parent) const {
24 return createIndex(row, column);
25}
26
27QModelIndex GraphicsVertexShaderModel::parent(const QModelIndex& child) const {
28 return QModelIndex();
29}
30
31int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
32 return 3;
33}
34
35int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
36 return info.code.size();
37}
38
39QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const {
40 switch(role) {
41 case Qt::DisplayRole:
42 {
43 if (section == 0) {
44 return tr("Offset");
45 } else if (section == 1) {
46 return tr("Raw");
47 } else if (section == 2) {
48 return tr("Disassembly");
49 }
50
51 break;
52 }
53 }
54
55 return QVariant();
56}
57
58QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
59 switch (role) {
60 case Qt::DisplayRole:
61 {
62 switch (index.column()) {
63 case 0:
64 if (info.HasLabel(index.row()))
65 return QString::fromStdString(info.GetLabel(index.row()));
66
67 return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0'));
68
69 case 1:
70 return QString("%1").arg(info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
71
72 case 2:
73 {
74 std::stringstream output;
75 output.flags(std::ios::hex);
76
77 Instruction instr = info.code[index.row()];
78 const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern;
79
80 // longest known instruction name: "setemit "
81 output << std::setw(8) << std::left << instr.opcode.GetInfo().name;
82
83 // e.g. "-c92.xyzw"
84 static auto print_input = [](std::stringstream& output, const SourceRegister& input,
85 bool negate, const std::string& swizzle_mask) {
86 output << std::setw(4) << std::right << (negate ? "-" : "") + input.GetName();
87 output << "." << swizzle_mask;
88 };
89
90 // e.g. "-c92[a0.x].xyzw"
91 static auto print_input_indexed = [](std::stringstream& output, const SourceRegister& input,
92 bool negate, const std::string& swizzle_mask,
93 const std::string& address_register_name) {
94 std::string relative_address;
95 if (!address_register_name.empty())
96 relative_address = "[" + address_register_name + "]";
97
98 output << std::setw(10) << std::right << (negate ? "-" : "") + input.GetName() + relative_address;
99 output << "." << swizzle_mask;
100 };
101
102 // Use print_input or print_input_indexed depending on whether relative addressing is used or not.
103 static auto print_input_indexed_compact = [](std::stringstream& output, const SourceRegister& input,
104 bool negate, const std::string& swizzle_mask,
105 const std::string& address_register_name) {
106 if (address_register_name.empty())
107 print_input(output, input, negate, swizzle_mask);
108 else
109 print_input_indexed(output, input, negate, swizzle_mask, address_register_name);
110 };
111
112 switch (instr.opcode.GetInfo().type) {
113 case Instruction::OpCodeType::Trivial:
114 // Nothing to do here
115 break;
116
117 case Instruction::OpCodeType::Arithmetic:
118 {
119 // Use custom code for special instructions
120 switch (instr.opcode.EffectiveOpCode()) {
121 case Instruction::OpCode::CMP:
122 {
123 // NOTE: CMP always writes both cc components, so we do not consider the dest mask here.
124 output << std::setw(4) << std::right << "cc.";
125 output << "xy ";
126
127 SourceRegister src1 = instr.common.GetSrc1(false);
128 SourceRegister src2 = instr.common.GetSrc2(false);
129
130 print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), instr.common.AddressRegisterName());
131 output << " " << instr.common.compare_op.ToString(instr.common.compare_op.x) << " ";
132 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(0,1));
133
134 output << ", ";
135
136 print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), instr.common.AddressRegisterName());
137 output << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " ";
138 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(1,1));
139
140 break;
141 }
142
143 default:
144 {
145 bool src_is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed);
146
147 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Dest) {
148 // e.g. "r12.xy__"
149 output << std::setw(4) << std::right << instr.common.dest.GetName() + ".";
150 output << swizzle.DestMaskToString();
151 } else if (instr.opcode.GetInfo().subtype == Instruction::OpCodeInfo::MOVA) {
152 output << std::setw(4) << std::right << "a0.";
153 output << swizzle.DestMaskToString();
154 } else {
155 output << " ";
156 }
157 output << " ";
158
159 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src1) {
160 SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
161 print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName());
162 } else {
163 output << " ";
164 }
165
166 // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1
167 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src2) {
168 SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
169 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false));
170 }
171 break;
172 }
173 }
174
175 break;
176 }
177
178 case Instruction::OpCodeType::Conditional:
179 {
180 switch (instr.opcode.EffectiveOpCode()) {
181 case Instruction::OpCode::LOOP:
182 output << "(unknown instruction format)";
183 break;
184
185 default:
186 output << "if ";
187
188 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasCondition) {
189 const char* ops[] = {
190 " || ", " && ", "", ""
191 };
192 if (instr.flow_control.op != instr.flow_control.JustY)
193 output << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x";
194
195 output << ops[instr.flow_control.op];
196
197 if (instr.flow_control.op != instr.flow_control.JustX)
198 output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y";
199
200 output << " ";
201 } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasUniformIndex) {
202 output << "b" << instr.flow_control.bool_uniform_id << " ";
203 }
204
205 u32 target_addr = instr.flow_control.dest_offset;
206 u32 target_addr_else = instr.flow_control.dest_offset;
207
208 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasAlternative) {
209 output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " ";
210 } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasExplicitDest) {
211 output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " ";
212 } else {
213 // TODO: Handle other cases
214 }
215
216 if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasFinishPoint) {
217 output << "(return on " << std::setw(4) << std::right << std::setfill('0')
218 << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")";
219 }
220
221 break;
222 }
223 break;
224 }
225
226 default:
227 output << "(unknown instruction format)";
228 break;
229 }
230
231 return QString::fromLatin1(output.str().c_str());
232 }
233
234 default:
235 break;
236 }
237 }
238
239 case Qt::FontRole:
240 return QFont("monospace");
241
242 default:
243 break;
244 }
245
246 return QVariant();
247}
248
249void GraphicsVertexShaderModel::OnUpdate()
250{
251 beginResetModel();
252
253 info.Clear();
254
255 for (auto instr : Pica::VertexShader::GetShaderBinary())
256 info.code.push_back({instr});
257
258 for (auto pattern : Pica::VertexShader::GetSwizzlePatterns())
259 info.swizzle_info.push_back({pattern});
260
261 info.labels.insert({Pica::registers.vs_main_offset, "main"});
262
263 endResetModel();
264}
265
266
267GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context,
268 QWidget* parent)
269 : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
270 setObjectName("PicaVertexShader");
271
272 auto binary_model = new GraphicsVertexShaderModel(this);
273 auto binary_list = new QTreeView;
274 binary_list->setModel(binary_model);
275 binary_list->setRootIsDecorated(false);
276 binary_list->setAlternatingRowColors(true);
277
278 connect(this, SIGNAL(Update()), binary_model, SLOT(OnUpdate()));
279
280 auto main_widget = new QWidget;
281 auto main_layout = new QVBoxLayout;
282 {
283 auto sub_layout = new QHBoxLayout;
284 sub_layout->addWidget(binary_list);
285 main_layout->addLayout(sub_layout);
286 }
287 main_widget->setLayout(main_layout);
288 setWidget(main_widget);
289}
290
291void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
292 emit Update();
293 widget()->setEnabled(true);
294}
295
296void GraphicsVertexShaderWidget::OnResumed() {
297 widget()->setEnabled(false);
298}