/ src / video_core / debug_utils / debug_utils.cpp
debug_utils.cpp
  1  // Copyright 2014 Citra Emulator Project
  2  // Licensed under GPLv2
  3  // Refer to the license.txt file included.
  4  
  5  #include <cstdint>
  6  #include <cstring>
  7  #include <fstream>
  8  #include <stdexcept>
  9  #include <nihstro/bit_field.h>
 10  #include <nihstro/float24.h>
 11  #include <nihstro/shader_binary.h>
 12  #include "common/assert.h"
 13  #include "common/bit_field.h"
 14  #include "common/vector_math.h"
 15  #include "core/core.h"
 16  #include "video_core/debug_utils/debug_utils.h"
 17  #include "video_core/gpu.h"
 18  #include "video_core/pica/regs_shader.h"
 19  #include "video_core/pica/shader_setup.h"
 20  #include "video_core/renderer_base.h"
 21  
 22  using nihstro::DVLBHeader;
 23  using nihstro::DVLEHeader;
 24  using nihstro::DVLPHeader;
 25  
 26  namespace Pica {
 27  
 28  void DebugContext::DoOnEvent(Event event, const void* data) {
 29      {
 30          std::unique_lock lock{breakpoint_mutex};
 31  
 32          // Commit the rasterizer's caches so framebuffers, render targets, etc. will show on debug
 33          // widgets
 34          Core::System::GetInstance().GPU().Renderer().Rasterizer()->FlushAll();
 35  
 36          // TODO: Should stop the CPU thread here once we multithread emulation.
 37  
 38          active_breakpoint = event;
 39          at_breakpoint = true;
 40  
 41          // Tell all observers that we hit a breakpoint
 42          for (auto& breakpoint_observer : breakpoint_observers) {
 43              breakpoint_observer->OnPicaBreakPointHit(event, data);
 44          }
 45  
 46          // Wait until another thread tells us to Resume()
 47          resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
 48      }
 49  }
 50  
 51  void DebugContext::Resume() {
 52      {
 53          std::lock_guard lock{breakpoint_mutex};
 54  
 55          // Tell all observers that we are about to resume
 56          for (auto& breakpoint_observer : breakpoint_observers) {
 57              breakpoint_observer->OnPicaResume();
 58          }
 59  
 60          // Resume the waiting thread (i.e. OnEvent())
 61          at_breakpoint = false;
 62      }
 63  
 64      resume_from_breakpoint.notify_one();
 65  }
 66  
 67  std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
 68  
 69  namespace DebugUtils {
 70  
 71  void DumpShader(const std::string& filename, const ShaderRegs& config, const ShaderSetup& setup,
 72                  const RasterizerRegs::VSOutputAttributes* output_attributes) {
 73      struct StuffToWrite {
 74          const u8* pointer;
 75          u32 size;
 76      };
 77      std::vector<StuffToWrite> writing_queue;
 78      u32 write_offset = 0;
 79  
 80      auto QueueForWriting = [&writing_queue, &write_offset](const u8* pointer, u32 size) {
 81          writing_queue.push_back({pointer, size});
 82          u32 old_write_offset = write_offset;
 83          write_offset += size;
 84          return old_write_offset;
 85      };
 86  
 87      // First off, try to translate Pica state (one enum for output attribute type and component)
 88      // into shbin format (separate type and component mask).
 89      union OutputRegisterInfo {
 90          enum Type : u64 {
 91              POSITION = 0,
 92              QUATERNION = 1,
 93              COLOR = 2,
 94              TEXCOORD0 = 3,
 95              TEXCOORD1 = 5,
 96              TEXCOORD2 = 6,
 97  
 98              VIEW = 8,
 99          };
100  
101          BitField<0, 64, u64> hex;
102  
103          BitField<0, 16, Type> type;
104          BitField<16, 16, u64> id;
105          BitField<32, 4, u64> component_mask;
106      };
107  
108      // This is put into a try-catch block to make sure we notice unknown configurations.
109      std::vector<OutputRegisterInfo> output_info_table;
110      for (unsigned i = 0; i < 7; ++i) {
111          using OutputAttributes = Pica::RasterizerRegs::VSOutputAttributes;
112  
113          // TODO: It's still unclear how the attribute components map to the register!
114          //       Once we know that, this code probably will not make much sense anymore.
115          std::map<OutputAttributes::Semantic, std::pair<OutputRegisterInfo::Type, u32>> map = {
116              {OutputAttributes::POSITION_X, {OutputRegisterInfo::POSITION, 1}},
117              {OutputAttributes::POSITION_Y, {OutputRegisterInfo::POSITION, 2}},
118              {OutputAttributes::POSITION_Z, {OutputRegisterInfo::POSITION, 4}},
119              {OutputAttributes::POSITION_W, {OutputRegisterInfo::POSITION, 8}},
120              {OutputAttributes::QUATERNION_X, {OutputRegisterInfo::QUATERNION, 1}},
121              {OutputAttributes::QUATERNION_Y, {OutputRegisterInfo::QUATERNION, 2}},
122              {OutputAttributes::QUATERNION_Z, {OutputRegisterInfo::QUATERNION, 4}},
123              {OutputAttributes::QUATERNION_W, {OutputRegisterInfo::QUATERNION, 8}},
124              {OutputAttributes::COLOR_R, {OutputRegisterInfo::COLOR, 1}},
125              {OutputAttributes::COLOR_G, {OutputRegisterInfo::COLOR, 2}},
126              {OutputAttributes::COLOR_B, {OutputRegisterInfo::COLOR, 4}},
127              {OutputAttributes::COLOR_A, {OutputRegisterInfo::COLOR, 8}},
128              {OutputAttributes::TEXCOORD0_U, {OutputRegisterInfo::TEXCOORD0, 1}},
129              {OutputAttributes::TEXCOORD0_V, {OutputRegisterInfo::TEXCOORD0, 2}},
130              {OutputAttributes::TEXCOORD1_U, {OutputRegisterInfo::TEXCOORD1, 1}},
131              {OutputAttributes::TEXCOORD1_V, {OutputRegisterInfo::TEXCOORD1, 2}},
132              {OutputAttributes::TEXCOORD2_U, {OutputRegisterInfo::TEXCOORD2, 1}},
133              {OutputAttributes::TEXCOORD2_V, {OutputRegisterInfo::TEXCOORD2, 2}},
134              {OutputAttributes::VIEW_X, {OutputRegisterInfo::VIEW, 1}},
135              {OutputAttributes::VIEW_Y, {OutputRegisterInfo::VIEW, 2}},
136              {OutputAttributes::VIEW_Z, {OutputRegisterInfo::VIEW, 4}},
137          };
138  
139          for (const auto& semantic : std::vector<OutputAttributes::Semantic>{
140                   output_attributes[i].map_x, output_attributes[i].map_y, output_attributes[i].map_z,
141                   output_attributes[i].map_w}) {
142              if (semantic == OutputAttributes::INVALID)
143                  continue;
144  
145              try {
146                  OutputRegisterInfo::Type type = map.at(semantic).first;
147                  u32 component_mask = map.at(semantic).second;
148  
149                  auto it = std::find_if(output_info_table.begin(), output_info_table.end(),
150                                         [&i, &type](const OutputRegisterInfo& info) {
151                                             return info.id == i && info.type == type;
152                                         });
153  
154                  if (it == output_info_table.end()) {
155                      output_info_table.emplace_back();
156                      output_info_table.back().type.Assign(type);
157                      output_info_table.back().component_mask.Assign(component_mask);
158                      output_info_table.back().id.Assign(i);
159                  } else {
160                      it->component_mask.Assign(it->component_mask | component_mask);
161                  }
162              } catch (const std::out_of_range&) {
163                  DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
164                  LOG_ERROR(HW_GPU,
165                            "Unknown output attribute mapping: {:03x}, {:03x}, {:03x}, {:03x}",
166                            (int)output_attributes[i].map_x.Value(),
167                            (int)output_attributes[i].map_y.Value(),
168                            (int)output_attributes[i].map_z.Value(),
169                            (int)output_attributes[i].map_w.Value());
170              }
171          }
172      }
173  
174      struct {
175          DVLBHeader header;
176          u32 dvle_offset;
177      } dvlb{{DVLBHeader::MAGIC_WORD, 1}}; // 1 DVLE
178  
179      DVLPHeader dvlp{DVLPHeader::MAGIC_WORD};
180      DVLEHeader dvle{DVLEHeader::MAGIC_WORD};
181  
182      QueueForWriting(reinterpret_cast<const u8*>(&dvlb), sizeof(dvlb));
183      u32 dvlp_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvlp), sizeof(dvlp));
184      dvlb.dvle_offset = QueueForWriting(reinterpret_cast<const u8*>(&dvle), sizeof(dvle));
185  
186      // TODO: Reduce the amount of binary code written to relevant portions
187      dvlp.binary_offset = write_offset - dvlp_offset;
188      dvlp.binary_size_words = static_cast<uint32_t>(setup.program_code.size());
189      QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()),
190                      static_cast<u32>(setup.program_code.size()) * sizeof(u32));
191  
192      dvlp.swizzle_info_offset = write_offset - dvlp_offset;
193      dvlp.swizzle_info_num_entries = static_cast<uint32_t>(setup.swizzle_data.size());
194      u32 dummy = 0;
195      for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
196          QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]),
197                          sizeof(setup.swizzle_data[i]));
198          QueueForWriting(reinterpret_cast<const u8*>(&dummy), sizeof(dummy));
199      }
200  
201      dvle.main_offset_words = config.main_offset;
202      dvle.output_register_table_offset = write_offset - dvlb.dvle_offset;
203      dvle.output_register_table_size = static_cast<u32>(output_info_table.size());
204      QueueForWriting(reinterpret_cast<const u8*>(output_info_table.data()),
205                      static_cast<u32>(output_info_table.size() * sizeof(OutputRegisterInfo)));
206  
207      // TODO: Create a label table for "main"
208  
209      std::vector<nihstro::ConstantInfo> constant_table;
210      for (unsigned i = 0; i < setup.uniforms.b.size(); ++i) {
211          nihstro::ConstantInfo constant;
212          std::memset(&constant, 0, sizeof(constant));
213          constant.type = nihstro::ConstantInfo::Bool;
214          constant.regid = i;
215          constant.b = setup.uniforms.b[i];
216          constant_table.emplace_back(constant);
217      }
218      for (unsigned i = 0; i < setup.uniforms.i.size(); ++i) {
219          nihstro::ConstantInfo constant;
220          std::memset(&constant, 0, sizeof(constant));
221          constant.type = nihstro::ConstantInfo::Int;
222          constant.regid = i;
223          constant.i.x = setup.uniforms.i[i].x;
224          constant.i.y = setup.uniforms.i[i].y;
225          constant.i.z = setup.uniforms.i[i].z;
226          constant.i.w = setup.uniforms.i[i].w;
227          constant_table.emplace_back(constant);
228      }
229      for (unsigned i = 0; i < sizeof(setup.uniforms.f) / sizeof(setup.uniforms.f[0]); ++i) {
230          nihstro::ConstantInfo constant;
231          std::memset(&constant, 0, sizeof(constant));
232          constant.type = nihstro::ConstantInfo::Float;
233          constant.regid = i;
234          constant.f.x = nihstro::to_float24(setup.uniforms.f[i].x.ToFloat32());
235          constant.f.y = nihstro::to_float24(setup.uniforms.f[i].y.ToFloat32());
236          constant.f.z = nihstro::to_float24(setup.uniforms.f[i].z.ToFloat32());
237          constant.f.w = nihstro::to_float24(setup.uniforms.f[i].w.ToFloat32());
238  
239          // Store constant if it's different from zero..
240          if (setup.uniforms.f[i].x.ToFloat32() != 0.0 || setup.uniforms.f[i].y.ToFloat32() != 0.0 ||
241              setup.uniforms.f[i].z.ToFloat32() != 0.0 || setup.uniforms.f[i].w.ToFloat32() != 0.0)
242              constant_table.emplace_back(constant);
243      }
244      dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
245      dvle.constant_table_size = static_cast<uint32_t>(constant_table.size());
246      for (const auto& constant : constant_table) {
247          QueueForWriting(reinterpret_cast<const u8*>(&constant), sizeof(constant));
248      }
249  
250      // Write data to file
251      std::ofstream file(filename, std::ios_base::out | std::ios_base::binary);
252  
253      for (const auto& chunk : writing_queue) {
254          file.write(reinterpret_cast<const char*>(chunk.pointer), chunk.size);
255      }
256  }
257  
258  static std::unique_ptr<PicaTrace> pica_trace;
259  static std::mutex pica_trace_mutex;
260  bool g_is_pica_tracing = false;
261  
262  void StartPicaTracing() {
263      if (g_is_pica_tracing) {
264          LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!");
265          return;
266      }
267  
268      std::lock_guard lock(pica_trace_mutex);
269      pica_trace = std::make_unique<PicaTrace>();
270  
271      g_is_pica_tracing = true;
272  }
273  
274  void OnPicaRegWrite(u16 cmd_id, u16 mask, u32 value) {
275      std::lock_guard lock(pica_trace_mutex);
276  
277      if (!g_is_pica_tracing)
278          return;
279  
280      pica_trace->writes.push_back(PicaTrace::Write{cmd_id, mask, value});
281  }
282  
283  std::unique_ptr<PicaTrace> FinishPicaTracing() {
284      if (!g_is_pica_tracing) {
285          LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!");
286          return {};
287      }
288  
289      // signalize that no further tracing should be performed
290      g_is_pica_tracing = false;
291  
292      // Wait until running tracing is finished
293      std::lock_guard lock(pica_trace_mutex);
294      std::unique_ptr<PicaTrace> ret(std::move(pica_trace));
295  
296      return ret;
297  }
298  
299  } // namespace DebugUtils
300  
301  } // namespace Pica