diff options
Diffstat (limited to 'src/citra_qt/debugger/graphics_vertex_shader.cpp')
| -rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.cpp | 214 |
1 files changed, 134 insertions, 80 deletions
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp index 391666d35..0b4320da5 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <iomanip> | 5 | #include <iomanip> |
| 6 | #include <sstream> | 6 | #include <sstream> |
| 7 | |||
| 8 | #include <QBoxLayout> | 7 | #include <QBoxLayout> |
| 9 | #include <QFileDialog> | 8 | #include <QFileDialog> |
| 10 | #include <QFormLayout> | 9 | #include <QFormLayout> |
| @@ -15,10 +14,8 @@ | |||
| 15 | #include <QSignalMapper> | 14 | #include <QSignalMapper> |
| 16 | #include <QSpinBox> | 15 | #include <QSpinBox> |
| 17 | #include <QTreeView> | 16 | #include <QTreeView> |
| 18 | |||
| 19 | #include "citra_qt/debugger/graphics_vertex_shader.h" | 17 | #include "citra_qt/debugger/graphics_vertex_shader.h" |
| 20 | #include "citra_qt/util/util.h" | 18 | #include "citra_qt/util/util.h" |
| 21 | |||
| 22 | #include "video_core/pica.h" | 19 | #include "video_core/pica.h" |
| 23 | #include "video_core/pica_state.h" | 20 | #include "video_core/pica_state.h" |
| 24 | #include "video_core/shader/shader.h" | 21 | #include "video_core/shader/shader.h" |
| @@ -28,9 +25,8 @@ using nihstro::Instruction; | |||
| 28 | using nihstro::SourceRegister; | 25 | using nihstro::SourceRegister; |
| 29 | using nihstro::SwizzlePattern; | 26 | using nihstro::SwizzlePattern; |
| 30 | 27 | ||
| 31 | GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent): QAbstractTableModel(parent), par(parent) { | 28 | GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent) |
| 32 | 29 | : QAbstractTableModel(parent), par(parent) {} | |
| 33 | } | ||
| 34 | 30 | ||
| 35 | int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | 31 | int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { |
| 36 | return 3; | 32 | return 3; |
| @@ -40,10 +36,10 @@ int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | |||
| 40 | return static_cast<int>(par->info.code.size()); | 36 | return static_cast<int>(par->info.code.size()); |
| 41 | } | 37 | } |
| 42 | 38 | ||
| 43 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | 39 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, |
| 44 | switch(role) { | 40 | int role) const { |
| 45 | case Qt::DisplayRole: | 41 | switch (role) { |
| 46 | { | 42 | case Qt::DisplayRole: { |
| 47 | if (section == 0) { | 43 | if (section == 0) { |
| 48 | return tr("Offset"); | 44 | return tr("Offset"); |
| 49 | } else if (section == 1) { | 45 | } else if (section == 1) { |
| @@ -69,8 +65,8 @@ static std::string SelectorToString(u32 selector) { | |||
| 69 | } | 65 | } |
| 70 | 66 | ||
| 71 | // e.g. "-c92[a0.x].xyzw" | 67 | // e.g. "-c92[a0.x].xyzw" |
| 72 | static void print_input(std::ostringstream& output, const SourceRegister& input, | 68 | static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate, |
| 73 | bool negate, const std::string& swizzle_mask, bool align = true, | 69 | const std::string& swizzle_mask, bool align = true, |
| 74 | const std::string& address_register_name = std::string()) { | 70 | const std::string& address_register_name = std::string()) { |
| 75 | if (align) | 71 | if (align) |
| 76 | output << std::setw(4) << std::right; | 72 | output << std::setw(4) << std::right; |
| @@ -83,20 +79,18 @@ static void print_input(std::ostringstream& output, const SourceRegister& input, | |||
| 83 | 79 | ||
| 84 | QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { | 80 | QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { |
| 85 | switch (role) { | 81 | switch (role) { |
| 86 | case Qt::DisplayRole: | 82 | case Qt::DisplayRole: { |
| 87 | { | ||
| 88 | switch (index.column()) { | 83 | switch (index.column()) { |
| 89 | case 0: | 84 | case 0: |
| 90 | if (par->info.HasLabel(index.row())) | 85 | if (par->info.HasLabel(index.row())) |
| 91 | return QString::fromStdString(par->info.GetLabel(index.row())); | 86 | return QString::fromStdString(par->info.GetLabel(index.row())); |
| 92 | 87 | ||
| 93 | return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); | 88 | return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0')); |
| 94 | 89 | ||
| 95 | case 1: | 90 | case 1: |
| 96 | return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); | 91 | return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0')); |
| 97 | 92 | ||
| 98 | case 2: | 93 | case 2: { |
| 99 | { | ||
| 100 | std::ostringstream output; | 94 | std::ostringstream output; |
| 101 | output.flags(std::ios::uppercase); | 95 | output.flags(std::ios::uppercase); |
| 102 | 96 | ||
| @@ -117,8 +111,9 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 117 | const Instruction instr = par->info.code[index.row()]; | 111 | const Instruction instr = par->info.code[index.row()]; |
| 118 | const OpCode opcode = instr.opcode; | 112 | const OpCode opcode = instr.opcode; |
| 119 | const OpCode::Info opcode_info = opcode.GetInfo(); | 113 | const OpCode::Info opcode_info = opcode.GetInfo(); |
| 120 | const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd ? | 114 | const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd |
| 121 | instr.mad.operand_desc_id.Value() : instr.common.operand_desc_id.Value(); | 115 | ? instr.mad.operand_desc_id.Value() |
| 116 | : instr.common.operand_desc_id.Value(); | ||
| 122 | const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; | 117 | const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern; |
| 123 | 118 | ||
| 124 | // longest known instruction name: "setemit " | 119 | // longest known instruction name: "setemit " |
| @@ -136,15 +131,14 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 136 | break; | 131 | break; |
| 137 | 132 | ||
| 138 | case OpCode::Type::Arithmetic: | 133 | case OpCode::Type::Arithmetic: |
| 139 | case OpCode::Type::MultiplyAdd: | 134 | case OpCode::Type::MultiplyAdd: { |
| 140 | { | ||
| 141 | // Use custom code for special instructions | 135 | // Use custom code for special instructions |
| 142 | switch (opcode.EffectiveOpCode()) { | 136 | switch (opcode.EffectiveOpCode()) { |
| 143 | case OpCode::Id::CMP: | 137 | case OpCode::Id::CMP: { |
| 144 | { | ||
| 145 | AlignToColumn(kOpcodeColumnWidth); | 138 | AlignToColumn(kOpcodeColumnWidth); |
| 146 | 139 | ||
| 147 | // NOTE: CMP always writes both cc components, so we do not consider the dest mask here. | 140 | // NOTE: CMP always writes both cc components, so we do not consider the dest |
| 141 | // mask here. | ||
| 148 | output << " cc.xy"; | 142 | output << " cc.xy"; |
| 149 | AlignToColumn(kOutputColumnWidth); | 143 | AlignToColumn(kOutputColumnWidth); |
| 150 | 144 | ||
| @@ -152,22 +146,29 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 152 | SourceRegister src2 = instr.common.GetSrc2(false); | 146 | SourceRegister src2 = instr.common.GetSrc2(false); |
| 153 | 147 | ||
| 154 | output << ' '; | 148 | output << ' '; |
| 155 | print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), false, instr.common.AddressRegisterName()); | 149 | print_input(output, src1, swizzle.negate_src1, |
| 156 | output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) << ' '; | 150 | swizzle.SelectorToString(false).substr(0, 1), false, |
| 157 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(0,1), false); | 151 | instr.common.AddressRegisterName()); |
| 152 | output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x) | ||
| 153 | << ' '; | ||
| 154 | print_input(output, src2, swizzle.negate_src2, | ||
| 155 | swizzle.SelectorToString(true).substr(0, 1), false); | ||
| 158 | 156 | ||
| 159 | output << ", "; | 157 | output << ", "; |
| 160 | 158 | ||
| 161 | print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), false, instr.common.AddressRegisterName()); | 159 | print_input(output, src1, swizzle.negate_src1, |
| 162 | output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) << ' '; | 160 | swizzle.SelectorToString(false).substr(1, 1), false, |
| 163 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true).substr(1,1), false); | 161 | instr.common.AddressRegisterName()); |
| 162 | output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y) | ||
| 163 | << ' '; | ||
| 164 | print_input(output, src2, swizzle.negate_src2, | ||
| 165 | swizzle.SelectorToString(true).substr(1, 1), false); | ||
| 164 | 166 | ||
| 165 | break; | 167 | break; |
| 166 | } | 168 | } |
| 167 | 169 | ||
| 168 | case OpCode::Id::MAD: | 170 | case OpCode::Id::MAD: |
| 169 | case OpCode::Id::MADI: | 171 | case OpCode::Id::MADI: { |
| 170 | { | ||
| 171 | AlignToColumn(kOpcodeColumnWidth); | 172 | AlignToColumn(kOpcodeColumnWidth); |
| 172 | 173 | ||
| 173 | bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); | 174 | bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); |
| @@ -175,34 +176,42 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 175 | SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); | 176 | SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted); |
| 176 | SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); | 177 | SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted); |
| 177 | 178 | ||
| 178 | output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); | 179 | output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.' |
| 180 | << swizzle.DestMaskToString(); | ||
| 179 | AlignToColumn(kOutputColumnWidth); | 181 | AlignToColumn(kOutputColumnWidth); |
| 180 | print_input(output, src1, swizzle.negate_src1, SelectorToString(swizzle.src1_selector)); | 182 | print_input(output, src1, swizzle.negate_src1, |
| 183 | SelectorToString(swizzle.src1_selector)); | ||
| 181 | AlignToColumn(kInputOperandColumnWidth); | 184 | AlignToColumn(kInputOperandColumnWidth); |
| 182 | if (src_is_inverted) { | 185 | if (src_is_inverted) { |
| 183 | print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector)); | 186 | print_input(output, src2, swizzle.negate_src2, |
| 187 | SelectorToString(swizzle.src2_selector)); | ||
| 184 | } else { | 188 | } else { |
| 185 | print_input(output, src2, swizzle.negate_src2, SelectorToString(swizzle.src2_selector), true, instr.mad.AddressRegisterName()); | 189 | print_input(output, src2, swizzle.negate_src2, |
| 190 | SelectorToString(swizzle.src2_selector), true, | ||
| 191 | instr.mad.AddressRegisterName()); | ||
| 186 | } | 192 | } |
| 187 | AlignToColumn(kInputOperandColumnWidth); | 193 | AlignToColumn(kInputOperandColumnWidth); |
| 188 | if (src_is_inverted) { | 194 | if (src_is_inverted) { |
| 189 | print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector), true, instr.mad.AddressRegisterName()); | 195 | print_input(output, src3, swizzle.negate_src3, |
| 196 | SelectorToString(swizzle.src3_selector), true, | ||
| 197 | instr.mad.AddressRegisterName()); | ||
| 190 | } else { | 198 | } else { |
| 191 | print_input(output, src3, swizzle.negate_src3, SelectorToString(swizzle.src3_selector)); | 199 | print_input(output, src3, swizzle.negate_src3, |
| 200 | SelectorToString(swizzle.src3_selector)); | ||
| 192 | } | 201 | } |
| 193 | AlignToColumn(kInputOperandColumnWidth); | 202 | AlignToColumn(kInputOperandColumnWidth); |
| 194 | break; | 203 | break; |
| 195 | } | 204 | } |
| 196 | 205 | ||
| 197 | default: | 206 | default: { |
| 198 | { | ||
| 199 | AlignToColumn(kOpcodeColumnWidth); | 207 | AlignToColumn(kOpcodeColumnWidth); |
| 200 | 208 | ||
| 201 | bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); | 209 | bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed); |
| 202 | 210 | ||
| 203 | if (opcode_info.subtype & OpCode::Info::Dest) { | 211 | if (opcode_info.subtype & OpCode::Info::Dest) { |
| 204 | // e.g. "r12.xy__" | 212 | // e.g. "r12.xy__" |
| 205 | output << std::setw(3) << std::right << instr.common.dest.Value().GetName() << '.' << swizzle.DestMaskToString(); | 213 | output << std::setw(3) << std::right << instr.common.dest.Value().GetName() |
| 214 | << '.' << swizzle.DestMaskToString(); | ||
| 206 | } else if (opcode_info.subtype == OpCode::Info::MOVA) { | 215 | } else if (opcode_info.subtype == OpCode::Info::MOVA) { |
| 207 | output << " a0." << swizzle.DestMaskToString(); | 216 | output << " a0." << swizzle.DestMaskToString(); |
| 208 | } | 217 | } |
| @@ -210,14 +219,18 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 210 | 219 | ||
| 211 | if (opcode_info.subtype & OpCode::Info::Src1) { | 220 | if (opcode_info.subtype & OpCode::Info::Src1) { |
| 212 | SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); | 221 | SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); |
| 213 | print_input(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), true, instr.common.AddressRegisterName()); | 222 | print_input(output, src1, swizzle.negate_src1, |
| 223 | swizzle.SelectorToString(false), true, | ||
| 224 | instr.common.AddressRegisterName()); | ||
| 214 | AlignToColumn(kInputOperandColumnWidth); | 225 | AlignToColumn(kInputOperandColumnWidth); |
| 215 | } | 226 | } |
| 216 | 227 | ||
| 217 | // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 | 228 | // TODO: In some cases, the Address Register is used as an index for SRC2 |
| 229 | // instead of SRC1 | ||
| 218 | if (opcode_info.subtype & OpCode::Info::Src2) { | 230 | if (opcode_info.subtype & OpCode::Info::Src2) { |
| 219 | SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); | 231 | SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); |
| 220 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(true)); | 232 | print_input(output, src2, swizzle.negate_src2, |
| 233 | swizzle.SelectorToString(true)); | ||
| 221 | AlignToColumn(kInputOperandColumnWidth); | 234 | AlignToColumn(kInputOperandColumnWidth); |
| 222 | } | 235 | } |
| 223 | break; | 236 | break; |
| @@ -228,8 +241,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 228 | } | 241 | } |
| 229 | 242 | ||
| 230 | case OpCode::Type::Conditional: | 243 | case OpCode::Type::Conditional: |
| 231 | case OpCode::Type::UniformFlowControl: | 244 | case OpCode::Type::UniformFlowControl: { |
| 232 | { | ||
| 233 | output << ' '; | 245 | output << ' '; |
| 234 | 246 | ||
| 235 | switch (opcode.EffectiveOpCode()) { | 247 | switch (opcode.EffectiveOpCode()) { |
| @@ -242,7 +254,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 242 | output << '('; | 254 | output << '('; |
| 243 | 255 | ||
| 244 | if (instr.flow_control.op != instr.flow_control.JustY) { | 256 | if (instr.flow_control.op != instr.flow_control.JustY) { |
| 245 | if (instr.flow_control.refx) output << '!'; | 257 | if (instr.flow_control.refx) |
| 258 | output << '!'; | ||
| 246 | output << "cc.x"; | 259 | output << "cc.x"; |
| 247 | } | 260 | } |
| 248 | 261 | ||
| @@ -253,7 +266,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 253 | } | 266 | } |
| 254 | 267 | ||
| 255 | if (instr.flow_control.op != instr.flow_control.JustX) { | 268 | if (instr.flow_control.op != instr.flow_control.JustX) { |
| 256 | if (instr.flow_control.refy) output << '!'; | 269 | if (instr.flow_control.refy) |
| 270 | output << '!'; | ||
| 257 | output << "cc.y"; | 271 | output << "cc.y"; |
| 258 | } | 272 | } |
| 259 | 273 | ||
| @@ -266,17 +280,23 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 266 | u32 target_addr_else = instr.flow_control.dest_offset; | 280 | u32 target_addr_else = instr.flow_control.dest_offset; |
| 267 | 281 | ||
| 268 | if (opcode_info.subtype & OpCode::Info::HasAlternative) { | 282 | if (opcode_info.subtype & OpCode::Info::HasAlternative) { |
| 269 | output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); | 283 | output << "else jump to 0x" << std::setw(4) << std::right |
| 284 | << std::setfill('0') << std::hex | ||
| 285 | << (4 * instr.flow_control.dest_offset); | ||
| 270 | } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { | 286 | } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) { |
| 271 | output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex << (4 * instr.flow_control.dest_offset); | 287 | output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') |
| 288 | << std::hex << (4 * instr.flow_control.dest_offset); | ||
| 272 | } else { | 289 | } else { |
| 273 | // TODO: Handle other cases | 290 | // TODO: Handle other cases |
| 274 | output << "(unknown destination)"; | 291 | output << "(unknown destination)"; |
| 275 | } | 292 | } |
| 276 | 293 | ||
| 277 | if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { | 294 | if (opcode_info.subtype & OpCode::Info::HasFinishPoint) { |
| 278 | output << " (return on 0x" << std::setw(4) << std::right << std::setfill('0') << std::hex | 295 | output << " (return on 0x" << std::setw(4) << std::right |
| 279 | << (4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions) << ')'; | 296 | << std::setfill('0') << std::hex |
| 297 | << (4 * instr.flow_control.dest_offset + | ||
| 298 | 4 * instr.flow_control.num_instructions) | ||
| 299 | << ')'; | ||
| 280 | } | 300 | } |
| 281 | 301 | ||
| 282 | break; | 302 | break; |
| @@ -300,8 +320,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 300 | case Qt::FontRole: | 320 | case Qt::FontRole: |
| 301 | return GetMonospaceFont(); | 321 | return GetMonospaceFont(); |
| 302 | 322 | ||
| 303 | case Qt::BackgroundRole: | 323 | case Qt::BackgroundRole: { |
| 304 | { | ||
| 305 | // Highlight current instruction | 324 | // Highlight current instruction |
| 306 | int current_record_index = par->cycle_index->value(); | 325 | int current_record_index = par->cycle_index->value(); |
| 307 | if (current_record_index < static_cast<int>(par->debug_data.records.size())) { | 326 | if (current_record_index < static_cast<int>(par->debug_data.records.size())) { |
| @@ -319,10 +338,8 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 319 | return QBrush(QColor(192, 192, 192)); | 338 | return QBrush(QColor(192, 192, 192)); |
| 320 | } | 339 | } |
| 321 | 340 | ||
| 322 | |||
| 323 | // TODO: Draw arrows for each "reachable" instruction to visualize control flow | 341 | // TODO: Draw arrows for each "reachable" instruction to visualize control flow |
| 324 | 342 | ||
| 325 | |||
| 326 | default: | 343 | default: |
| 327 | break; | 344 | break; |
| 328 | } | 345 | } |
| @@ -331,23 +348,24 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 331 | } | 348 | } |
| 332 | 349 | ||
| 333 | void GraphicsVertexShaderWidget::DumpShader() { | 350 | void GraphicsVertexShaderWidget::DumpShader() { |
| 334 | QString filename = QFileDialog::getSaveFileName(this, tr("Save Shader Dump"), "shader_dump.shbin", | 351 | QString filename = QFileDialog::getSaveFileName( |
| 335 | tr("Shader Binary (*.shbin)")); | 352 | this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)")); |
| 336 | 353 | ||
| 337 | if (filename.isEmpty()) { | 354 | if (filename.isEmpty()) { |
| 338 | // If the user canceled the dialog, don't dump anything. | 355 | // If the user canceled the dialog, don't dump anything. |
| 339 | return; | 356 | return; |
| 340 | } | 357 | } |
| 341 | 358 | ||
| 342 | auto& setup = Pica::g_state.vs; | 359 | auto& setup = Pica::g_state.vs; |
| 343 | auto& config = Pica::g_state.regs.vs; | 360 | auto& config = Pica::g_state.regs.vs; |
| 344 | 361 | ||
| 345 | Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, Pica::g_state.regs.vs_output_attributes); | 362 | Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup, |
| 363 | Pica::g_state.regs.vs_output_attributes); | ||
| 346 | } | 364 | } |
| 347 | 365 | ||
| 348 | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, | 366 | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget( |
| 349 | QWidget* parent) | 367 | std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) |
| 350 | : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { | 368 | : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { |
| 351 | setObjectName("PicaVertexShader"); | 369 | setObjectName("PicaVertexShader"); |
| 352 | 370 | ||
| 353 | // Clear input vertex data so that it contains valid float values in case a debug shader | 371 | // Clear input vertex data so that it contains valid float values in case a debug shader |
| @@ -365,7 +383,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De | |||
| 365 | input_data[i]->setValidator(new QDoubleValidator(input_data[i])); | 383 | input_data[i]->setValidator(new QDoubleValidator(input_data[i])); |
| 366 | } | 384 | } |
| 367 | 385 | ||
| 368 | breakpoint_warning = new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); | 386 | breakpoint_warning = |
| 387 | new QLabel(tr("(data only available at vertex shader invocation breakpoints)")); | ||
| 369 | 388 | ||
| 370 | // TODO: Add some button for jumping to the shader entry point | 389 | // TODO: Add some button for jumping to the shader entry point |
| 371 | 390 | ||
| @@ -442,7 +461,8 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::De | |||
| 442 | 461 | ||
| 443 | // Set a minimum height so that the size of this label doesn't cause the rest of the bottom | 462 | // Set a minimum height so that the size of this label doesn't cause the rest of the bottom |
| 444 | // part of the UI to keep jumping up and down when cycling through instructions. | 463 | // part of the UI to keep jumping up and down when cycling through instructions. |
| 445 | instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * 6); | 464 | instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() * |
| 465 | 6); | ||
| 446 | instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); | 466 | instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop); |
| 447 | main_layout->addWidget(instruction_description); | 467 | main_layout->addWidget(instruction_description); |
| 448 | 468 | ||
| @@ -471,7 +491,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d | |||
| 471 | memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); | 491 | memcpy(&input_vertex, vertex_data, sizeof(input_vertex)); |
| 472 | for (unsigned attr = 0; attr < 16; ++attr) { | 492 | for (unsigned attr = 0; attr < 16; ++attr) { |
| 473 | for (unsigned comp = 0; comp < 4; ++comp) { | 493 | for (unsigned comp = 0; comp < 4; ++comp) { |
| 474 | input_data[4 * attr + comp]->setText(QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); | 494 | input_data[4 * attr + comp]->setText( |
| 495 | QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32())); | ||
| 475 | } | 496 | } |
| 476 | } | 497 | } |
| 477 | breakpoint_warning->hide(); | 498 | breakpoint_warning->hide(); |
| @@ -498,10 +519,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d | |||
| 498 | info.swizzle_info.push_back({pattern}); | 519 | info.swizzle_info.push_back({pattern}); |
| 499 | 520 | ||
| 500 | u32 entry_point = Pica::g_state.regs.vs.main_offset; | 521 | u32 entry_point = Pica::g_state.regs.vs.main_offset; |
| 501 | info.labels.insert({ entry_point, "main" }); | 522 | info.labels.insert({entry_point, "main"}); |
| 502 | 523 | ||
| 503 | // Generate debug information | 524 | // Generate debug information |
| 504 | debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); | 525 | debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, |
| 526 | shader_setup); | ||
| 505 | 527 | ||
| 506 | // Reload widget state | 528 | // Reload widget state |
| 507 | for (int attr = 0; attr < num_attributes; ++attr) { | 529 | for (int attr = 0; attr < num_attributes; ++attr) { |
| @@ -537,29 +559,60 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) { | |||
| 537 | 559 | ||
| 538 | auto& record = debug_data.records[index]; | 560 | auto& record = debug_data.records[index]; |
| 539 | if (record.mask & Pica::Shader::DebugDataRecord::SRC1) | 561 | if (record.mask & Pica::Shader::DebugDataRecord::SRC1) |
| 540 | 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()); | 562 | text += tr("SRC1: %1, %2, %3, %4\n") |
| 563 | .arg(record.src1.x.ToFloat32()) | ||
| 564 | .arg(record.src1.y.ToFloat32()) | ||
| 565 | .arg(record.src1.z.ToFloat32()) | ||
| 566 | .arg(record.src1.w.ToFloat32()); | ||
| 541 | if (record.mask & Pica::Shader::DebugDataRecord::SRC2) | 567 | if (record.mask & Pica::Shader::DebugDataRecord::SRC2) |
| 542 | 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()); | 568 | text += tr("SRC2: %1, %2, %3, %4\n") |
| 569 | .arg(record.src2.x.ToFloat32()) | ||
| 570 | .arg(record.src2.y.ToFloat32()) | ||
| 571 | .arg(record.src2.z.ToFloat32()) | ||
| 572 | .arg(record.src2.w.ToFloat32()); | ||
| 543 | if (record.mask & Pica::Shader::DebugDataRecord::SRC3) | 573 | if (record.mask & Pica::Shader::DebugDataRecord::SRC3) |
| 544 | 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()); | 574 | text += tr("SRC3: %1, %2, %3, %4\n") |
| 575 | .arg(record.src3.x.ToFloat32()) | ||
| 576 | .arg(record.src3.y.ToFloat32()) | ||
| 577 | .arg(record.src3.z.ToFloat32()) | ||
| 578 | .arg(record.src3.w.ToFloat32()); | ||
| 545 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) | 579 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN) |
| 546 | 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()); | 580 | text += tr("DEST_IN: %1, %2, %3, %4\n") |
| 581 | .arg(record.dest_in.x.ToFloat32()) | ||
| 582 | .arg(record.dest_in.y.ToFloat32()) | ||
| 583 | .arg(record.dest_in.z.ToFloat32()) | ||
| 584 | .arg(record.dest_in.w.ToFloat32()); | ||
| 547 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) | 585 | if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT) |
| 548 | 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()); | 586 | text += tr("DEST_OUT: %1, %2, %3, %4\n") |
| 587 | .arg(record.dest_out.x.ToFloat32()) | ||
| 588 | .arg(record.dest_out.y.ToFloat32()) | ||
| 589 | .arg(record.dest_out.z.ToFloat32()) | ||
| 590 | .arg(record.dest_out.w.ToFloat32()); | ||
| 549 | 591 | ||
| 550 | if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) | 592 | if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT) |
| 551 | text += tr("Addres Registers: %1, %2\n").arg(record.address_registers[0]).arg(record.address_registers[1]); | 593 | text += tr("Addres Registers: %1, %2\n") |
| 594 | .arg(record.address_registers[0]) | ||
| 595 | .arg(record.address_registers[1]); | ||
| 552 | if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) | 596 | if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT) |
| 553 | text += tr("Compare Result: %1, %2\n").arg(record.conditional_code[0] ? "true" : "false").arg(record.conditional_code[1] ? "true" : "false"); | 597 | text += tr("Compare Result: %1, %2\n") |
| 598 | .arg(record.conditional_code[0] ? "true" : "false") | ||
| 599 | .arg(record.conditional_code[1] ? "true" : "false"); | ||
| 554 | 600 | ||
| 555 | if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) | 601 | if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN) |
| 556 | text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); | 602 | text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false"); |
| 557 | if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) | 603 | if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN) |
| 558 | text += tr("Dynamic Conditions: %1, %2\n").arg(record.cond_cmp[0] ? "true" : "false").arg(record.cond_cmp[1] ? "true" : "false"); | 604 | text += tr("Dynamic Conditions: %1, %2\n") |
| 605 | .arg(record.cond_cmp[0] ? "true" : "false") | ||
| 606 | .arg(record.cond_cmp[1] ? "true" : "false"); | ||
| 559 | if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) | 607 | if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN) |
| 560 | 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); | 608 | text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n") |
| 561 | 609 | .arg(record.loop_int.x) | |
| 562 | text += tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); | 610 | .arg(record.loop_int.y) |
| 611 | .arg(record.loop_int.z) | ||
| 612 | .arg(record.loop_int.w); | ||
| 613 | |||
| 614 | text += | ||
| 615 | tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0')); | ||
| 563 | if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { | 616 | if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) { |
| 564 | text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); | 617 | text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0')); |
| 565 | } else { | 618 | } else { |
| @@ -570,6 +623,7 @@ void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) { | |||
| 570 | 623 | ||
| 571 | // Emit model update notification and scroll to current instruction | 624 | // Emit model update notification and scroll to current instruction |
| 572 | QModelIndex instr_index = model->index(record.instruction_offset, 0); | 625 | QModelIndex instr_index = model->index(record.instruction_offset, 0); |
| 573 | emit model->dataChanged(instr_index, model->index(record.instruction_offset, model->columnCount())); | 626 | emit model->dataChanged(instr_index, |
| 627 | model->index(record.instruction_offset, model->columnCount())); | ||
| 574 | binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); | 628 | binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible); |
| 575 | } | 629 | } |