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