diff options
Diffstat (limited to 'src/citra_qt/debugger')
| -rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.cpp | 251 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.h | 52 |
2 files changed, 261 insertions, 42 deletions
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp index b1657620e..359193226 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp | |||
| @@ -6,11 +6,16 @@ | |||
| 6 | #include <sstream> | 6 | #include <sstream> |
| 7 | 7 | ||
| 8 | #include <QBoxLayout> | 8 | #include <QBoxLayout> |
| 9 | #include <QFileDialog> | ||
| 10 | #include <QGroupBox> | ||
| 9 | #include <QLabel> | 11 | #include <QLabel> |
| 12 | #include <QLineEdit> | ||
| 10 | #include <QPushButton> | 13 | #include <QPushButton> |
| 14 | #include <QSignalMapper> | ||
| 15 | #include <QSpinBox> | ||
| 11 | #include <QTreeView> | 16 | #include <QTreeView> |
| 12 | 17 | ||
| 13 | #include "video_core/shader/shader_interpreter.h" | 18 | #include "video_core/shader/shader.h" |
| 14 | 19 | ||
| 15 | #include "graphics_vertex_shader.h" | 20 | #include "graphics_vertex_shader.h" |
| 16 | 21 | ||
| @@ -19,7 +24,7 @@ using nihstro::Instruction; | |||
| 19 | using nihstro::SourceRegister; | 24 | using nihstro::SourceRegister; |
| 20 | using nihstro::SwizzlePattern; | 25 | using nihstro::SwizzlePattern; |
| 21 | 26 | ||
| 22 | GraphicsVertexShaderModel::GraphicsVertexShaderModel(QObject* parent): QAbstractItemModel(parent) { | 27 | GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractItemModel(parent), par(parent) { |
| 23 | 28 | ||
| 24 | } | 29 | } |
| 25 | 30 | ||
| @@ -36,7 +41,7 @@ int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | |||
| 36 | } | 41 | } |
| 37 | 42 | ||
| 38 | int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | 43 | int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { |
| 39 | return static_cast<int>(info.code.size()); | 44 | return static_cast<int>(par->info.code.size()); |
| 40 | } | 45 | } |
| 41 | 46 | ||
| 42 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | 47 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { |
| @@ -64,21 +69,21 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 64 | { | 69 | { |
| 65 | switch (index.column()) { | 70 | switch (index.column()) { |
| 66 | case 0: | 71 | case 0: |
| 67 | if (info.HasLabel(index.row())) | 72 | if (par->info.HasLabel(index.row())) |
| 68 | return QString::fromStdString(info.GetLabel(index.row())); | 73 | return QString::fromStdString(par->info.GetLabel(index.row())); |
| 69 | 74 | ||
| 70 | return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); | 75 | return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); |
| 71 | 76 | ||
| 72 | case 1: | 77 | case 1: |
| 73 | return QString("%1").arg(info.code[index.row()].hex, 8, 16, QLatin1Char('0')); | 78 | return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); |
| 74 | 79 | ||
| 75 | case 2: | 80 | case 2: |
| 76 | { | 81 | { |
| 77 | std::stringstream output; | 82 | std::stringstream output; |
| 78 | output.flags(std::ios::hex); | 83 | output.flags(std::ios::hex); |
| 79 | 84 | ||
| 80 | Instruction instr = info.code[index.row()]; | 85 | Instruction instr = par->info.code[index.row()]; |
| 81 | const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern; | 86 | const SwizzlePattern& swizzle = par->info.swizzle_info[instr.common.operand_desc_id].pattern; |
| 82 | 87 | ||
| 83 | // longest known instruction name: "setemit " | 88 | // longest known instruction name: "setemit " |
| 84 | output << std::setw(8) << std::left << instr.opcode.Value().GetInfo().name; | 89 | output << std::setw(8) << std::left << instr.opcode.Value().GetInfo().name; |
| @@ -242,6 +247,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 242 | case Qt::FontRole: | 247 | case Qt::FontRole: |
| 243 | return QFont("monospace"); | 248 | return QFont("monospace"); |
| 244 | 249 | ||
| 250 | case Qt::BackgroundRole: | ||
| 251 | // Highlight instructions which have no debug data associated to them | ||
| 252 | for (const auto& record : par->debug_data.records) | ||
| 253 | if (index.row() == record.instruction_offset) | ||
| 254 | return QVariant(); | ||
| 255 | |||
| 256 | return QBrush(QColor(255, 255, 127)); | ||
| 257 | |||
| 258 | |||
| 259 | // TODO: Draw arrows for each "reachable" instruction to visualize control flow | ||
| 260 | |||
| 261 | |||
| 245 | default: | 262 | default: |
| 246 | break; | 263 | break; |
| 247 | } | 264 | } |
| @@ -249,30 +266,19 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 249 | return QVariant(); | 266 | return QVariant(); |
| 250 | } | 267 | } |
| 251 | 268 | ||
| 252 | void GraphicsVertexShaderModel::OnUpdate() | 269 | void GraphicsVertexShaderWidget::DumpShader() { |
| 253 | { | 270 | QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", |
| 254 | beginResetModel(); | 271 | tr("Shader Binary (*.shbin)")); |
| 255 | |||
| 256 | info.Clear(); | ||
| 257 | |||
| 258 | auto& shader_setup = Pica::g_state.vs; | ||
| 259 | for (auto instr : shader_setup.program_code) | ||
| 260 | info.code.push_back({instr}); | ||
| 261 | |||
| 262 | for (auto pattern : shader_setup.swizzle_data) | ||
| 263 | info.swizzle_info.push_back({pattern}); | ||
| 264 | |||
| 265 | u32 entry_point = Pica::g_state.regs.vs.main_offset; | ||
| 266 | info.labels.insert({ entry_point, "main" }); | ||
| 267 | 272 | ||
| 268 | endResetModel(); | 273 | if (filename.isEmpty()) { |
| 269 | } | 274 | // If the user canceled the dialog, don't dump anything. |
| 275 | return; | ||
| 276 | } | ||
| 270 | 277 | ||
| 271 | void GraphicsVertexShaderModel::DumpShader() { | ||
| 272 | auto& setup = Pica::g_state.vs; | 278 | auto& setup = Pica::g_state.vs; |
| 273 | auto& config = Pica::g_state.regs.vs; | 279 | auto& config = Pica::g_state.regs.vs; |
| 274 | 280 | ||
| 275 | Pica::DebugUtils::DumpShader(config, setup, Pica::g_state.regs.vs_output_attributes); | 281 | Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); |
| 276 | } | 282 | } |
| 277 | 283 | ||
| 278 | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, | 284 | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, |
| @@ -280,34 +286,211 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De | |||
| 280 | : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { | 286 | : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { |
| 281 | setObjectName("PicaVertexShader"); | 287 | setObjectName("PicaVertexShader"); |
| 282 | 288 | ||
| 283 | auto binary_model = new GraphicsVertexShaderModel(this); | 289 | auto input_data_mapper = new QSignalMapper(this); |
| 284 | auto binary_list = new QTreeView; | 290 | |
| 285 | binary_list->setModel(binary_model); | 291 | // TODO: Support inputting data in hexadecimal raw format |
| 292 | for (unsigned i = 0; i < ARRAY_SIZE(input_data); ++i) { | ||
| 293 | input_data[i] = new QLineEdit; | ||
| 294 | input_data[i]->setValidator(new QDoubleValidator(input_data[i])); | ||
| 295 | } | ||
| 296 | |||
| 297 | breakpoint_warning = new QLabel(tr("(data only available at VertexLoaded breakpoints)")); | ||
| 298 | |||
| 299 | // TODO: Add some button for jumping to the shader entry point | ||
| 300 | |||
| 301 | model = new GraphicsVertexShaderModel(this); | ||
| 302 | binary_list = new QTreeView; | ||
| 303 | binary_list->setModel(model); | ||
| 286 | binary_list->setRootIsDecorated(false); | 304 | binary_list->setRootIsDecorated(false); |
| 287 | binary_list->setAlternatingRowColors(true); | 305 | binary_list->setAlternatingRowColors(true); |
| 288 | 306 | ||
| 289 | auto dump_shader = new QPushButton(tr("Dump")); | 307 | auto dump_shader = new QPushButton(QIcon::fromTheme("document-save"), tr("Dump")); |
| 308 | |||
| 309 | instruction_description = new QLabel; | ||
| 290 | 310 | ||
| 291 | connect(dump_shader, SIGNAL(clicked()), binary_model, SLOT(DumpShader())); | 311 | iteration_index = new QSpinBox; |
| 292 | connect(this, SIGNAL(Update()), binary_model, SLOT(OnUpdate())); | 312 | |
| 313 | connect(this, SIGNAL(SelectCommand(const QModelIndex&, QItemSelectionModel::SelectionFlags)), | ||
| 314 | binary_list->selectionModel(), SLOT(select(const QModelIndex&, QItemSelectionModel::SelectionFlags))); | ||
| 315 | |||
| 316 | connect(dump_shader, SIGNAL(clicked()), this, SLOT(DumpShader())); | ||
| 317 | |||
| 318 | connect(iteration_index, SIGNAL(valueChanged(int)), this, SLOT(OnIterationIndexChanged(int))); | ||
| 319 | |||
| 320 | for (unsigned i = 0; i < ARRAY_SIZE(input_data); ++i) { | ||
| 321 | connect(input_data[i], SIGNAL(textEdited(const QString&)), input_data_mapper, SLOT(map())); | ||
| 322 | input_data_mapper->setMapping(input_data[i], i); | ||
| 323 | } | ||
| 324 | connect(input_data_mapper, SIGNAL(mapped(int)), this, SLOT(OnInputAttributeChanged(int))); | ||
| 293 | 325 | ||
| 294 | auto main_widget = new QWidget; | 326 | auto main_widget = new QWidget; |
| 295 | auto main_layout = new QVBoxLayout; | 327 | auto main_layout = new QVBoxLayout; |
| 296 | { | 328 | { |
| 329 | auto input_data_group = new QGroupBox(tr("Input Data")); | ||
| 330 | |||
| 331 | // For each vertex attribute, add a QHBoxLayout consisting of: | ||
| 332 | // - A QLabel denoting the source attribute index | ||
| 333 | // - Four QLineEdits for showing and manipulating attribute data | ||
| 334 | // - A QLabel denoting the shader input attribute index | ||
| 335 | auto sub_layout = new QVBoxLayout; | ||
| 336 | for (unsigned i = 0; i < 16; ++i) { | ||
| 337 | // Create an HBoxLayout to store the widgets used to specify a particular attribute | ||
| 338 | // and store it in a QWidget to allow for easy hiding and unhiding. | ||
| 339 | auto row_layout = new QHBoxLayout; | ||
| 340 | row_layout->addWidget(new QLabel(tr("Attribute %1").arg(i, 2))); | ||
| 341 | for (unsigned comp = 0; comp < 4; ++comp) | ||
| 342 | row_layout->addWidget(input_data[4 * i + comp]); | ||
| 343 | |||
| 344 | row_layout->addWidget(input_data_mapping[i] = new QLabel); | ||
| 345 | |||
| 346 | input_data_container[i] = new QWidget; | ||
| 347 | input_data_container[i]->setLayout(row_layout); | ||
| 348 | input_data_container[i]->hide(); | ||
| 349 | |||
| 350 | sub_layout->addWidget(input_data_container[i]); | ||
| 351 | } | ||
| 352 | |||
| 353 | sub_layout->addWidget(breakpoint_warning); | ||
| 354 | breakpoint_warning->hide(); | ||
| 355 | |||
| 356 | input_data_group->setLayout(sub_layout); | ||
| 357 | main_layout->addWidget(input_data_group); | ||
| 358 | } | ||
| 359 | { | ||
| 297 | auto sub_layout = new QHBoxLayout; | 360 | auto sub_layout = new QHBoxLayout; |
| 298 | sub_layout->addWidget(binary_list); | 361 | sub_layout->addWidget(binary_list); |
| 299 | main_layout->addLayout(sub_layout); | 362 | main_layout->addLayout(sub_layout); |
| 300 | } | 363 | } |
| 301 | main_layout->addWidget(dump_shader); | 364 | main_layout->addWidget(dump_shader); |
| 365 | { | ||
| 366 | auto sub_layout = new QHBoxLayout; | ||
| 367 | sub_layout->addWidget(new QLabel(tr("Iteration Index:"))); | ||
| 368 | sub_layout->addWidget(iteration_index); | ||
| 369 | main_layout->addLayout(sub_layout); | ||
| 370 | } | ||
| 371 | main_layout->addWidget(instruction_description); | ||
| 302 | main_widget->setLayout(main_layout); | 372 | main_widget->setLayout(main_layout); |
| 303 | setWidget(main_widget); | 373 | setWidget(main_widget); |
| 374 | |||
| 375 | widget()->setEnabled(false); | ||
| 304 | } | 376 | } |
| 305 | 377 | ||
| 306 | void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | 378 | void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { |
| 307 | emit Update(); | 379 | auto input = static_cast<Pica::Shader::InputVertex*>(data); |
| 380 | if (event == Pica::DebugContext::Event::VertexLoaded) { | ||
| 381 | Reload(true, data); | ||
| 382 | } else { | ||
| 383 | // No vertex data is retrievable => invalidate currently stored vertex data | ||
| 384 | Reload(true, nullptr); | ||
| 385 | } | ||
| 308 | widget()->setEnabled(true); | 386 | widget()->setEnabled(true); |
| 309 | } | 387 | } |
| 310 | 388 | ||
| 389 | void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_data) { | ||
| 390 | model->beginResetModel(); | ||
| 391 | |||
| 392 | if (replace_vertex_data) { | ||
| 393 | if (vertex_data) { | ||
| 394 | memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); | ||
| 395 | for (unsigned attr = 0; attr < 16; ++attr) { | ||
| 396 | for (unsigned comp = 0; comp < 4; ++comp) { | ||
| 397 | input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | breakpoint_warning->hide(); | ||
| 401 | } else { | ||
| 402 | for (unsigned attr = 0; attr < 16; ++attr) { | ||
| 403 | for (unsigned comp = 0; comp < 4; ++comp) { | ||
| 404 | input_data[4 * attr + comp]->setText(QString("???")); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | breakpoint_warning->show(); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | // Reload shader code | ||
| 412 | info.Clear(); | ||
| 413 | |||
| 414 | auto& shader_setup = Pica::g_state.vs; | ||
| 415 | auto& shader_config = Pica::g_state.regs.vs; | ||
| 416 | for (auto instr : shader_setup.program_code) | ||
| 417 | info.code.push_back({instr}); | ||
| 418 | |||
| 419 | for (auto pattern : shader_setup.swizzle_data) | ||
| 420 | info.swizzle_info.push_back({pattern}); | ||
| 421 | |||
| 422 | u32 entry_point = Pica::g_state.regs.vs.main_offset; | ||
| 423 | info.labels.insert({ entry_point, "main" }); | ||
| 424 | |||
| 425 | // Generate debug information | ||
| 426 | debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, 1, shader_config, shader_setup); | ||
| 427 | |||
| 428 | // Reload widget state | ||
| 429 | |||
| 430 | // Only show input attributes which are used as input to the shader | ||
| 431 | for (unsigned int attr = 0; attr < 16; ++attr) { | ||
| 432 | input_data_container[attr]->setVisible(false); | ||
| 433 | } | ||
| 434 | for (unsigned int attr = 0; attr < Pica::g_state.regs.vertex_attributes.GetNumTotalAttributes(); ++attr) { | ||
| 435 | unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr); | ||
| 436 | input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr)); | ||
| 437 | input_data_container[source_attr]->setVisible(true); | ||
| 438 | } | ||
| 439 | |||
| 440 | // Initialize debug info text for current iteration count | ||
| 441 | iteration_index->setMaximum(debug_data.records.size() - 1); | ||
| 442 | OnIterationIndexChanged(iteration_index->value()); | ||
| 443 | |||
| 444 | model->endResetModel(); | ||
| 445 | } | ||
| 446 | |||
| 311 | void GraphicsVertexShaderWidget::OnResumed() { | 447 | void GraphicsVertexShaderWidget::OnResumed() { |
| 312 | widget()->setEnabled(false); | 448 | widget()->setEnabled(false); |
| 313 | } | 449 | } |
| 450 | |||
| 451 | void GraphicsVertexShaderWidget::OnInputAttributeChanged(int index) { | ||
| 452 | float value = input_data[index]->text().toFloat(); | ||
| 453 | Reload(); | ||
| 454 | } | ||
| 455 | |||
| 456 | void GraphicsVertexShaderWidget::OnIterationIndexChanged(int index) { | ||
| 457 | QString text; | ||
| 458 | |||
| 459 | auto& record = debug_data.records[index]; | ||
| 460 | if (record.mask & Pica::Shader::DebugDataRecord::SRC1) | ||
| 461 | text += tr("SRC1: %1, %2, %3, %4\n").arg(record.src1.x.ToFloat32()).arg(record.src1.y.ToFloat32()).arg(record.src1.z.ToFloat32()).arg(record.src1.w.ToFloat32()); | ||
| 462 | if (record.mask & Pica::Shader::DebugDataRecord::SRC2) | ||
| 463 | text += tr("SRC2: %1, %2, %3, %4\n").arg(record.src2.x.ToFloat32()).arg(record.src2.y.ToFloat32()).arg(record.src2.z.ToFloat32()).arg(record.src2.w.ToFloat32()); | ||
| 464 | if (record.mask & Pica::Shader::DebugDataRecord::SRC3) | ||
| 465 | text += tr("SRC3: %1, %2, %3, %4\n").arg(record.src3.x.ToFloat32()).arg(record.src3.y.ToFloat32()).arg(record.src3.z.ToFloat32()).arg(record.src3.w.ToFloat32()); | ||
| 466 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) | ||
| 467 | text += tr("DEST_IN: %1, %2, %3, %4\n").arg(record.dest_in.x.ToFloat32()).arg(record.dest_in.y.ToFloat32()).arg(record.dest_in.z.ToFloat32()).arg(record.dest_in.w.ToFloat32()); | ||
| 468 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) | ||
| 469 | text += tr("DEST_OUT: %1, %2, %3, %4\n").arg(record.dest_out.x.ToFloat32()).arg(record.dest_out.y.ToFloat32()).arg(record.dest_out.z.ToFloat32()).arg(record.dest_out.w.ToFloat32()); | ||
| 470 | |||
| 471 | if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) | ||
| 472 | text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); | ||
| 473 | if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) | ||
| 474 | text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); | ||
| 475 | |||
| 476 | if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) | ||
| 477 | text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); | ||
| 478 | if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) | ||
| 479 | text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); | ||
| 480 | if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) | ||
| 481 | text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n").arg(record.loop_int.x).arg(record.loop_int.y).arg(record.loop_int.z).arg(record.loop_int.w); | ||
| 482 | |||
| 483 | text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); | ||
| 484 | if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { | ||
| 485 | text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); | ||
| 486 | } else { | ||
| 487 | text += tr(" (last instruction)"); | ||
| 488 | } | ||
| 489 | |||
| 490 | instruction_description->setText(text); | ||
| 491 | |||
| 492 | // Scroll to current instruction | ||
| 493 | const QModelIndex& instr_index = model->index(record.instruction_offset, 0); | ||
| 494 | emit SelectCommand(instr_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); | ||
| 495 | binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); | ||
| 496 | } | ||
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics_vertex_shader.h index 5dc9e3703..1b46aa0d9 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.h +++ b/src/citra_qt/debugger/graphics_vertex_shader.h | |||
| @@ -10,11 +10,18 @@ | |||
| 10 | 10 | ||
| 11 | #include "nihstro/parser_shbin.h" | 11 | #include "nihstro/parser_shbin.h" |
| 12 | 12 | ||
| 13 | #include "video_core/shader/shader.h" | ||
| 14 | |||
| 15 | class QLabel; | ||
| 16 | class QSpinBox; | ||
| 17 | |||
| 18 | class GraphicsVertexShaderWidget; | ||
| 19 | |||
| 13 | class GraphicsVertexShaderModel : public QAbstractItemModel { | 20 | class GraphicsVertexShaderModel : public QAbstractItemModel { |
| 14 | Q_OBJECT | 21 | Q_OBJECT |
| 15 | 22 | ||
| 16 | public: | 23 | public: |
| 17 | GraphicsVertexShaderModel(QObject* parent); | 24 | GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent); |
| 18 | 25 | ||
| 19 | QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | 26 | QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; |
| 20 | QModelIndex parent(const QModelIndex& child) const override; | 27 | QModelIndex parent(const QModelIndex& child) const override; |
| @@ -23,13 +30,10 @@ public: | |||
| 23 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | 30 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |
| 24 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | 31 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |
| 25 | 32 | ||
| 26 | public slots: | ||
| 27 | void OnUpdate(); | ||
| 28 | |||
| 29 | void DumpShader(); | ||
| 30 | |||
| 31 | private: | 33 | private: |
| 32 | nihstro::ShaderInfo info; | 34 | GraphicsVertexShaderWidget* par; |
| 35 | |||
| 36 | friend class GraphicsVertexShaderWidget; | ||
| 33 | }; | 37 | }; |
| 34 | 38 | ||
| 35 | class GraphicsVertexShaderWidget : public BreakPointObserverDock { | 39 | class GraphicsVertexShaderWidget : public BreakPointObserverDock { |
| @@ -45,9 +49,41 @@ private slots: | |||
| 45 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | 49 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; |
| 46 | void OnResumed() override; | 50 | void OnResumed() override; |
| 47 | 51 | ||
| 52 | void OnInputAttributeChanged(int index); | ||
| 53 | |||
| 54 | void OnIterationIndexChanged(int index); | ||
| 55 | |||
| 56 | void DumpShader(); | ||
| 57 | |||
| 58 | /** Reload widget based on the current PICA200 state | ||
| 59 | * @param replace_vertex_data If true, invalidate all current vertex data | ||
| 60 | * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to specify that no valid vertex data can be retrieved currently. Only used if replace_vertex_data is true. | ||
| 61 | */ | ||
| 62 | void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr); | ||
| 63 | |||
| 64 | |||
| 48 | signals: | 65 | signals: |
| 49 | void Update(); | 66 | // Call this to change the current command selection in the disassembly view |
| 67 | void SelectCommand(const QModelIndex&, QItemSelectionModel::SelectionFlags); | ||
| 50 | 68 | ||
| 51 | private: | 69 | private: |
| 70 | QLabel* instruction_description; | ||
| 71 | QTreeView* binary_list; | ||
| 72 | GraphicsVertexShaderModel* model; | ||
| 73 | |||
| 74 | // TODO: Move these into a single struct | ||
| 75 | std::array<QLineEdit*, 4*16> input_data; // A text box for each of the 4 components of up to 16 vertex attributes | ||
| 76 | std::array<QWidget*, 16> input_data_container; // QWidget containing the QLayout containing each vertex attribute | ||
| 77 | std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute which the vertex attribute maps to | ||
| 78 | |||
| 79 | // Text to be shown when input vertex data is not retrievable | ||
| 80 | QLabel* breakpoint_warning; | ||
| 81 | |||
| 82 | QSpinBox* iteration_index; | ||
| 83 | |||
| 84 | nihstro::ShaderInfo info; | ||
| 85 | Pica::Shader::DebugData<true> debug_data; | ||
| 86 | Pica::Shader::InputVertex input_vertex; | ||
| 52 | 87 | ||
| 88 | friend class GraphicsVertexShaderModel; | ||
| 53 | }; | 89 | }; |