graphics_tracing.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2015 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <iterator>
  7. #include <memory>
  8. #include <QBoxLayout>
  9. #include <QComboBox>
  10. #include <QFileDialog>
  11. #include <QMessageBox>
  12. #include <QPushButton>
  13. #include <boost/range/algorithm/copy.hpp>
  14. #include "citra_qt/debugger/graphics/graphics_tracing.h"
  15. #include "common/common_types.h"
  16. #include "core/hw/gpu.h"
  17. #include "core/hw/lcd.h"
  18. #include "core/tracer/recorder.h"
  19. #include "nihstro/float24.h"
  20. #include "video_core/pica_state.h"
  21. GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
  22. QWidget* parent)
  23. : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) {
  24. setObjectName("CiTracing");
  25. QPushButton* start_recording = new QPushButton(tr("Start Recording"));
  26. QPushButton* stop_recording =
  27. new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
  28. QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
  29. connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording,
  30. SLOT(setVisible(bool)));
  31. connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording,
  32. SLOT(setVisible(bool)));
  33. connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording,
  34. SLOT(setVisible(bool)));
  35. connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
  36. connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
  37. connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
  38. stop_recording->setVisible(false);
  39. abort_recording->setVisible(false);
  40. auto main_widget = new QWidget;
  41. auto main_layout = new QVBoxLayout;
  42. {
  43. auto sub_layout = new QHBoxLayout;
  44. sub_layout->addWidget(start_recording);
  45. sub_layout->addWidget(stop_recording);
  46. sub_layout->addWidget(abort_recording);
  47. main_layout->addLayout(sub_layout);
  48. }
  49. main_widget->setLayout(main_layout);
  50. setWidget(main_widget);
  51. }
  52. void GraphicsTracingWidget::StartRecording() {
  53. auto context = context_weak.lock();
  54. if (!context)
  55. return;
  56. auto shader_binary = Pica::g_state.vs.program_code;
  57. auto swizzle_data = Pica::g_state.vs.swizzle_data;
  58. // Encode floating point numbers to 24-bit values
  59. // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally.
  60. std::array<u32, 4 * 16> default_attributes;
  61. for (unsigned i = 0; i < 16; ++i) {
  62. for (unsigned comp = 0; comp < 3; ++comp) {
  63. default_attributes[4 * i + comp] = nihstro::to_float24(
  64. Pica::g_state.input_default_attributes.attr[i][comp].ToFloat32());
  65. }
  66. }
  67. std::array<u32, 4 * 96> vs_float_uniforms;
  68. for (unsigned i = 0; i < 96; ++i)
  69. for (unsigned comp = 0; comp < 3; ++comp)
  70. vs_float_uniforms[4 * i + comp] =
  71. nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
  72. CiTrace::Recorder::InitialState state;
  73. std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
  74. std::back_inserter(state.gpu_registers));
  75. std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32),
  76. std::back_inserter(state.lcd_registers));
  77. std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32),
  78. std::back_inserter(state.pica_registers));
  79. boost::copy(default_attributes, std::back_inserter(state.default_attributes));
  80. boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
  81. boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
  82. boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
  83. // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
  84. // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
  85. // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
  86. auto recorder = new CiTrace::Recorder(state);
  87. context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
  88. emit SetStartTracingButtonEnabled(false);
  89. emit SetStopTracingButtonEnabled(true);
  90. emit SetAbortTracingButtonEnabled(true);
  91. }
  92. void GraphicsTracingWidget::StopRecording() {
  93. auto context = context_weak.lock();
  94. if (!context)
  95. return;
  96. QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf",
  97. tr("CiTrace File (*.ctf)"));
  98. if (filename.isEmpty()) {
  99. // If the user canceled the dialog, keep recording
  100. return;
  101. }
  102. context->recorder->Finish(filename.toStdString());
  103. context->recorder = nullptr;
  104. emit SetStopTracingButtonEnabled(false);
  105. emit SetAbortTracingButtonEnabled(false);
  106. emit SetStartTracingButtonEnabled(true);
  107. }
  108. void GraphicsTracingWidget::AbortRecording() {
  109. auto context = context_weak.lock();
  110. if (!context)
  111. return;
  112. context->recorder = nullptr;
  113. emit SetStopTracingButtonEnabled(false);
  114. emit SetAbortTracingButtonEnabled(false);
  115. emit SetStartTracingButtonEnabled(true);
  116. }
  117. void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
  118. widget()->setEnabled(true);
  119. }
  120. void GraphicsTracingWidget::OnResumed() {
  121. widget()->setEnabled(false);
  122. }
  123. void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) {
  124. // Disable tracing starting/stopping until a GPU breakpoint is reached
  125. widget()->setEnabled(false);
  126. }
  127. void GraphicsTracingWidget::OnEmulationStopping() {
  128. // TODO: Is it safe to access the context here?
  129. auto context = context_weak.lock();
  130. if (!context)
  131. return;
  132. if (context->recorder) {
  133. auto reply =
  134. QMessageBox::question(this, tr("CiTracing still active"),
  135. tr("A CiTrace is still being recorded. Do you want to save it? "
  136. "If not, all recorded data will be discarded."),
  137. QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  138. if (reply == QMessageBox::Yes) {
  139. StopRecording();
  140. } else {
  141. AbortRecording();
  142. }
  143. }
  144. // If the widget was disabled before, enable it now to allow starting
  145. // tracing before starting the next emulation session
  146. widget()->setEnabled(true);
  147. }