summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2015-09-07 16:54:45 -0300
committerGravatar Yuri Kunde Schlesner2015-09-07 16:54:45 -0300
commit87a2fd8b3fdc0931cce95300cd85c5ee3ea0382c (patch)
tree7380029505ac8c21ab77aecb9ce3b9145b80705a /src
parentMerge pull request #1117 from yuriks/fix-glad-build (diff)
parentShader Debugger: Allow editing of input vertex data (diff)
downloadyuzu-87a2fd8b3fdc0931cce95300cd85c5ee3ea0382c.tar.gz
yuzu-87a2fd8b3fdc0931cce95300cd85c5ee3ea0382c.tar.xz
yuzu-87a2fd8b3fdc0931cce95300cd85c5ee3ea0382c.zip
Merge pull request #1052 from yuriks/vertex-disasm
Shader Debugger Improvements
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp301
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.h11
3 files changed, 187 insertions, 127 deletions
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 025434687..fa32d24b5 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -359,7 +359,7 @@ void GPUCommandListWidget::CopyAllToClipboard() {
359 QClipboard* clipboard = QApplication::clipboard(); 359 QClipboard* clipboard = QApplication::clipboard();
360 QString text; 360 QString text;
361 361
362 QAbstractItemModel* model = static_cast<QAbstractListModel*>(list_widget->model()); 362 QAbstractItemModel* model = static_cast<QAbstractItemModel*>(list_widget->model());
363 363
364 for (int row = 0; row < model->rowCount({}); ++row) { 364 for (int row = 0; row < model->rowCount({}); ++row) {
365 for (int col = 0; col < model->columnCount({}); ++col) { 365 for (int col = 0; col < model->columnCount({}); ++col) {
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index 1d9a00e89..d48b3294b 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -7,6 +7,7 @@
7 7
8#include <QBoxLayout> 8#include <QBoxLayout>
9#include <QFileDialog> 9#include <QFileDialog>
10#include <QFormLayout>
10#include <QGroupBox> 11#include <QGroupBox>
11#include <QLabel> 12#include <QLabel>
12#include <QLineEdit> 13#include <QLineEdit>
@@ -26,18 +27,10 @@ using nihstro::Instruction;
26using nihstro::SourceRegister; 27using nihstro::SourceRegister;
27using nihstro::SwizzlePattern; 28using nihstro::SwizzlePattern;
28 29
29GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractItemModel(parent), par(parent) { 30GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) {
30 31
31} 32}
32 33
33QModelIndex GraphicsVertexShaderModel::index(int row, int column, const QModelIndex& parent) const {
34 return createIndex(row, column);
35}
36
37QModelIndex GraphicsVertexShaderModel::parent(const QModelIndex& child) const {
38 return QModelIndex();
39}
40
41int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { 34int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
42 return 3; 35 return 3;
43} 36}
@@ -65,6 +58,28 @@ QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orie
65 return QVariant(); 58 return QVariant();
66} 59}
67 60
61static std::string SelectorToString(u32 selector) {
62 std::string ret;
63 for (int i = 0; i < 4; ++i) {
64 int component = (selector >> ((3 - i) * 2)) & 3;
65 ret += "xyzw"[component];
66 }
67 return ret;
68}
69
70// e.g. "-c92[a0.x].xyzw"
71static void print_input(std::ostringstream& output, const SourceRegister& input,
72 bool negate, const std::string& swizzle_mask, bool align = true,
73 const std::string& address_register_name = std::string()) {
74 if (align)
75 output << std::setw(4) << std::right;
76 output << ((negate ? "-" : "") + input.GetName());
77
78 if (!address_register_name.empty())
79 output << '[' << address_register_name << ']';
80 output << '.' << swizzle_mask;
81};
82
68QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { 83QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
69 switch (role) { 84 switch (role) {
70 case Qt::DisplayRole: 85 case Qt::DisplayRole:
@@ -81,102 +96,120 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
81 96
82 case 2: 97 case 2:
83 { 98 {
84 std::stringstream output; 99 std::ostringstream output;
85 output.flags(std::ios::hex); 100 output.flags(std::ios::uppercase);
86 101
87 Instruction instr = par->info.code[index.row()]; 102 // To make the code aligning columns of assembly easier to keep track of, this function
88 const SwizzlePattern& swizzle = par->info.swizzle_info[instr.common.operand_desc_id].pattern; 103 // keeps track of the start of the start of the previous column, allowing alignment
89 104 // based on desired field widths.
90 // longest known instruction name: "setemit " 105 int current_column = 0;
91 output << std::setw(8) << std::left << instr.opcode.Value().GetInfo().name; 106 auto AlignToColumn = [&](int col_width) {
92 107 // Prints spaces to the output to pad previous column to size and advances the
93 // e.g. "-c92.xyzw" 108 // column marker.
94 static auto print_input = [](std::stringstream& output, const SourceRegister& input, 109 current_column += col_width;
95 bool negate, const std::string& swizzle_mask) { 110 int to_add = std::max(1, current_column - (int)output.tellp());
96 output << std::setw(4) << std::right << (negate ? "-" : "") + input.GetName(); 111 for (int i = 0; i < to_add; ++i) {
97 output << "." << swizzle_mask; 112 output << ' ';
113 }
98 }; 114 };
99 115
100 // e.g. "-c92[a0.x].xyzw" 116 const Instruction instr = par->info.code[index.row()];
101 static auto print_input_indexed = [](std::stringstream& output, const SourceRegister& input, 117 const OpCode opcode = instr.opcode;
102 bool negate, const std::string& swizzle_mask, 118 const OpCode::Info opcode_info = opcode.GetInfo();
103 const std::string& address_register_name) { 119 const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ?
104 std::string relative_address; 120 instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value();
105 if (!address_register_name.empty()) 121 const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
106 relative_address = "[" + address_register_name + "]";
107 122
108 output << std::setw(10) << std::right << (negate ? "-" : "") + input.GetName() + relative_address; 123 // longest known instruction name: "setemit "
109 output << "." << swizzle_mask; 124 int kOpcodeColumnWidth = 8;
110 }; 125 // "rXX.xyzw "
126 int kOutputColumnWidth = 10;
127 // "-rXX.xyzw ", no attempt is made to align indexed inputs
128 int kInputOperandColumnWidth = 11;
111 129
112 // Use print_input or print_input_indexed depending on whether relative addressing is used or not. 130 output << opcode_info.name;
113 static auto print_input_indexed_compact = [](std::stringstream& output, const SourceRegister& input,
114 bool negate, const std::string& swizzle_mask,
115 const std::string& address_register_name) {
116 if (address_register_name.empty())
117 print_input(output, input, negate, swizzle_mask);
118 else
119 print_input_indexed(output, input, negate, swizzle_mask, address_register_name);
120 };
121 131
122 switch (instr.opcode.Value().GetInfo().type) { 132 switch (opcode_info.type) {
123 case OpCode::Type::Trivial: 133 case OpCode::Type::Trivial:
124 // Nothing to do here 134 // Nothing to do here
125 break; 135 break;
126 136
127 case OpCode::Type::Arithmetic: 137 case OpCode::Type::Arithmetic:
138 case OpCode::Type::MultiplyAdd:
128 { 139 {
129 // Use custom code for special instructions 140 // Use custom code for special instructions
130 switch (instr.opcode.Value().EffectiveOpCode()) { 141 switch (opcode.EffectiveOpCode()) {
131 case OpCode::Id::CMP: 142 case OpCode::Id::CMP:
132 { 143 {
144 AlignToColumn(kOpcodeColumnWidth);
145
133 // NOTE: CMP always writes both cc components, so we do not consider the dest mask here. 146 // NOTE: CMP always writes both cc components, so we do not consider the dest mask here.
134 output << std::setw(4) << std::right << "cc."; 147 output << " cc.xy";
135 output << "xy "; 148 AlignToColumn(kOutputColumnWidth);
136 149
137 SourceRegister src1 = instr.common.GetSrc1(false); 150 SourceRegister src1 = instr.common.GetSrc1(false);
138 SourceRegister src2 = instr.common.GetSrc2(false); 151 SourceRegister src2 = instr.common.GetSrc2(false);
139 152
140 print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), instr.common.AddressRegisterName()); 153 output << ' ';
141 output << " " << instr.common.compare_op.ToString(instr.common.compare_op.x) << " "; 154 print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName());
142 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1)); 155 output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' ';
156 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false);
143 157
144 output << ", "; 158 output << ", ";
145 159
146 print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), instr.common.AddressRegisterName()); 160 print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName());
147 output << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " "; 161 output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' ';
148 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1)); 162 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false);
149 163
150 break; 164 break;
151 } 165 }
152 166
167 case OpCode::Id::MAD:
168 case OpCode::Id::MADI:
169 {
170 AlignToColumn(kOpcodeColumnWidth);
171
172 bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
173 SourceRegister src1 = instr.mad.GetSrc1(src_is_inverted);
174 SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted);
175 SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted);
176
177 output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString();
178 AlignToColumn(kOutputColumnWidth);
179 print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector));
180 AlignToColumn(kInputOperandColumnWidth);
181 print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector));
182 AlignToColumn(kInputOperandColumnWidth);
183 print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector));
184 AlignToColumn(kInputOperandColumnWidth);
185 break;
186 }
187
153 default: 188 default:
154 { 189 {
155 bool src_is_inverted = 0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed); 190 AlignToColumn(kOpcodeColumnWidth);
191
192 bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
156 193
157 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Dest) { 194 if (opcode_info.subtype & OpCode::Info::Dest) {
158 // e.g. "r12.xy__" 195 // e.g. "r12.xy__"
159 output << std::setw(4) << std::right << instr.common.dest.Value().GetName() + "."; 196 output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString();
160 output << swizzle.DestMaskToString(); 197 } else if (opcode_info.subtype == OpCode::Info::MOVA) {
161 } else if (instr.opcode.Value().GetInfo().subtype == OpCode::Info::MOVA) { 198 output << " a0." << swizzle.DestMaskToString();
162 output << std::setw(4) << std::right << "a0.";
163 output << swizzle.DestMaskToString();
164 } else {
165 output << " ";
166 } 199 }
167 output << " "; 200 AlignToColumn(kOutputColumnWidth);
168 201
169 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Src1) { 202 if (opcode_info.subtype & OpCode::Info::Src1) {
170 SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); 203 SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
171 print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName()); 204 print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName());
172 } else { 205 AlignToColumn(kInputOperandColumnWidth);
173 output << " ";
174 } 206 }
175 207
176 // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 208 // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1
177 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Src2) { 209 if (opcode_info.subtype & OpCode::Info::Src2) {
178 SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); 210 SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
179 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); 211 print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true));
212 AlignToColumn(kInputOperandColumnWidth);
180 } 213 }
181 break; 214 break;
182 } 215 }
@@ -186,46 +219,55 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
186 } 219 }
187 220
188 case OpCode::Type::Conditional: 221 case OpCode::Type::Conditional:
222 case OpCode::Type::UniformFlowControl:
189 { 223 {
190 switch (instr.opcode.Value().EffectiveOpCode()) { 224 output << ' ';
225
226 switch (opcode.EffectiveOpCode()) {
191 case OpCode::Id::LOOP: 227 case OpCode::Id::LOOP:
192 output << "(unknown instruction format)"; 228 output << "(unknown instruction format)";
193 break; 229 break;
194 230
195 default: 231 default:
196 output << "if "; 232 if (opcode_info.subtype & OpCode::Info::HasCondition) {
197 233 output << '(';
198 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasCondition) { 234
199 const char* ops[] = { 235 if (instr.flow_control.op != instr.flow_control.JustY) {
200 " || ", " && ", "", "" 236 if (instr.flow_control.refx) output << '!';
201 }; 237 output << "cc.x";
202 if (instr.flow_control.op != instr.flow_control.JustY) 238 }
203 output << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x"; 239
204 240 if (instr.flow_control.op == instr.flow_control.Or) {
205 output << ops[instr.flow_control.op]; 241 output << " || ";
206 242 } else if (instr.flow_control.op == instr.flow_control.And) {
207 if (instr.flow_control.op != instr.flow_control.JustX) 243 output << " && ";
208 output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y"; 244 }
209 245
210 output << " "; 246 if (instr.flow_control.op != instr.flow_control.JustX) {
211 } else if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasUniformIndex) { 247 if (instr.flow_control.refy) output << '!';
212 output << "b" << instr.flow_control.bool_uniform_id << " "; 248 output << "cc.y";
249 }
250
251 output << ") ";
252 } else if (opcode_info.subtype & OpCode::Info::HasUniformIndex) {
253 output << 'b' << instr.flow_control.bool_uniform_id << ' ';
213 } 254 }
214 255
215 u32 target_addr = instr.flow_control.dest_offset; 256 u32 target_addr = instr.flow_control.dest_offset;
216 u32 target_addr_else = instr.flow_control.dest_offset; 257 u32 target_addr_else = instr.flow_control.dest_offset;
217 258
218 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasAlternative) { 259 if (opcode_info.subtype & OpCode::Info::HasAlternative) {
219 output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; 260 output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset);
220 } else if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasExplicitDest) { 261 } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) {
221 output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; 262 output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset);
222 } else { 263 } else {
223 // TODO: Handle other cases 264 // TODO: Handle other cases
265 output << "(unknown destination)";
224 } 266 }
225 267
226 if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasFinishPoint) { 268 if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
227 output << "(return on " << std::setw(4) << std::right << std::setfill('0') 269 output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex
228 << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")"; 270 << (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')';
229 } 271 }
230 272
231 break; 273 break;
@@ -234,7 +276,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
234 } 276 }
235 277
236 default: 278 default:
237 output << "(unknown instruction format)"; 279 output << " (unknown instruction format)";
238 break; 280 break;
239 } 281 }
240 282
@@ -250,12 +292,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
250 return GetMonospaceFont(); 292 return GetMonospaceFont();
251 293
252 case Qt::BackgroundRole: 294 case Qt::BackgroundRole:
253 // Highlight instructions which have no debug data associated to them 295 {
296 // Highlight current instruction
297 int current_record_index = par->cycle_index->value();
298 if (current_record_index < par->debug_data.records.size()) {
299 const auto& current_record = par->debug_data.records[current_record_index];
300 if (index.row() == current_record.instruction_offset) {
301 return QColor(255, 255, 63);
302 }
303 }
304
305 // Use a grey background for instructions which have no debug data associated to them
254 for (const auto& record : par->debug_data.records) 306 for (const auto& record : par->debug_data.records)
255 if (index.row() == record.instruction_offset) 307 if (index.row() == record.instruction_offset)
256 return QVariant(); 308 return QVariant();
257 309
258 return QBrush(QColor(255, 255, 127)); 310 return QBrush(QColor(192, 192, 192));
311 }
259 312
260 313
261 // TODO: Draw arrows for each "reachable" instruction to visualize control flow 314 // TODO: Draw arrows for each "reachable" instruction to visualize control flow
@@ -288,6 +341,13 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
288 : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { 341 : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
289 setObjectName("PicaVertexShader"); 342 setObjectName("PicaVertexShader");
290 343
344 // Clear input vertex data so that it contains valid float values in case a debug shader
345 // execution happens before the first Vertex Loaded breakpoint.
346 // TODO: This makes a crash in the interpreter much less likely, but not impossible. The
347 // interpreter should guard against out-of-bounds accesses to ensure crashes in it aren't
348 // possible.
349 std::memset(&input_vertex, 0, sizeof(input_vertex));
350
291 auto input_data_mapper = new QSignalMapper(this); 351 auto input_data_mapper = new QSignalMapper(this);
292 352
293 // TODO: Support inputting data in hexadecimal raw format 353 // TODO: Support inputting data in hexadecimal raw format
@@ -312,9 +372,6 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
312 372
313 cycle_index = new QSpinBox; 373 cycle_index = new QSpinBox;
314 374
315 connect(this, SIGNAL(SelectCommand(const QModelIndex&, QItemSelectionModel::SelectionFlags)),
316 binary_list->selectionModel(), SLOT(select(const QModelIndex&, QItemSelectionModel::SelectionFlags)));
317
318 connect(dump_shader, SIGNAL(clicked()), this, SLOT(DumpShader())); 375 connect(dump_shader, SIGNAL(clicked()), this, SLOT(DumpShader()));
319 376
320 connect(cycle_index, SIGNAL(valueChanged(int)), this, SLOT(OnCycleIndexChanged(int))); 377 connect(cycle_index, SIGNAL(valueChanged(int)), this, SLOT(OnCycleIndexChanged(int)));
@@ -339,6 +396,9 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
339 // Create an HBoxLayout to store the widgets used to specify a particular attribute 396 // Create an HBoxLayout to store the widgets used to specify a particular attribute
340 // and store it in a QWidget to allow for easy hiding and unhiding. 397 // and store it in a QWidget to allow for easy hiding and unhiding.
341 auto row_layout = new QHBoxLayout; 398 auto row_layout = new QHBoxLayout;
399 // Remove unecessary padding between rows
400 row_layout->setContentsMargins(0, 0, 0, 0);
401
342 row_layout->addWidget(new QLabel(tr("Attribute %1").arg(i, 2))); 402 row_layout->addWidget(new QLabel(tr("Attribute %1").arg(i, 2)));
343 for (unsigned comp = 0; comp < 4; ++comp) 403 for (unsigned comp = 0; comp < 4; ++comp)
344 row_layout->addWidget(input_data[4 * i + comp]); 404 row_layout->addWidget(input_data[4 * i + comp]);
@@ -358,20 +418,25 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De
358 input_data_group->setLayout(sub_layout); 418 input_data_group->setLayout(sub_layout);
359 main_layout->addWidget(input_data_group); 419 main_layout->addWidget(input_data_group);
360 } 420 }
361 { 421
362 auto sub_layout = new QHBoxLayout; 422 // Make program listing expand to fill available space in the dialog
363 sub_layout->addWidget(binary_list); 423 binary_list->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
364 main_layout->addLayout(sub_layout); 424 main_layout->addWidget(binary_list);
365 } 425
366 main_layout->addWidget(dump_shader); 426 main_layout->addWidget(dump_shader);
367 { 427 {
368 auto sub_layout = new QHBoxLayout; 428 auto sub_layout = new QFormLayout;
369 sub_layout->addWidget(new QLabel(tr("Cycle Index:"))); 429 sub_layout->addRow(tr("Cycle Index:"), cycle_index);
370 sub_layout->addWidget(cycle_index); 430
371 main_layout->addLayout(sub_layout); 431 main_layout->addLayout(sub_layout);
372 } 432 }
433
434 // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
435 // part of the UI to keep jumping up and down when cycling through instructions.
436 instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6);
437 instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop);
373 main_layout->addWidget(instruction_description); 438 main_layout->addWidget(instruction_description);
374 main_layout->addStretch(); 439
375 main_widget->setLayout(main_layout); 440 main_widget->setLayout(main_layout);
376 setWidget(main_widget); 441 setWidget(main_widget);
377 442
@@ -418,6 +483,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
418 auto& shader_config = Pica::g_state.regs.vs; 483 auto& shader_config = Pica::g_state.regs.vs;
419 for (auto instr : shader_setup.program_code) 484 for (auto instr : shader_setup.program_code)
420 info.code.push_back({instr}); 485 info.code.push_back({instr});
486 int num_attributes = Pica::g_state.regs.vertex_attributes.GetNumTotalAttributes();
421 487
422 for (auto pattern : shader_setup.swizzle_data) 488 for (auto pattern : shader_setup.swizzle_data)
423 info.swizzle_info.push_back({pattern}); 489 info.swizzle_info.push_back({pattern});
@@ -426,19 +492,18 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
426 info.labels.insert({ entry_point, "main" }); 492 info.labels.insert({ entry_point, "main" });
427 493
428 // Generate debug information 494 // Generate debug information
429 debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, 1, shader_config, shader_setup); 495 debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup);
430 496
431 // Reload widget state 497 // Reload widget state
432 498 for (unsigned int attr = 0; attr < num_attributes; ++attr) {
433 // Only show input attributes which are used as input to the shader
434 for (unsigned int attr = 0; attr < 16; ++attr) {
435 input_data_container[attr]->setVisible(false);
436 }
437 for (unsigned int attr = 0; attr < Pica::g_state.regs.vertex_attributes.GetNumTotalAttributes(); ++attr) {
438 unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr); 499 unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr);
439 input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr)); 500 input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr));
440 input_data_container[source_attr]->setVisible(true); 501 input_data_container[source_attr]->setVisible(true);
441 } 502 }
503 // Only show input attributes which are used as input to the shader
504 for (unsigned int attr = num_attributes; attr < 16; ++attr) {
505 input_data_container[attr]->setVisible(false);
506 }
442 507
443 // Initialize debug info text for current cycle count 508 // Initialize debug info text for current cycle count
444 cycle_index->setMaximum(debug_data.records.size() - 1); 509 cycle_index->setMaximum(debug_data.records.size() - 1);
@@ -453,6 +518,8 @@ void GraphicsVertexShaderWidget::OnResumed() {
453 518
454void GraphicsVertexShaderWidget::OnInputAttributeChanged(int index) { 519void GraphicsVertexShaderWidget::OnInputAttributeChanged(int index) {
455 float value = input_data[index]->text().toFloat(); 520 float value = input_data[index]->text().toFloat();
521 input_vertex.attr[index / 4][index % 4] = Pica::float24::FromFloat32(value);
522 // Re-execute shader with updated value
456 Reload(); 523 Reload();
457} 524}
458 525
@@ -492,8 +559,8 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
492 559
493 instruction_description->setText(text); 560 instruction_description->setText(text);
494 561
495 // Scroll to current instruction 562 // Emit model update notification and scroll to current instruction
496 const QModelIndex& instr_index = model->index(record.instruction_offset, 0); 563 QModelIndex instr_index = model->index(record.instruction_offset, 0);
497 emit SelectCommand(instr_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 564 emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount()));
498 binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); 565 binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible);
499} 566}
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics_vertex_shader.h
index 1b3f1f7ec..0bf1652fc 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.h
+++ b/src/citra_qt/debugger/graphics_vertex_shader.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QAbstractListModel> 7#include <QAbstractTableModel>
8 8
9#include "graphics_breakpoint_observer.h" 9#include "graphics_breakpoint_observer.h"
10 10
@@ -17,14 +17,12 @@ class QSpinBox;
17 17
18class GraphicsVertexShaderWidget; 18class GraphicsVertexShaderWidget;
19 19
20class GraphicsVertexShaderModel : public QAbstractItemModel { 20class GraphicsVertexShaderModel : public QAbstractTableModel {
21 Q_OBJECT 21 Q_OBJECT
22 22
23public: 23public:
24 GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent); 24 GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent);
25 25
26 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
27 QModelIndex parent(const QModelIndex& child) const override;
28 int columnCount(const QModelIndex& parent = QModelIndex()) const override; 26 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
29 int rowCount(const QModelIndex& parent = QModelIndex()) const override; 27 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
30 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 28 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
@@ -62,11 +60,6 @@ private slots:
62 */ 60 */
63 void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); 61 void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr);
64 62
65
66signals:
67 // Call this to change the current command selection in the disassembly view
68 void SelectCommand(const QModelIndex&, QItemSelectionModel::SelectionFlags);
69
70private: 63private:
71 QLabel* instruction_description; 64 QLabel* instruction_description;
72 QTreeView* binary_list; 65 QTreeView* binary_list;