mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Add CiTrace recording support.
This is exposed in the GUI as a new "CiTrace Recording" widget. Playback is implemented by a standalone 3DS homebrew application (which only runs reliably within Citra currently; on an actual 3DS it will often crash still).
This commit is contained in:
		
							parent
							
								
									93d66475d4
								
							
						
					
					
						commit
						902fa4da52
					
				
					 15 changed files with 641 additions and 4 deletions
				
			
		|  | @ -12,6 +12,7 @@ set(SRCS | |||
|             debugger/graphics_breakpoints.cpp | ||||
|             debugger/graphics_cmdlists.cpp | ||||
|             debugger/graphics_framebuffer.cpp | ||||
|             debugger/graphics_tracing.cpp | ||||
|             debugger/graphics_vertex_shader.cpp | ||||
|             debugger/profiler.cpp | ||||
|             debugger/ramview.cpp | ||||
|  | @ -35,6 +36,7 @@ set(HEADERS | |||
|             debugger/graphics_breakpoints_p.h | ||||
|             debugger/graphics_cmdlists.h | ||||
|             debugger/graphics_framebuffer.h | ||||
|             debugger/graphics_tracing.h | ||||
|             debugger/graphics_vertex_shader.h | ||||
|             debugger/profiler.h | ||||
|             debugger/ramview.h | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
|  * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||||
|  * the widget usually wants to perform reactions in the GUI thread. | ||||
|  */ | ||||
| class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { | ||||
| class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|  |  | |||
							
								
								
									
										123
									
								
								src/citra_qt/debugger/graphics_tracing.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/citra_qt/debugger/graphics_tracing.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | |||
| // Copyright 2015 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QBoxLayout> | ||||
| #include <QComboBox> | ||||
| #include <QFileDialog> | ||||
| #include <QLabel> | ||||
| #include <QPushButton> | ||||
| #include <QSpinBox> | ||||
| 
 | ||||
| #include "core/hw/gpu.h" | ||||
| #include "video_core/pica.h" | ||||
| 
 | ||||
| #include "nihstro/float24.h" | ||||
| 
 | ||||
| #include "graphics_tracing.h" | ||||
| 
 | ||||
| GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                              QWidget* parent) | ||||
|     : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) { | ||||
| 
 | ||||
|     setObjectName("CiTracing"); | ||||
| 
 | ||||
|     QPushButton* start_recording = new QPushButton(tr("Start Recording")); | ||||
|     QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); | ||||
|     QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); | ||||
| 
 | ||||
|     connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); | ||||
|     connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); | ||||
|     connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); | ||||
|     connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); | ||||
|     connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); | ||||
|     connect(abort_recording, SIGNAL(clicked()), this, SLOT(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); | ||||
| 
 | ||||
|     // TODO: Make sure to have this widget disabled as soon as emulation is started!
 | ||||
| } | ||||
| 
 | ||||
| void GraphicsTracingWidget::StartRecording() { | ||||
|     auto context = context_weak.lock(); | ||||
|     if (!context) | ||||
|         return; | ||||
| 
 | ||||
|     auto shader_binary = Pica::g_state.vs.program_code; | ||||
|     auto swizzle_data = Pica::g_state.vs.swizzle_data; | ||||
| 
 | ||||
|     // Encode floating point numbers to 24-bit values
 | ||||
|     // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally.
 | ||||
|     std::array<Math::Vec4<uint32_t>, 96> vs_float_uniforms; | ||||
|     for (unsigned i = 0; i < 96; ++i) | ||||
|         for (unsigned comp = 0; comp < 3; ++comp) | ||||
|             vs_float_uniforms[i][comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); | ||||
| 
 | ||||
|     auto recorder = new CiTrace::Recorder((u32*)&GPU::g_regs, 0x700, nullptr, 0, (u32*)&Pica::g_state.regs, 0x300, | ||||
|                                           shader_binary.data(), shader_binary.size(), | ||||
|                                           swizzle_data.data(), swizzle_data.size(), | ||||
|                                           (u32*)vs_float_uniforms.data(), vs_float_uniforms.size() * 4, | ||||
|                                           nullptr, 0, nullptr, 0, nullptr, 0 // Geometry shader is not implemented yet, so submit dummy data for now
 | ||||
|                                           ); | ||||
|     context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); | ||||
| 
 | ||||
|     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"), "citrace.ctf", | ||||
|                                                     tr("CiTrace File (*.ctf)")); | ||||
| 
 | ||||
|     if (filename.isEmpty()) { | ||||
|         // If the user canceled the dialog, keep recording
 | ||||
|         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, void* data) { | ||||
|     widget()->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsTracingWidget::OnResumed() { | ||||
|     widget()->setEnabled(false); | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/citra_qt/debugger/graphics_tracing.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/citra_qt/debugger/graphics_tracing.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| // Copyright 2015 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "graphics_breakpoint_observer.h" | ||||
| 
 | ||||
| class GraphicsTracingWidget : public BreakPointObserverDock { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); | ||||
| 
 | ||||
| private slots: | ||||
|     void StartRecording(); | ||||
|     void StopRecording(); | ||||
|     void AbortRecording(); | ||||
| 
 | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnResumed() override; | ||||
| 
 | ||||
| signals: | ||||
|     void SetStartTracingButtonEnabled(bool enable); | ||||
|     void SetStopTracingButtonEnabled(bool enable); | ||||
|     void SetAbortTracingButtonEnabled(bool enable); | ||||
| }; | ||||
|  | @ -32,6 +32,7 @@ | |||
| #include "debugger/graphics_breakpoints.h" | ||||
| #include "debugger/graphics_cmdlists.h" | ||||
| #include "debugger/graphics_framebuffer.h" | ||||
| #include "debugger/graphics_tracing.h" | ||||
| #include "debugger/graphics_vertex_shader.h" | ||||
| #include "debugger/profiler.h" | ||||
| 
 | ||||
|  | @ -94,6 +95,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); | ||||
|     graphicsVertexShaderWidget->hide(); | ||||
| 
 | ||||
|     auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); | ||||
|     graphicsTracingWidget->hide(); | ||||
| 
 | ||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||
|     debug_menu->addAction(profilerWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|  | @ -104,6 +109,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); | ||||
| 
 | ||||
|     // Set default UI state
 | ||||
|     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue