| | |
| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <array> |
| | #include <iterator> |
| | #include <memory> |
| | #include <QBoxLayout> |
| | #include <QComboBox> |
| | #include <QFileDialog> |
| | #include <QMessageBox> |
| | #include <QPushButton> |
| | #include <nihstro/float24.h> |
| | #include "citra_qt/debugger/graphics/graphics_tracing.h" |
| | #include "common/common_types.h" |
| | #include "core/core.h" |
| | #include "core/tracer/recorder.h" |
| | #include "video_core/gpu.h" |
| | #include "video_core/pica/pica_core.h" |
| |
|
| | GraphicsTracingWidget::GraphicsTracingWidget(Core::System& system_, |
| | std::shared_ptr<Pica::DebugContext> debug_context, |
| | QWidget* parent) |
| | : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent), system{system_} { |
| |
|
| | setObjectName(QStringLiteral("CiTracing")); |
| |
|
| | QPushButton* start_recording = new QPushButton(tr("Start Recording")); |
| | QPushButton* stop_recording = |
| | new QPushButton(QIcon::fromTheme(QStringLiteral("document-save")), tr("Stop and Save")); |
| | QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); |
| |
|
| | connect(this, &GraphicsTracingWidget::SetStartTracingButtonEnabled, start_recording, |
| | &QPushButton::setVisible); |
| | connect(this, &GraphicsTracingWidget::SetStopTracingButtonEnabled, stop_recording, |
| | &QPushButton::setVisible); |
| | connect(this, &GraphicsTracingWidget::SetAbortTracingButtonEnabled, abort_recording, |
| | &QPushButton::setVisible); |
| | connect(start_recording, &QPushButton::clicked, this, &GraphicsTracingWidget::StartRecording); |
| | connect(stop_recording, &QPushButton::clicked, this, &GraphicsTracingWidget::StopRecording); |
| | connect(abort_recording, &QPushButton::clicked, this, &GraphicsTracingWidget::AbortRecording); |
| |
|
| | stop_recording->setVisible(false); |
| | abort_recording->setVisible(false); |
| |
|
| | auto main_widget = new QWidget; |
| | auto main_layout = new QVBoxLayout; |
| | { |
| | auto sub_layout = new QHBoxLayout; |
| | sub_layout->addWidget(start_recording); |
| | sub_layout->addWidget(stop_recording); |
| | sub_layout->addWidget(abort_recording); |
| | main_layout->addLayout(sub_layout); |
| | } |
| | main_widget->setLayout(main_layout); |
| | setWidget(main_widget); |
| | } |
| |
|
| | void GraphicsTracingWidget::StartRecording() { |
| | auto context = context_weak.lock(); |
| | if (!context) |
| | return; |
| |
|
| | auto& pica = system.GPU().PicaCore(); |
| | auto shader_binary = pica.vs_setup.program_code; |
| | auto swizzle_data = pica.vs_setup.swizzle_data; |
| |
|
| | |
| | |
| | std::array<u32, 4 * 16> default_attributes; |
| | for (u32 i = 0; i < 16; ++i) { |
| | for (u32 comp = 0; comp < 3; ++comp) { |
| | default_attributes[4 * i + comp] = |
| | nihstro::to_float24(pica.input_default_attributes[i][comp].ToFloat32()); |
| | } |
| | } |
| |
|
| | std::array<u32, 4 * 96> vs_float_uniforms; |
| | for (u32 i = 0; i < 96; ++i) { |
| | for (u32 comp = 0; comp < 3; ++comp) { |
| | vs_float_uniforms[4 * i + comp] = |
| | nihstro::to_float24(pica.vs_setup.uniforms.f[i][comp].ToFloat32()); |
| | } |
| | } |
| |
|
| | CiTrace::Recorder::InitialState state; |
| |
|
| | const auto copy = [&](std::vector<u32>& dest, auto& data) { |
| | dest.resize(sizeof(data)); |
| | std::memcpy(dest.data(), std::addressof(data), sizeof(data)); |
| | }; |
| |
|
| | copy(state.pica_registers, pica.regs); |
| | copy(state.lcd_registers, pica.regs_lcd); |
| | copy(state.default_attributes, default_attributes); |
| | copy(state.vs_program_binary, shader_binary); |
| | copy(state.vs_swizzle_data, swizzle_data); |
| | copy(state.vs_float_uniforms, vs_float_uniforms); |
| | |
| | |
| | |
| |
|
| | context->recorder = std::make_shared<CiTrace::Recorder>(state); |
| |
|
| | emit SetStartTracingButtonEnabled(false); |
| | emit SetStopTracingButtonEnabled(true); |
| | emit SetAbortTracingButtonEnabled(true); |
| | } |
| |
|
| | void GraphicsTracingWidget::StopRecording() { |
| | auto context = context_weak.lock(); |
| | if (!context) |
| | return; |
| |
|
| | QString filename = QFileDialog::getSaveFileName( |
| | this, tr("Save CiTrace"), QStringLiteral("citrace.ctf"), tr("CiTrace File (*.ctf)")); |
| |
|
| | if (filename.isEmpty()) { |
| | |
| | return; |
| | } |
| |
|
| | context->recorder->Finish(filename.toStdString()); |
| | context->recorder = nullptr; |
| |
|
| | emit SetStopTracingButtonEnabled(false); |
| | emit SetAbortTracingButtonEnabled(false); |
| | emit SetStartTracingButtonEnabled(true); |
| | } |
| |
|
| | void GraphicsTracingWidget::AbortRecording() { |
| | auto context = context_weak.lock(); |
| | if (!context) |
| | return; |
| |
|
| | context->recorder = nullptr; |
| |
|
| | emit SetStopTracingButtonEnabled(false); |
| | emit SetAbortTracingButtonEnabled(false); |
| | emit SetStartTracingButtonEnabled(true); |
| | } |
| |
|
| | void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, const void* data) { |
| | widget()->setEnabled(true); |
| | } |
| |
|
| | void GraphicsTracingWidget::OnResumed() { |
| | widget()->setEnabled(false); |
| | } |
| |
|
| | void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) { |
| | |
| | widget()->setEnabled(false); |
| | } |
| |
|
| | void GraphicsTracingWidget::OnEmulationStopping() { |
| | |
| |
|
| | auto context = context_weak.lock(); |
| | if (!context) |
| | return; |
| |
|
| | if (context->recorder) { |
| | auto reply = |
| | QMessageBox::question(this, tr("CiTracing still active"), |
| | tr("A CiTrace is still being recorded. Do you want to save it? " |
| | "If not, all recorded data will be discarded."), |
| | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |
| |
|
| | if (reply == QMessageBox::Yes) { |
| | StopRecording(); |
| | } else { |
| | AbortRecording(); |
| | } |
| | } |
| |
|
| | |
| | |
| | widget()->setEnabled(true); |
| | } |
| |
|