summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/hw/hw.cpp7
-rw-r--r--src/core/hw/lcd.cpp9
-rw-r--r--src/core/memory.cpp41
-rw-r--r--src/core/telemetry_session.cpp5
-rw-r--r--src/video_core/video_core.cpp3
-rw-r--r--src/video_core/video_core.h2
-rw-r--r--src/yuzu/CMakeLists.txt21
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/citra-qt.rc4
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_audio.cpp77
-rw-r--r--src/yuzu/configuration/configure_audio.h30
-rw-r--r--src/yuzu/configuration/configure_audio.ui73
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_general.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_input.cpp5
-rw-r--r--src/yuzu/configuration/configure_system.cpp8
-rw-r--r--src/yuzu/configuration/configure_web.cpp102
-rw-r--r--src/yuzu/configuration/configure_web.h40
-rw-r--r--src/yuzu/configuration/configure_web.ui190
-rw-r--r--src/yuzu/debugger/graphics/graphics.cpp77
-rw-r--r--src/yuzu/debugger/graphics/graphics.h41
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp27
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoint_observer.h33
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp213
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.h46
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h36
-rw-r--r--src/yuzu/debugger/graphics/graphics_cmdlists.cpp259
-rw-r--r--src/yuzu/debugger/graphics/graphics_cmdlists.h61
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp713
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h118
-rw-r--r--src/yuzu/debugger/graphics/graphics_tracing.cpp177
-rw-r--r--src/yuzu/debugger/graphics/graphics_tracing.h33
-rw-r--r--src/yuzu/debugger/graphics/graphics_vertex_shader.cpp622
-rw-r--r--src/yuzu/debugger/graphics/graphics_vertex_shader.h88
-rw-r--r--src/yuzu/debugger/profiler.cpp4
-rw-r--r--src/yuzu/debugger/registers.cpp5
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/game_list_p.h2
-rw-r--r--src/yuzu/hotkeys.cpp4
-rw-r--r--src/yuzu/main.cpp72
-rw-r--r--src/yuzu/util/spinbox.cpp2
-rw-r--r--src/yuzu/util/util.cpp2
46 files changed, 47 insertions, 3245 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e5f299f26..ae1eb2430 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -4,7 +4,6 @@
4 4
5#include <memory> 5#include <memory>
6#include <utility> 6#include <utility>
7#include "audio_core/audio_core.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9#include "core/arm/dynarmic/arm_dynarmic.h" 8#include "core/arm/dynarmic/arm_dynarmic.h"
10#include "core/arm/unicorn/arm_unicorn.h" 9#include "core/arm/unicorn/arm_unicorn.h"
@@ -19,7 +18,6 @@
19#include "core/loader/loader.h" 18#include "core/loader/loader.h"
20#include "core/memory_setup.h" 19#include "core/memory_setup.h"
21#include "core/settings.h" 20#include "core/settings.h"
22#include "network/network.h"
23#include "video_core/video_core.h" 21#include "video_core/video_core.h"
24 22
25namespace Core { 23namespace Core {
@@ -156,7 +154,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
156 HW::Init(); 154 HW::Init();
157 Kernel::Init(system_mode); 155 Kernel::Init(system_mode);
158 Service::Init(); 156 Service::Init();
159 AudioCore::Init();
160 GDBStub::Init(); 157 GDBStub::Init();
161 158
162 if (!VideoCore::Init(emu_window)) { 159 if (!VideoCore::Init(emu_window)) {
@@ -184,7 +181,6 @@ void System::Shutdown() {
184 181
185 // Shutdown emulation session 182 // Shutdown emulation session
186 GDBStub::Shutdown(); 183 GDBStub::Shutdown();
187 AudioCore::Shutdown();
188 VideoCore::Shutdown(); 184 VideoCore::Shutdown();
189 Service::Shutdown(); 185 Service::Shutdown();
190 Kernel::Shutdown(); 186 Kernel::Shutdown();
@@ -193,10 +189,6 @@ void System::Shutdown() {
193 cpu_core = nullptr; 189 cpu_core = nullptr;
194 app_loader = nullptr; 190 app_loader = nullptr;
195 telemetry_session = nullptr; 191 telemetry_session = nullptr;
196 if (auto room_member = Network::GetRoomMember().lock()) {
197 Network::GameInfo game_info{};
198 room_member->SendGameInfo(game_info);
199 }
200 192
201 LOG_DEBUG(Core, "Shutdown OK"); 193 LOG_DEBUG(Core, "Shutdown OK");
202} 194}
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 8499f2ce6..a751b1d62 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -4,8 +4,6 @@
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hw/aes/key.h"
8#include "core/hw/gpu.h"
9#include "core/hw/hw.h" 7#include "core/hw/hw.h"
10#include "core/hw/lcd.h" 8#include "core/hw/lcd.h"
11 9
@@ -30,7 +28,6 @@ inline void Read(T& var, const u32 addr) {
30 case VADDR_GPU + 0xD000: 28 case VADDR_GPU + 0xD000:
31 case VADDR_GPU + 0xE000: 29 case VADDR_GPU + 0xE000:
32 case VADDR_GPU + 0xF000: 30 case VADDR_GPU + 0xF000:
33 GPU::Read(var, addr);
34 break; 31 break;
35 case VADDR_LCD: 32 case VADDR_LCD:
36 LCD::Read(var, addr); 33 LCD::Read(var, addr);
@@ -59,7 +56,6 @@ inline void Write(u32 addr, const T data) {
59 case VADDR_GPU + 0xD000: 56 case VADDR_GPU + 0xD000:
60 case VADDR_GPU + 0xE000: 57 case VADDR_GPU + 0xE000:
61 case VADDR_GPU + 0xF000: 58 case VADDR_GPU + 0xF000:
62 GPU::Write(addr, data);
63 break; 59 break;
64 case VADDR_LCD: 60 case VADDR_LCD:
65 LCD::Write(addr, data); 61 LCD::Write(addr, data);
@@ -86,15 +82,12 @@ void Update() {}
86 82
87/// Initialize hardware 83/// Initialize hardware
88void Init() { 84void Init() {
89 AES::InitKeys();
90 GPU::Init();
91 LCD::Init(); 85 LCD::Init();
92 LOG_DEBUG(HW, "initialized OK"); 86 LOG_DEBUG(HW, "initialized OK");
93} 87}
94 88
95/// Shutdown hardware 89/// Shutdown hardware
96void Shutdown() { 90void Shutdown() {
97 GPU::Shutdown();
98 LCD::Shutdown(); 91 LCD::Shutdown();
99 LOG_DEBUG(HW, "shutdown OK"); 92 LOG_DEBUG(HW, "shutdown OK");
100} 93}
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
index 2aa89de18..763ac1c4d 100644
--- a/src/core/hw/lcd.cpp
+++ b/src/core/hw/lcd.cpp
@@ -8,7 +8,6 @@
8#include "core/hw/hw.h" 8#include "core/hw/hw.h"
9#include "core/hw/lcd.h" 9#include "core/hw/lcd.h"
10#include "core/tracer/recorder.h" 10#include "core/tracer/recorder.h"
11#include "video_core/debug_utils/debug_utils.h"
12 11
13namespace LCD { 12namespace LCD {
14 13
@@ -40,14 +39,6 @@ inline void Write(u32 addr, const T data) {
40 } 39 }
41 40
42 g_regs[index] = static_cast<u32>(data); 41 g_regs[index] = static_cast<u32>(data);
43
44 // Notify tracer about the register write
45 // This is happening *after* handling the write to make sure we properly catch all memory reads.
46 if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
47 // addr + GPU VBase - IO VBase + IO PBase
48 Pica::g_debug_context->recorder->RegisterWritten<T>(
49 addr + HW::VADDR_LCD - 0x1EC00000 + 0x10100000, data);
50 }
51} 42}
52 43
53// Explicitly instantiate template functions because we aren't defining this in the header: 44// Explicitly instantiate template functions because we aren't defining this in the header:
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 01ec231f6..74a598852 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,7 +4,6 @@
4 4
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include "audio_core/audio_core.h"
8#include "common/assert.h" 7#include "common/assert.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/logging/log.h" 9#include "common/logging/log.h"
@@ -311,7 +310,6 @@ u8* GetPhysicalPointer(PAddr address) {
311 target_pointer = vram.data() + offset_into_region; 310 target_pointer = vram.data() + offset_into_region;
312 break; 311 break;
313 case DSP_RAM_PADDR: 312 case DSP_RAM_PADDR:
314 target_pointer = AudioCore::GetDspMemory().data() + offset_into_region;
315 break; 313 break;
316 case FCRAM_PADDR: 314 case FCRAM_PADDR:
317 for (const auto& region : Kernel::memory_regions) { 315 for (const auto& region : Kernel::memory_regions) {
@@ -413,53 +411,16 @@ void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) {
413 } 411 }
414} 412}
415 413
416void RasterizerFlushRegion(PAddr start, u64 size) { 414void RasterizerFlushRegion(PAddr start, u64 size) {}
417 if (VideoCore::g_renderer != nullptr) {
418 VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size);
419 }
420}
421 415
422void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size) { 416void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size) {
423 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 417 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
424 // null here 418 // null here
425 if (VideoCore::g_renderer != nullptr) {
426 VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size);
427 }
428} 419}
429 420
430void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { 421void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
431 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 422 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
432 // null here 423 // null here
433 if (VideoCore::g_renderer != nullptr) {
434 VAddr end = start + size;
435
436 auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
437 if (start >= region_end || end <= region_start) {
438 // No overlap with region
439 return;
440 }
441
442 VAddr overlap_start = std::max(start, region_start);
443 VAddr overlap_end = std::min(end, region_end);
444
445 PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value();
446 u32 overlap_size = static_cast<u32>(overlap_end - overlap_start);
447
448 auto* rasterizer = VideoCore::g_renderer->Rasterizer();
449 switch (mode) {
450 case FlushMode::Flush:
451 rasterizer->FlushRegion(physical_start, overlap_size);
452 break;
453 case FlushMode::FlushAndInvalidate:
454 rasterizer->FlushAndInvalidateRegion(physical_start, overlap_size);
455 break;
456 }
457 };
458
459 CheckRegion(LINEAR_HEAP_VADDR, LINEAR_HEAP_VADDR_END);
460 CheckRegion(NEW_LINEAR_HEAP_VADDR, NEW_LINEAR_HEAP_VADDR_END);
461 CheckRegion(VRAM_VADDR, VRAM_VADDR_END);
462 }
463} 424}
464 425
465u8 Read8(const VAddr addr) { 426u8 Read8(const VAddr addr) {
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 560c8af55..4e402bc58 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -12,11 +12,6 @@
12#include "core/settings.h" 12#include "core/settings.h"
13#include "core/telemetry_session.h" 13#include "core/telemetry_session.h"
14 14
15#ifdef ENABLE_WEB_SERVICE
16#include "web_service/telemetry_json.h"
17#include "web_service/verify_login.h"
18#endif
19
20namespace Core { 15namespace Core {
21 16
22static const char* CpuVendorToStr(Common::CPUVendor vendor) { 17static const char* CpuVendorToStr(Common::CPUVendor vendor) {
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 110f38a3e..106d62562 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -16,9 +16,6 @@ namespace VideoCore {
16EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window 16EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
17std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin 17std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
18 18
19std::atomic<bool> g_hw_renderer_enabled;
20std::atomic<bool> g_shader_jit_enabled;
21std::atomic<bool> g_vsync_enabled;
22std::atomic<bool> g_toggle_framelimit_enabled; 19std::atomic<bool> g_toggle_framelimit_enabled;
23 20
24/// Initialize the video core 21/// Initialize the video core
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 94e0867f0..0b8785898 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -20,8 +20,6 @@ extern EmuWindow* g_emu_window; ///< Emu window
20 20
21// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from 21// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
22// qt ui) 22// qt ui)
23extern std::atomic<bool> g_hw_renderer_enabled;
24extern std::atomic<bool> g_shader_jit_enabled;
25extern std::atomic<bool> g_toggle_framelimit_enabled; 23extern std::atomic<bool> g_toggle_framelimit_enabled;
26 24
27/// Start the video core 25/// Start the video core
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 38bbc0043..02953f1e6 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -5,21 +5,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
5 5
6set(SRCS 6set(SRCS
7 configuration/config.cpp 7 configuration/config.cpp
8 configuration/configure_audio.cpp
9 configuration/configure_debug.cpp 8 configuration/configure_debug.cpp
10 configuration/configure_dialog.cpp 9 configuration/configure_dialog.cpp
11 configuration/configure_general.cpp 10 configuration/configure_general.cpp
12 configuration/configure_graphics.cpp 11 configuration/configure_graphics.cpp
13 configuration/configure_input.cpp 12 configuration/configure_input.cpp
14 configuration/configure_system.cpp 13 configuration/configure_system.cpp
15 configuration/configure_web.cpp
16 debugger/graphics/graphics.cpp
17 debugger/graphics/graphics_breakpoint_observer.cpp
18 debugger/graphics/graphics_breakpoints.cpp
19 debugger/graphics/graphics_cmdlists.cpp
20 debugger/graphics/graphics_surface.cpp
21 debugger/graphics/graphics_tracing.cpp
22 debugger/graphics/graphics_vertex_shader.cpp
23 debugger/profiler.cpp 14 debugger/profiler.cpp
24 debugger/registers.cpp 15 debugger/registers.cpp
25 debugger/wait_tree.cpp 16 debugger/wait_tree.cpp
@@ -36,22 +27,12 @@ set(SRCS
36 27
37set(HEADERS 28set(HEADERS
38 configuration/config.h 29 configuration/config.h
39 configuration/configure_audio.h
40 configuration/configure_debug.h 30 configuration/configure_debug.h
41 configuration/configure_dialog.h 31 configuration/configure_dialog.h
42 configuration/configure_general.h 32 configuration/configure_general.h
43 configuration/configure_graphics.h 33 configuration/configure_graphics.h
44 configuration/configure_input.h 34 configuration/configure_input.h
45 configuration/configure_system.h 35 configuration/configure_system.h
46 configuration/configure_web.h
47 debugger/graphics/graphics.h
48 debugger/graphics/graphics_breakpoint_observer.h
49 debugger/graphics/graphics_breakpoints.h
50 debugger/graphics/graphics_breakpoints_p.h
51 debugger/graphics/graphics_cmdlists.h
52 debugger/graphics/graphics_surface.h
53 debugger/graphics/graphics_tracing.h
54 debugger/graphics/graphics_vertex_shader.h
55 debugger/profiler.h 36 debugger/profiler.h
56 debugger/registers.h 37 debugger/registers.h
57 debugger/wait_tree.h 38 debugger/wait_tree.h
@@ -67,13 +48,11 @@ set(HEADERS
67 48
68set(UIS 49set(UIS
69 configuration/configure.ui 50 configuration/configure.ui
70 configuration/configure_audio.ui
71 configuration/configure_debug.ui 51 configuration/configure_debug.ui
72 configuration/configure_general.ui 52 configuration/configure_general.ui
73 configuration/configure_graphics.ui 53 configuration/configure_graphics.ui
74 configuration/configure_input.ui 54 configuration/configure_input.ui
75 configuration/configure_system.ui 55 configuration/configure_system.ui
76 configuration/configure_web.ui
77 debugger/registers.ui 56 debugger/registers.ui
78 hotkeys.ui 57 hotkeys.ui
79 main.ui 58 main.ui
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index eb542ad4e..6c17af708 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,7 +8,6 @@
8#include <QWindow> 8#include <QWindow>
9#endif 9#endif
10 10
11#include "citra_qt/bootmanager.h"
12#include "common/microprofile.h" 11#include "common/microprofile.h"
13#include "common/scm_rev.h" 12#include "common/scm_rev.h"
14#include "common/string_util.h" 13#include "common/string_util.h"
@@ -18,7 +17,8 @@
18#include "input_common/keyboard.h" 17#include "input_common/keyboard.h"
19#include "input_common/main.h" 18#include "input_common/main.h"
20#include "input_common/motion_emu.h" 19#include "input_common/motion_emu.h"
21#include "network/network.h" 20#include "yuzu/bootmanager.h"
21
22 22
23EmuThread::EmuThread(GRenderWindow* render_window) 23EmuThread::EmuThread(GRenderWindow* render_window)
24 : exec_step(false), running(false), stop_run(false), render_window(render_window) {} 24 : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
@@ -112,12 +112,10 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
112 setWindowTitle(QString::fromStdString(window_title)); 112 setWindowTitle(QString::fromStdString(window_title));
113 113
114 InputCommon::Init(); 114 InputCommon::Init();
115 Network::Init();
116} 115}
117 116
118GRenderWindow::~GRenderWindow() { 117GRenderWindow::~GRenderWindow() {
119 InputCommon::Shutdown(); 118 InputCommon::Shutdown();
120 Network::Shutdown();
121} 119}
122 120
123void GRenderWindow::moveContext() { 121void GRenderWindow::moveContext() {
diff --git a/src/yuzu/citra-qt.rc b/src/yuzu/citra-qt.rc
index a48a9440d..1b253653f 100644
--- a/src/yuzu/citra-qt.rc
+++ b/src/yuzu/citra-qt.rc
@@ -8,7 +8,7 @@
8// remains consistent on all systems. 8// remains consistent on all systems.
9// QT requires that the default application icon is named IDI_ICON1 9// QT requires that the default application icon is named IDI_ICON1
10 10
11IDI_ICON1 ICON "../../dist/citra.ico" 11IDI_ICON1 ICON "../../dist/yuzu.ico"
12 12
13 13
14///////////////////////////////////////////////////////////////////////////// 14/////////////////////////////////////////////////////////////////////////////
@@ -16,4 +16,4 @@ IDI_ICON1 ICON "../../dist/citra.ico"
16// RT_MANIFEST 16// RT_MANIFEST
17// 17//
18 18
191 RT_MANIFEST "../../dist/citra.manifest" 191 RT_MANIFEST "../../dist/yuzu.manifest"
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index fd884db7a..362f4ffe2 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -3,10 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QSettings> 5#include <QSettings>
6#include "citra_qt/configuration/config.h"
7#include "citra_qt/ui_settings.h"
8#include "common/file_util.h" 6#include "common/file_util.h"
9#include "input_common/main.h" 7#include "input_common/main.h"
8#include "yuzu/configuration/config.h"
9#include "yuzu/ui_settings.h"
10
10 11
11Config::Config() { 12Config::Config() {
12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 13 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
deleted file mode 100644
index 3fd1d127a..000000000
--- a/src/yuzu/configuration/configure_audio.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "audio_core/audio_core.h"
7#include "audio_core/sink.h"
8#include "audio_core/sink_details.h"
9#include "citra_qt/configuration/configure_audio.h"
10#include "core/settings.h"
11#include "ui_configure_audio.h"
12
13ConfigureAudio::ConfigureAudio(QWidget* parent)
14 : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
15 ui->setupUi(this);
16
17 ui->output_sink_combo_box->clear();
18 ui->output_sink_combo_box->addItem("auto");
19 for (const auto& sink_detail : AudioCore::g_sink_details) {
20 ui->output_sink_combo_box->addItem(sink_detail.id);
21 }
22
23 this->setConfiguration();
24 connect(ui->output_sink_combo_box, SIGNAL(currentIndexChanged(int)), this,
25 SLOT(updateAudioDevices(int)));
26}
27
28ConfigureAudio::~ConfigureAudio() {}
29
30void ConfigureAudio::setConfiguration() {
31 int new_sink_index = 0;
32 for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
33 if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) {
34 new_sink_index = index;
35 break;
36 }
37 }
38 ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
39
40 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
41
42 // The device list cannot be pre-populated (nor listed) until the output sink is known.
43 updateAudioDevices(new_sink_index);
44
45 int new_device_index = -1;
46 for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
47 if (ui->audio_device_combo_box->itemText(index).toStdString() ==
48 Settings::values.audio_device_id) {
49 new_device_index = index;
50 break;
51 }
52 }
53 ui->audio_device_combo_box->setCurrentIndex(new_device_index);
54}
55
56void ConfigureAudio::applyConfiguration() {
57 Settings::values.sink_id =
58 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
59 .toStdString();
60 Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
61 Settings::values.audio_device_id =
62 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
63 .toStdString();
64 Settings::Apply();
65}
66
67void ConfigureAudio::updateAudioDevices(int sink_index) {
68 ui->audio_device_combo_box->clear();
69 ui->audio_device_combo_box->addItem("auto");
70
71 std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
72 std::vector<std::string> device_list =
73 AudioCore::GetSinkDetails(sink_id).factory()->GetDeviceList();
74 for (const auto& device : device_list) {
75 ui->audio_device_combo_box->addItem(device.c_str());
76 }
77}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
deleted file mode 100644
index 8190e694f..000000000
--- a/src/yuzu/configuration/configure_audio.h
+++ /dev/null
@@ -1,30 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureAudio;
12}
13
14class ConfigureAudio : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureAudio(QWidget* parent = nullptr);
19 ~ConfigureAudio();
20
21 void applyConfiguration();
22
23public slots:
24 void updateAudioDevices(int sink_index);
25
26private:
27 void setConfiguration();
28
29 std::unique_ptr<Ui::ConfigureAudio> ui;
30};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
deleted file mode 100644
index dd870eb61..000000000
--- a/src/yuzu/configuration/configure_audio.ui
+++ /dev/null
@@ -1,73 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2
3<ui version="4.0">
4 <class>ConfigureAudio</class>
5 <widget class="QWidget" name="ConfigureAudio">
6 <layout class="QVBoxLayout">
7 <item>
8 <widget class="QGroupBox">
9 <property name="title">
10 <string>Audio</string>
11 </property>
12 <layout class="QVBoxLayout">
13 <item>
14 <layout class="QHBoxLayout">
15 <item>
16 <widget class="QLabel">
17 <property name="text">
18 <string>Output Engine:</string>
19 </property>
20 </widget>
21 </item>
22 <item>
23 <widget class="QComboBox" name="output_sink_combo_box">
24 </widget>
25 </item>
26 </layout>
27 </item>
28 <item>
29 <widget class="QCheckBox" name="toggle_audio_stretching">
30 <property name="text">
31 <string>Enable audio stretching</string>
32 </property>
33 <property name="toolTip">
34 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
35 </property>
36 </widget>
37 </item>
38 <item>
39 <layout class="QHBoxLayout">
40 <item>
41 <widget class="QLabel">
42 <property name="text">
43 <string>Audio Device:</string>
44 </property>
45 </widget>
46 </item>
47 <item>
48 <widget class="QComboBox" name="audio_device_combo_box">
49 </widget>
50 </item>
51 </layout>
52 </item>
53 </layout>
54 </widget>
55 </item>
56 <item>
57 <spacer>
58 <property name="orientation">
59 <enum>Qt::Vertical</enum>
60 </property>
61 <property name="sizeHint" stdset="0">
62 <size>
63 <width>20</width>
64 <height>40</height>
65 </size>
66 </property>
67 </spacer>
68 </item>
69 </layout>
70 </widget>
71 <resources />
72 <connections />
73</ui>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 263f73f38..9eb71c593 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -2,9 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configuration/configure_debug.h"
6#include "core/settings.h" 5#include "core/settings.h"
7#include "ui_configure_debug.h" 6#include "ui_configure_debug.h"
7#include "yuzu/configuration/configure_debug.h"
8
8 9
9ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) { 10ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
10 ui->setupUi(this); 11 ui->setupUi(this);
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index b87dc0e6c..23e5687d0 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -2,10 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configuration/config.h"
6#include "citra_qt/configuration/configure_dialog.h"
7#include "core/settings.h" 5#include "core/settings.h"
8#include "ui_configure.h" 6#include "ui_configure.h"
7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_dialog.h"
9
9 10
10ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { 11ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
11 ui->setupUi(this); 12 ui->setupUi(this);
@@ -21,8 +22,6 @@ void ConfigureDialog::applyConfiguration() {
21 ui->systemTab->applyConfiguration(); 22 ui->systemTab->applyConfiguration();
22 ui->inputTab->applyConfiguration(); 23 ui->inputTab->applyConfiguration();
23 ui->graphicsTab->applyConfiguration(); 24 ui->graphicsTab->applyConfiguration();
24 ui->audioTab->applyConfiguration();
25 ui->debugTab->applyConfiguration(); 25 ui->debugTab->applyConfiguration();
26 ui->webTab->applyConfiguration();
27 Settings::Apply(); 26 Settings::Apply();
28} 27}
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 0de27aa8b..4a1002c17 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -2,11 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configuration/configure_general.h"
6#include "citra_qt/ui_settings.h"
7#include "core/core.h" 5#include "core/core.h"
8#include "core/settings.h" 6#include "core/settings.h"
9#include "ui_configure_general.h" 7#include "ui_configure_general.h"
8#include "yuzu/configuration/configure_general.h"
9#include "yuzu/ui_settings.h"
10 10
11ConfigureGeneral::ConfigureGeneral(QWidget* parent) 11ConfigureGeneral::ConfigureGeneral(QWidget* parent)
12 : QWidget(parent), ui(new Ui::ConfigureGeneral) { 12 : QWidget(parent), ui(new Ui::ConfigureGeneral) {
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index b5a5ab1e1..379e13301 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -2,10 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configuration/configure_graphics.h"
6#include "core/core.h" 5#include "core/core.h"
7#include "core/settings.h" 6#include "core/settings.h"
8#include "ui_configure_graphics.h" 7#include "ui_configure_graphics.h"
8#include "yuzu/configuration/configure_graphics.h"
9
9 10
10ConfigureGraphics::ConfigureGraphics(QWidget* parent) 11ConfigureGraphics::ConfigureGraphics(QWidget* parent)
11 : QWidget(parent), ui(new Ui::ConfigureGraphics) { 12 : QWidget(parent), ui(new Ui::ConfigureGraphics) {
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 116a6330f..4c2a3e738 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -6,10 +6,11 @@
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QTimer> 8#include <QTimer>
9#include "citra_qt/configuration/config.h"
10#include "citra_qt/configuration/configure_input.h"
11#include "common/param_package.h" 9#include "common/param_package.h"
12#include "input_common/main.h" 10#include "input_common/main.h"
11#include "yuzu/configuration/config.h"
12#include "yuzu/configuration/configure_input.h"
13
13 14
14const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 15const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
15 ConfigureInput::analog_sub_buttons{{ 16 ConfigureInput::analog_sub_buttons{{
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index d83c2db23..00966266b 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -3,10 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QMessageBox> 5#include <QMessageBox>
6#include "citra_qt/configuration/configure_system.h"
7#include "citra_qt/ui_settings.h"
8#include "core/core.h" 6#include "core/core.h"
9#include "ui_configure_system.h" 7#include "ui_configure_system.h"
8#include "yuzu/configuration/configure_system.h"
9#include "yuzu/ui_settings.h"
10
10 11
11static const std::array<int, 12> days_in_month = {{ 12static const std::array<int, 12> days_in_month = {{
12 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 13 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
@@ -29,8 +30,7 @@ void ConfigureSystem::setConfiguration() {
29 enabled = !Core::System::GetInstance().IsPoweredOn(); 30 enabled = !Core::System::GetInstance().IsPoweredOn();
30} 31}
31 32
32void ConfigureSystem::ReadSystemSettings() { 33void ConfigureSystem::ReadSystemSettings() {}
33}
34 34
35void ConfigureSystem::applyConfiguration() { 35void ConfigureSystem::applyConfiguration() {
36 if (!enabled) 36 if (!enabled)
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
deleted file mode 100644
index bf8c21ac7..000000000
--- a/src/yuzu/configuration/configure_web.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QMessageBox>
6#include "citra_qt/configuration/configure_web.h"
7#include "core/settings.h"
8#include "core/telemetry_session.h"
9#include "ui_configure_web.h"
10
11ConfigureWeb::ConfigureWeb(QWidget* parent)
12 : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
13 ui->setupUi(this);
14 connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
15 &ConfigureWeb::RefreshTelemetryID);
16 connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
17 connect(this, &ConfigureWeb::LoginVerified, this, &ConfigureWeb::OnLoginVerified);
18
19 this->setConfiguration();
20}
21
22ConfigureWeb::~ConfigureWeb() {}
23
24void ConfigureWeb::setConfiguration() {
25 ui->web_credentials_disclaimer->setWordWrap(true);
26 ui->telemetry_learn_more->setOpenExternalLinks(true);
27 ui->telemetry_learn_more->setText(tr("<a "
28 "href='https://citra-emu.org/entry/"
29 "telemetry-and-why-thats-a-good-thing/'>Learn more</a>"));
30
31 ui->web_signup_link->setOpenExternalLinks(true);
32 ui->web_signup_link->setText(tr("<a href='https://services.citra-emu.org/'>Sign up</a>"));
33 ui->web_token_info_link->setOpenExternalLinks(true);
34 ui->web_token_info_link->setText(
35 tr("<a href='https://citra-emu.org/wiki/citra-web-service/'>What is my token?</a>"));
36
37 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
38 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
39 ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token));
40 // Connect after setting the values, to avoid calling OnLoginChanged now
41 connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
42 connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
43 ui->label_telemetry_id->setText(
44 tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
45 user_verified = true;
46}
47
48void ConfigureWeb::applyConfiguration() {
49 Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
50 if (user_verified) {
51 Settings::values.citra_username = ui->edit_username->text().toStdString();
52 Settings::values.citra_token = ui->edit_token->text().toStdString();
53 } else {
54 QMessageBox::warning(this, tr("Username and token not verfied"),
55 tr("Username and token were not verified. The changes to your "
56 "username and/or token have not been saved."));
57 }
58 Settings::Apply();
59}
60
61void ConfigureWeb::RefreshTelemetryID() {
62 const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
63 ui->label_telemetry_id->setText(
64 tr("Telemetry ID: 0x%1").arg(QString::number(new_telemetry_id, 16).toUpper()));
65}
66
67void ConfigureWeb::OnLoginChanged() {
68 if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
69 user_verified = true;
70 ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
71 ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
72 } else {
73 user_verified = false;
74 ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
75 ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
76 }
77}
78
79void ConfigureWeb::VerifyLogin() {
80 verified =
81 Core::VerifyLogin(ui->edit_username->text().toStdString(),
82 ui->edit_token->text().toStdString(), [&]() { emit LoginVerified(); });
83 ui->button_verify_login->setDisabled(true);
84 ui->button_verify_login->setText(tr("Verifying"));
85}
86
87void ConfigureWeb::OnLoginVerified() {
88 ui->button_verify_login->setEnabled(true);
89 ui->button_verify_login->setText(tr("Verify"));
90 if (verified.get()) {
91 user_verified = true;
92 ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
93 ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
94 } else {
95 ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
96 ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
97 QMessageBox::critical(
98 this, tr("Verification failed"),
99 tr("Verification failed. Check that you have entered your username and token "
100 "correctly, and that your internet connection is working."));
101 }
102}
diff --git a/src/yuzu/configuration/configure_web.h b/src/yuzu/configuration/configure_web.h
deleted file mode 100644
index ad2d58f6e..000000000
--- a/src/yuzu/configuration/configure_web.h
+++ /dev/null
@@ -1,40 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <future>
8#include <memory>
9#include <QWidget>
10
11namespace Ui {
12class ConfigureWeb;
13}
14
15class ConfigureWeb : public QWidget {
16 Q_OBJECT
17
18public:
19 explicit ConfigureWeb(QWidget* parent = nullptr);
20 ~ConfigureWeb();
21
22 void applyConfiguration();
23
24public slots:
25 void RefreshTelemetryID();
26 void OnLoginChanged();
27 void VerifyLogin();
28 void OnLoginVerified();
29
30signals:
31 void LoginVerified();
32
33private:
34 void setConfiguration();
35
36 bool user_verified = true;
37 std::future<bool> verified;
38
39 std::unique_ptr<Ui::ConfigureWeb> ui;
40};
diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui
deleted file mode 100644
index dd996ab62..000000000
--- a/src/yuzu/configuration/configure_web.ui
+++ /dev/null
@@ -1,190 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureWeb</class>
4 <widget class="QWidget" name="ConfigureWeb">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>926</width>
10 <height>561</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBoxWebConfig">
21 <property name="title">
22 <string>Citra Web Service</string>
23 </property>
24 <layout class="QVBoxLayout" name="verticalLayoutCitraWebService">
25 <item>
26 <widget class="QLabel" name="web_credentials_disclaimer">
27 <property name="text">
28 <string>By providing your username and token, you agree to allow Citra to collect additional usage data, which may include user identifying information.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <layout class="QGridLayout" name="gridLayoutCitraUsername">
34 <item row="2" column="3">
35 <widget class="QPushButton" name="button_verify_login">
36 <property name="sizePolicy">
37 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
38 <horstretch>0</horstretch>
39 <verstretch>0</verstretch>
40 </sizepolicy>
41 </property>
42 <property name="layoutDirection">
43 <enum>Qt::RightToLeft</enum>
44 </property>
45 <property name="text">
46 <string>Verify</string>
47 </property>
48 </widget>
49 </item>
50 <item row="2" column="0">
51 <widget class="QLabel" name="web_signup_link">
52 <property name="text">
53 <string>Sign up</string>
54 </property>
55 </widget>
56 </item>
57 <item row="0" column="1" colspan="3">
58 <widget class="QLineEdit" name="edit_username">
59 <property name="maxLength">
60 <number>36</number>
61 </property>
62 </widget>
63 </item>
64 <item row="1" column="0">
65 <widget class="QLabel" name="label_token">
66 <property name="text">
67 <string>Token: </string>
68 </property>
69 </widget>
70 </item>
71 <item row="1" column="4">
72 <widget class="QLabel" name="label_token_verified">
73 </widget>
74 </item>
75 <item row="0" column="0">
76 <widget class="QLabel" name="label_username">
77 <property name="text">
78 <string>Username: </string>
79 </property>
80 </widget>
81 </item>
82 <item row="0" column="4">
83 <widget class="QLabel" name="label_username_verified">
84 </widget>
85 </item>
86 <item row="1" column="1" colspan="3">
87 <widget class="QLineEdit" name="edit_token">
88 <property name="maxLength">
89 <number>36</number>
90 </property>
91 <property name="echoMode">
92 <enum>QLineEdit::Password</enum>
93 </property>
94 </widget>
95 </item>
96 <item row="2" column="1">
97 <widget class="QLabel" name="web_token_info_link">
98 <property name="text">
99 <string>What is my token?</string>
100 </property>
101 </widget>
102 </item>
103 <item row="2" column="2">
104 <spacer name="horizontalSpacer">
105 <property name="orientation">
106 <enum>Qt::Horizontal</enum>
107 </property>
108 <property name="sizeHint" stdset="0">
109 <size>
110 <width>40</width>
111 <height>20</height>
112 </size>
113 </property>
114 </spacer>
115 </item>
116 </layout>
117 </item>
118 </layout>
119 </widget>
120 </item>
121 <item>
122 <widget class="QGroupBox" name="groupBox">
123 <property name="title">
124 <string>Telemetry</string>
125 </property>
126 <layout class="QVBoxLayout" name="verticalLayout_2">
127 <item>
128 <widget class="QCheckBox" name="toggle_telemetry">
129 <property name="text">
130 <string>Share anonymous usage data with the Citra team</string>
131 </property>
132 </widget>
133 </item>
134 <item>
135 <widget class="QLabel" name="telemetry_learn_more">
136 <property name="text">
137 <string>Learn more</string>
138 </property>
139 </widget>
140 </item>
141 <item>
142 <layout class="QGridLayout" name="gridLayoutTelemetryId">
143 <item row="0" column="0">
144 <widget class="QLabel" name="label_telemetry_id">
145 <property name="text">
146 <string>Telemetry ID:</string>
147 </property>
148 </widget>
149 </item>
150 <item row="0" column="1">
151 <widget class="QPushButton" name="button_regenerate_telemetry_id">
152 <property name="sizePolicy">
153 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
154 <horstretch>0</horstretch>
155 <verstretch>0</verstretch>
156 </sizepolicy>
157 </property>
158 <property name="layoutDirection">
159 <enum>Qt::RightToLeft</enum>
160 </property>
161 <property name="text">
162 <string>Regenerate</string>
163 </property>
164 </widget>
165 </item>
166 </layout>
167 </item>
168 </layout>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 <item>
174 <spacer name="verticalSpacer">
175 <property name="orientation">
176 <enum>Qt::Vertical</enum>
177 </property>
178 <property name="sizeHint" stdset="0">
179 <size>
180 <width>20</width>
181 <height>40</height>
182 </size>
183 </property>
184 </spacer>
185 </item>
186 </layout>
187 </widget>
188 <resources/>
189 <connections/>
190</ui>
diff --git a/src/yuzu/debugger/graphics/graphics.cpp b/src/yuzu/debugger/graphics/graphics.cpp
deleted file mode 100644
index 8154363a2..000000000
--- a/src/yuzu/debugger/graphics/graphics.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QListView>
6#include "citra_qt/debugger/graphics/graphics.h"
7#include "citra_qt/util/util.h"
8
9GraphicsDebugger g_debugger;
10
11GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent)
12 : QAbstractListModel(parent), command_count(0) {
13 connect(this, SIGNAL(GXCommandFinished(int)), this, SLOT(OnGXCommandFinishedInternal(int)));
14}
15
16int GPUCommandStreamItemModel::rowCount(const QModelIndex& parent) const {
17 return command_count;
18}
19
20QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) const {
21 if (!index.isValid())
22 return QVariant();
23
24 int command_index = index.row();
25 const Service::GSP::Command& command = GetDebugger()->ReadGXCommandHistory(command_index);
26 if (role == Qt::DisplayRole) {
27 std::map<Service::GSP::CommandId, const char*> command_names = {
28 {Service::GSP::CommandId::REQUEST_DMA, "REQUEST_DMA"},
29 {Service::GSP::CommandId::SUBMIT_GPU_CMDLIST, "SUBMIT_GPU_CMDLIST"},
30 {Service::GSP::CommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL"},
31 {Service::GSP::CommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER"},
32 {Service::GSP::CommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY"},
33 {Service::GSP::CommandId::CACHE_FLUSH, "CACHE_FLUSH"},
34 };
35 const u32* command_data = reinterpret_cast<const u32*>(&command);
36 QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9")
37 .arg(command_names[command.id])
38 .arg(command_data[0], 8, 16, QLatin1Char('0'))
39 .arg(command_data[1], 8, 16, QLatin1Char('0'))
40 .arg(command_data[2], 8, 16, QLatin1Char('0'))
41 .arg(command_data[3], 8, 16, QLatin1Char('0'))
42 .arg(command_data[4], 8, 16, QLatin1Char('0'))
43 .arg(command_data[5], 8, 16, QLatin1Char('0'))
44 .arg(command_data[6], 8, 16, QLatin1Char('0'))
45 .arg(command_data[7], 8, 16, QLatin1Char('0'));
46 return QVariant(str);
47 } else {
48 return QVariant();
49 }
50}
51
52void GPUCommandStreamItemModel::GXCommandProcessed(int total_command_count) {
53 emit GXCommandFinished(total_command_count);
54}
55
56void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_count) {
57 if (total_command_count == 0)
58 return;
59
60 int prev_command_count = command_count;
61 command_count = total_command_count;
62 emit dataChanged(index(prev_command_count, 0), index(total_command_count - 1, 0));
63}
64
65GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
66 : QDockWidget(tr("Graphics Debugger"), parent) {
67 setObjectName("GraphicsDebugger");
68
69 GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);
70 g_debugger.RegisterObserver(command_model);
71
72 QListView* command_list = new QListView;
73 command_list->setModel(command_model);
74 command_list->setFont(GetMonospaceFont());
75
76 setWidget(command_list);
77}
diff --git a/src/yuzu/debugger/graphics/graphics.h b/src/yuzu/debugger/graphics/graphics.h
deleted file mode 100644
index 8837fb792..000000000
--- a/src/yuzu/debugger/graphics/graphics.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QAbstractListModel>
8#include <QDockWidget>
9#include "video_core/gpu_debugger.h"
10
11class GPUCommandStreamItemModel : public QAbstractListModel,
12 public GraphicsDebugger::DebuggerObserver {
13 Q_OBJECT
14
15public:
16 explicit GPUCommandStreamItemModel(QObject* parent);
17
18 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
19 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
20
21public:
22 void GXCommandProcessed(int total_command_count) override;
23
24public slots:
25 void OnGXCommandFinishedInternal(int total_command_count);
26
27signals:
28 void GXCommandFinished(int total_command_count);
29
30private:
31 int command_count;
32};
33
34class GPUCommandStreamWidget : public QDockWidget {
35 Q_OBJECT
36
37public:
38 GPUCommandStreamWidget(QWidget* parent = nullptr);
39
40private:
41};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
deleted file mode 100644
index dc6070dea..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QMetaType>
6#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h"
7
8BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
9 const QString& title, QWidget* parent)
10 : QDockWidget(title, parent), BreakPointObserver(debug_context) {
11 qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
12
13 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
14
15 // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
16 // care of delaying its handling to the GUI thread.
17 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
18 SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
19}
20
21void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) {
22 emit BreakPointHit(event, data);
23}
24
25void BreakPointObserverDock::OnPicaResume() {
26 emit Resumed();
27}
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
deleted file mode 100644
index e77df4f5b..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
+++ /dev/null
@@ -1,33 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDockWidget>
8#include "video_core/debug_utils/debug_utils.h"
9
10/**
11 * Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots.
12 * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
13 * the widget usually wants to perform reactions in the GUI thread.
14 */
15class BreakPointObserverDock : public QDockWidget,
16 protected Pica::DebugContext::BreakPointObserver {
17 Q_OBJECT
18
19public:
20 BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title,
21 QWidget* parent = nullptr);
22
23 void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
24 void OnPicaResume() override;
25
26private slots:
27 virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0;
28 virtual void OnResumed() = 0;
29
30signals:
31 void Resumed();
32 void BreakPointHit(Pica::DebugContext::Event event, void* data);
33};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
deleted file mode 100644
index 030828ba8..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QLabel>
6#include <QMetaType>
7#include <QPushButton>
8#include <QTreeView>
9#include <QVBoxLayout>
10#include "citra_qt/debugger/graphics/graphics_breakpoints.h"
11#include "citra_qt/debugger/graphics/graphics_breakpoints_p.h"
12#include "common/assert.h"
13
14BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
15 : QAbstractListModel(parent), context_weak(debug_context),
16 at_breakpoint(debug_context->at_breakpoint),
17 active_breakpoint(debug_context->active_breakpoint) {}
18
19int BreakPointModel::columnCount(const QModelIndex& parent) const {
20 return 1;
21}
22
23int BreakPointModel::rowCount(const QModelIndex& parent) const {
24 return static_cast<int>(Pica::DebugContext::Event::NumEvents);
25}
26
27QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
28 const auto event = static_cast<Pica::DebugContext::Event>(index.row());
29
30 switch (role) {
31 case Qt::DisplayRole: {
32 if (index.column() == 0) {
33 static const std::map<Pica::DebugContext::Event, QString> map = {
34 {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")},
35 {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")},
36 {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")},
37 {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")},
38 {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")},
39 {Pica::DebugContext::Event::IncomingDisplayTransfer,
40 tr("Incoming display transfer")},
41 {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")},
42 {Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")},
43 };
44
45 DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
46 return (map.find(event) != map.end()) ? map.at(event) : QString();
47 }
48
49 break;
50 }
51
52 case Qt::CheckStateRole: {
53 if (index.column() == 0)
54 return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
55 break;
56 }
57
58 case Qt::BackgroundRole: {
59 if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
60 return QBrush(QColor(0xE0, 0xE0, 0x10));
61 }
62 break;
63 }
64
65 case Role_IsEnabled: {
66 auto context = context_weak.lock();
67 return context && context->breakpoints[(int)event].enabled;
68 }
69
70 default:
71 break;
72 }
73 return QVariant();
74}
75
76Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
77 if (!index.isValid())
78 return 0;
79
80 Qt::ItemFlags flags = Qt::ItemIsEnabled;
81 if (index.column() == 0)
82 flags |= Qt::ItemIsUserCheckable;
83 return flags;
84}
85
86bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
87 const auto event = static_cast<Pica::DebugContext::Event>(index.row());
88
89 switch (role) {
90 case Qt::CheckStateRole: {
91 if (index.column() != 0)
92 return false;
93
94 auto context = context_weak.lock();
95 if (!context)
96 return false;
97
98 context->breakpoints[(int)event].enabled = value == Qt::Checked;
99 QModelIndex changed_index = createIndex(index.row(), 0);
100 emit dataChanged(changed_index, changed_index);
101 return true;
102 }
103 }
104
105 return false;
106}
107
108void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) {
109 auto context = context_weak.lock();
110 if (!context)
111 return;
112
113 active_breakpoint = context->active_breakpoint;
114 at_breakpoint = context->at_breakpoint;
115 emit dataChanged(createIndex(static_cast<int>(event), 0),
116 createIndex(static_cast<int>(event), 0));
117}
118
119void BreakPointModel::OnResumed() {
120 auto context = context_weak.lock();
121 if (!context)
122 return;
123
124 at_breakpoint = context->at_breakpoint;
125 emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
126 createIndex(static_cast<int>(active_breakpoint), 0));
127 active_breakpoint = context->active_breakpoint;
128}
129
130GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
131 std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
132 : QDockWidget(tr("Pica Breakpoints"), parent),
133 Pica::DebugContext::BreakPointObserver(debug_context) {
134 setObjectName("PicaBreakPointsWidget");
135
136 status_text = new QLabel(tr("Emulation running"));
137 resume_button = new QPushButton(tr("Resume"));
138 resume_button->setEnabled(false);
139
140 breakpoint_model = new BreakPointModel(debug_context, this);
141 breakpoint_list = new QTreeView;
142 breakpoint_list->setRootIsDecorated(false);
143 breakpoint_list->setHeaderHidden(true);
144 breakpoint_list->setModel(breakpoint_model);
145
146 qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
147
148 connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
149 SLOT(OnItemDoubleClicked(const QModelIndex&)));
150
151 connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
152
153 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this,
154 SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
155 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
156
157 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model,
158 SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection);
159 connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
160
161 connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
162 breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
163
164 QWidget* main_widget = new QWidget;
165 auto main_layout = new QVBoxLayout;
166 {
167 auto sub_layout = new QHBoxLayout;
168 sub_layout->addWidget(status_text);
169 sub_layout->addWidget(resume_button);
170 main_layout->addLayout(sub_layout);
171 }
172 main_layout->addWidget(breakpoint_list);
173 main_widget->setLayout(main_layout);
174
175 setWidget(main_widget);
176}
177
178void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) {
179 // Process in GUI thread
180 emit BreakPointHit(event, data);
181}
182
183void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
184 status_text->setText(tr("Emulation halted at breakpoint"));
185 resume_button->setEnabled(true);
186}
187
188void GraphicsBreakPointsWidget::OnPicaResume() {
189 // Process in GUI thread
190 emit Resumed();
191}
192
193void GraphicsBreakPointsWidget::OnResumed() {
194 status_text->setText(tr("Emulation running"));
195 resume_button->setEnabled(false);
196}
197
198void GraphicsBreakPointsWidget::OnResumeRequested() {
199 if (auto context = context_weak.lock())
200 context->Resume();
201}
202
203void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
204 if (!index.isValid())
205 return;
206
207 QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0);
208 QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole);
209 QVariant new_state = Qt::Unchecked;
210 if (enabled == Qt::Unchecked)
211 new_state = Qt::Checked;
212 breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole);
213}
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h
deleted file mode 100644
index bec72a2db..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.h
+++ /dev/null
@@ -1,46 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDockWidget>
9#include "video_core/debug_utils/debug_utils.h"
10
11class QLabel;
12class QPushButton;
13class QTreeView;
14
15class BreakPointModel;
16
17class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver {
18 Q_OBJECT
19
20 using Event = Pica::DebugContext::Event;
21
22public:
23 explicit GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
24 QWidget* parent = nullptr);
25
26 void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
27 void OnPicaResume() override;
28
29public slots:
30 void OnBreakPointHit(Pica::DebugContext::Event event, void* data);
31 void OnItemDoubleClicked(const QModelIndex&);
32 void OnResumeRequested();
33 void OnResumed();
34
35signals:
36 void Resumed();
37 void BreakPointHit(Pica::DebugContext::Event event, void* data);
38 void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
39
40private:
41 QLabel* status_text;
42 QPushButton* resume_button;
43
44 BreakPointModel* breakpoint_model;
45 QTreeView* breakpoint_list;
46};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
deleted file mode 100644
index dc64706bd..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QAbstractListModel>
9#include "video_core/debug_utils/debug_utils.h"
10
11class BreakPointModel : public QAbstractListModel {
12 Q_OBJECT
13
14public:
15 enum {
16 Role_IsEnabled = Qt::UserRole,
17 };
18
19 BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent);
20
21 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
22 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
23 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
24 Qt::ItemFlags flags(const QModelIndex& index) const override;
25
26 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
27
28public slots:
29 void OnBreakPointHit(Pica::DebugContext::Event event);
30 void OnResumed();
31
32private:
33 std::weak_ptr<Pica::DebugContext> context_weak;
34 bool at_breakpoint;
35 Pica::DebugContext::Event active_breakpoint;
36};
diff --git a/src/yuzu/debugger/graphics/graphics_cmdlists.cpp b/src/yuzu/debugger/graphics/graphics_cmdlists.cpp
deleted file mode 100644
index ce2b9fa50..000000000
--- a/src/yuzu/debugger/graphics/graphics_cmdlists.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QApplication>
6#include <QClipboard>
7#include <QComboBox>
8#include <QHeaderView>
9#include <QLabel>
10#include <QListView>
11#include <QMainWindow>
12#include <QPushButton>
13#include <QSpinBox>
14#include <QTreeView>
15#include <QVBoxLayout>
16#include "citra_qt/debugger/graphics/graphics_cmdlists.h"
17#include "citra_qt/util/spinbox.h"
18#include "citra_qt/util/util.h"
19#include "common/vector_math.h"
20#include "core/memory.h"
21#include "video_core/debug_utils/debug_utils.h"
22#include "video_core/pica_state.h"
23#include "video_core/regs.h"
24#include "video_core/texture/texture_decode.h"
25
26namespace {
27QImage LoadTexture(const u8* src, const Pica::Texture::TextureInfo& info) {
28 QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
29 for (u32 y = 0; y < info.height; ++y) {
30 for (u32 x = 0; x < info.width; ++x) {
31 Math::Vec4<u8> color = Pica::Texture::LookupTexture(src, x, y, info, true);
32 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
33 }
34 }
35
36 return decoded_image;
37}
38
39class TextureInfoWidget : public QWidget {
40public:
41 TextureInfoWidget(const u8* src, const Pica::Texture::TextureInfo& info,
42 QWidget* parent = nullptr)
43 : QWidget(parent) {
44
45 QLabel* image_widget = new QLabel;
46 QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
47 image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
48 image_widget->setPixmap(image_pixmap);
49
50 QVBoxLayout* layout = new QVBoxLayout;
51 layout->addWidget(image_widget);
52 setLayout(layout);
53 }
54};
55} // Anonymous namespace
56
57GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {}
58
59int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
60 return static_cast<int>(pica_trace.writes.size());
61}
62
63int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
64 return 4;
65}
66
67QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
68 if (!index.isValid())
69 return QVariant();
70
71 const auto& write = pica_trace.writes[index.row()];
72
73 if (role == Qt::DisplayRole) {
74 switch (index.column()) {
75 case 0:
76 return QString::fromLatin1(Pica::Regs::GetRegisterName(write.cmd_id));
77 case 1:
78 return QString("%1").arg(write.cmd_id, 3, 16, QLatin1Char('0'));
79 case 2:
80 return QString("%1").arg(write.mask, 4, 2, QLatin1Char('0'));
81 case 3:
82 return QString("%1").arg(write.value, 8, 16, QLatin1Char('0'));
83 }
84 } else if (role == CommandIdRole) {
85 return QVariant::fromValue<int>(write.cmd_id);
86 }
87
88 return QVariant();
89}
90
91QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
92 switch (role) {
93 case Qt::DisplayRole: {
94 switch (section) {
95 case 0:
96 return tr("Command Name");
97 case 1:
98 return tr("Register");
99 case 2:
100 return tr("Mask");
101 case 3:
102 return tr("New Value");
103 }
104
105 break;
106 }
107 }
108
109 return QVariant();
110}
111
112void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) {
113 beginResetModel();
114
115 pica_trace = trace;
116
117 endResetModel();
118}
119
120#define COMMAND_IN_RANGE(cmd_id, reg_name) \
121 (cmd_id >= PICA_REG_INDEX(reg_name) && \
122 cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4)
123
124void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
125 const unsigned int command_id =
126 list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
127 if (COMMAND_IN_RANGE(command_id, texturing.texture0) ||
128 COMMAND_IN_RANGE(command_id, texturing.texture1) ||
129 COMMAND_IN_RANGE(command_id, texturing.texture2)) {
130
131 unsigned texture_index;
132 if (COMMAND_IN_RANGE(command_id, texturing.texture0)) {
133 texture_index = 0;
134 } else if (COMMAND_IN_RANGE(command_id, texturing.texture1)) {
135 texture_index = 1;
136 } else if (COMMAND_IN_RANGE(command_id, texturing.texture2)) {
137 texture_index = 2;
138 } else {
139 UNREACHABLE_MSG("Unknown texture command");
140 }
141
142 // TODO: Open a surface debugger
143 }
144}
145
146void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
147 QWidget* new_info_widget = nullptr;
148
149 const unsigned int command_id =
150 list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
151 if (COMMAND_IN_RANGE(command_id, texturing.texture0) ||
152 COMMAND_IN_RANGE(command_id, texturing.texture1) ||
153 COMMAND_IN_RANGE(command_id, texturing.texture2)) {
154
155 unsigned texture_index;
156 if (COMMAND_IN_RANGE(command_id, texturing.texture0)) {
157 texture_index = 0;
158 } else if (COMMAND_IN_RANGE(command_id, texturing.texture1)) {
159 texture_index = 1;
160 } else {
161 texture_index = 2;
162 }
163
164 const auto texture = Pica::g_state.regs.texturing.GetTextures()[texture_index];
165 const auto config = texture.config;
166 const auto format = texture.format;
167
168 const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
169 const u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress());
170 new_info_widget = new TextureInfoWidget(src, info);
171 }
172 if (command_info_widget) {
173 delete command_info_widget;
174 command_info_widget = nullptr;
175 }
176 if (new_info_widget) {
177 widget()->layout()->addWidget(new_info_widget);
178 command_info_widget = new_info_widget;
179 }
180}
181#undef COMMAND_IN_RANGE
182
183GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
184 : QDockWidget(tr("Pica Command List"), parent) {
185 setObjectName("Pica Command List");
186 GPUCommandListModel* model = new GPUCommandListModel(this);
187
188 QWidget* main_widget = new QWidget;
189
190 list_widget = new QTreeView;
191 list_widget->setModel(model);
192 list_widget->setFont(GetMonospaceFont());
193 list_widget->setRootIsDecorated(false);
194 list_widget->setUniformRowHeights(true);
195
196#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
197 list_widget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
198#else
199 list_widget->header()->setResizeMode(QHeaderView::ResizeToContents);
200#endif
201
202 connect(list_widget->selectionModel(),
203 SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this,
204 SLOT(SetCommandInfo(const QModelIndex&)));
205 connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)), this,
206 SLOT(OnCommandDoubleClicked(const QModelIndex&)));
207
208 toggle_tracing = new QPushButton(tr("Start Tracing"));
209 QPushButton* copy_all = new QPushButton(tr("Copy All"));
210
211 connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
212 connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model,
213 SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
214
215 connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));
216
217 command_info_widget = nullptr;
218
219 QVBoxLayout* main_layout = new QVBoxLayout;
220 main_layout->addWidget(list_widget);
221 {
222 QHBoxLayout* sub_layout = new QHBoxLayout;
223 sub_layout->addWidget(toggle_tracing);
224 sub_layout->addWidget(copy_all);
225 main_layout->addLayout(sub_layout);
226 }
227 main_widget->setLayout(main_layout);
228
229 setWidget(main_widget);
230}
231
232void GPUCommandListWidget::OnToggleTracing() {
233 if (!Pica::DebugUtils::IsPicaTracing()) {
234 Pica::DebugUtils::StartPicaTracing();
235 toggle_tracing->setText(tr("Finish Tracing"));
236 } else {
237 pica_trace = Pica::DebugUtils::FinishPicaTracing();
238 emit TracingFinished(*pica_trace);
239 toggle_tracing->setText(tr("Start Tracing"));
240 }
241}
242
243void GPUCommandListWidget::CopyAllToClipboard() {
244 QClipboard* clipboard = QApplication::clipboard();
245 QString text;
246
247 QAbstractItemModel* model = static_cast<QAbstractItemModel*>(list_widget->model());
248
249 for (int row = 0; row < model->rowCount({}); ++row) {
250 for (int col = 0; col < model->columnCount({}); ++col) {
251 QModelIndex index = model->index(row, col);
252 text += model->data(index).value<QString>();
253 text += '\t';
254 }
255 text += '\n';
256 }
257
258 clipboard->setText(text);
259}
diff --git a/src/yuzu/debugger/graphics/graphics_cmdlists.h b/src/yuzu/debugger/graphics/graphics_cmdlists.h
deleted file mode 100644
index 8f40b94c5..000000000
--- a/src/yuzu/debugger/graphics/graphics_cmdlists.h
+++ /dev/null
@@ -1,61 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QAbstractListModel>
8#include <QDockWidget>
9#include "video_core/debug_utils/debug_utils.h"
10#include "video_core/gpu_debugger.h"
11
12class QPushButton;
13class QTreeView;
14
15class GPUCommandListModel : public QAbstractListModel {
16 Q_OBJECT
17
18public:
19 enum {
20 CommandIdRole = Qt::UserRole,
21 };
22
23 explicit GPUCommandListModel(QObject* parent);
24
25 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
26 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
27 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
28 QVariant headerData(int section, Qt::Orientation orientation,
29 int role = Qt::DisplayRole) const override;
30
31public slots:
32 void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
33
34private:
35 Pica::DebugUtils::PicaTrace pica_trace;
36};
37
38class GPUCommandListWidget : public QDockWidget {
39 Q_OBJECT
40
41public:
42 explicit GPUCommandListWidget(QWidget* parent = nullptr);
43
44public slots:
45 void OnToggleTracing();
46 void OnCommandDoubleClicked(const QModelIndex&);
47
48 void SetCommandInfo(const QModelIndex&);
49
50 void CopyAllToClipboard();
51
52signals:
53 void TracingFinished(const Pica::DebugUtils::PicaTrace&);
54
55private:
56 std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
57
58 QTreeView* list_widget;
59 QWidget* command_info_widget;
60 QPushButton* toggle_tracing;
61};
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
deleted file mode 100644
index c974545ef..000000000
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ /dev/null
@@ -1,713 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QBoxLayout>
6#include <QComboBox>
7#include <QDebug>
8#include <QFileDialog>
9#include <QLabel>
10#include <QMouseEvent>
11#include <QPushButton>
12#include <QScrollArea>
13#include <QSpinBox>
14#include "citra_qt/debugger/graphics/graphics_surface.h"
15#include "citra_qt/util/spinbox.h"
16#include "common/color.h"
17#include "core/hw/gpu.h"
18#include "core/memory.h"
19#include "video_core/pica_state.h"
20#include "video_core/regs_framebuffer.h"
21#include "video_core/regs_texturing.h"
22#include "video_core/texture/texture_decode.h"
23#include "video_core/utils.h"
24
25SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
26 : QLabel(parent), surface_widget(surface_widget_) {}
27SurfacePicture::~SurfacePicture() {}
28
29void SurfacePicture::mousePressEvent(QMouseEvent* event) {
30 // Only do something while the left mouse button is held down
31 if (!(event->buttons() & Qt::LeftButton))
32 return;
33
34 if (pixmap() == nullptr)
35 return;
36
37 if (surface_widget)
38 surface_widget->Pick(event->x() * pixmap()->width() / width(),
39 event->y() * pixmap()->height() / height());
40}
41
42void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
43 // We also want to handle the event if the user moves the mouse while holding down the LMB
44 mousePressEvent(event);
45}
46
47GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
48 QWidget* parent)
49 : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
50 surface_source(Source::ColorBuffer) {
51 setObjectName("PicaSurface");
52
53 surface_source_list = new QComboBox;
54 surface_source_list->addItem(tr("Color Buffer"));
55 surface_source_list->addItem(tr("Depth Buffer"));
56 surface_source_list->addItem(tr("Stencil Buffer"));
57 surface_source_list->addItem(tr("Texture 0"));
58 surface_source_list->addItem(tr("Texture 1"));
59 surface_source_list->addItem(tr("Texture 2"));
60 surface_source_list->addItem(tr("Custom"));
61 surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
62
63 surface_address_control = new CSpinBox;
64 surface_address_control->SetBase(16);
65 surface_address_control->SetRange(0, 0xFFFFFFFF);
66 surface_address_control->SetPrefix("0x");
67
68 unsigned max_dimension = 16384; // TODO: Find actual maximum
69
70 surface_width_control = new QSpinBox;
71 surface_width_control->setRange(0, max_dimension);
72
73 surface_height_control = new QSpinBox;
74 surface_height_control->setRange(0, max_dimension);
75
76 surface_picker_x_control = new QSpinBox;
77 surface_picker_x_control->setRange(0, max_dimension - 1);
78
79 surface_picker_y_control = new QSpinBox;
80 surface_picker_y_control->setRange(0, max_dimension - 1);
81
82 surface_format_control = new QComboBox;
83
84 // Color formats sorted by Pica texture format index
85 surface_format_control->addItem(tr("RGBA8"));
86 surface_format_control->addItem(tr("RGB8"));
87 surface_format_control->addItem(tr("RGB5A1"));
88 surface_format_control->addItem(tr("RGB565"));
89 surface_format_control->addItem(tr("RGBA4"));
90 surface_format_control->addItem(tr("IA8"));
91 surface_format_control->addItem(tr("RG8"));
92 surface_format_control->addItem(tr("I8"));
93 surface_format_control->addItem(tr("A8"));
94 surface_format_control->addItem(tr("IA4"));
95 surface_format_control->addItem(tr("I4"));
96 surface_format_control->addItem(tr("A4"));
97 surface_format_control->addItem(tr("ETC1"));
98 surface_format_control->addItem(tr("ETC1A4"));
99 surface_format_control->addItem(tr("D16"));
100 surface_format_control->addItem(tr("D24"));
101 surface_format_control->addItem(tr("D24X8"));
102 surface_format_control->addItem(tr("X24S8"));
103 surface_format_control->addItem(tr("Unknown"));
104
105 surface_info_label = new QLabel();
106 surface_info_label->setWordWrap(true);
107
108 surface_picture_label = new SurfacePicture(0, this);
109 surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
110 surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
111 surface_picture_label->setScaledContents(false);
112
113 auto scroll_area = new QScrollArea();
114 scroll_area->setBackgroundRole(QPalette::Dark);
115 scroll_area->setWidgetResizable(false);
116 scroll_area->setWidget(surface_picture_label);
117
118 save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
119
120 // Connections
121 connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
122 connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
123 SLOT(OnSurfaceSourceChanged(int)));
124 connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
125 SLOT(OnSurfaceAddressChanged(qint64)));
126 connect(surface_width_control, SIGNAL(valueChanged(int)), this,
127 SLOT(OnSurfaceWidthChanged(int)));
128 connect(surface_height_control, SIGNAL(valueChanged(int)), this,
129 SLOT(OnSurfaceHeightChanged(int)));
130 connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
131 SLOT(OnSurfaceFormatChanged(int)));
132 connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
133 SLOT(OnSurfacePickerXChanged(int)));
134 connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
135 SLOT(OnSurfacePickerYChanged(int)));
136 connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
137
138 auto main_widget = new QWidget;
139 auto main_layout = new QVBoxLayout;
140 {
141 auto sub_layout = new QHBoxLayout;
142 sub_layout->addWidget(new QLabel(tr("Source:")));
143 sub_layout->addWidget(surface_source_list);
144 main_layout->addLayout(sub_layout);
145 }
146 {
147 auto sub_layout = new QHBoxLayout;
148 sub_layout->addWidget(new QLabel(tr("Physical Address:")));
149 sub_layout->addWidget(surface_address_control);
150 main_layout->addLayout(sub_layout);
151 }
152 {
153 auto sub_layout = new QHBoxLayout;
154 sub_layout->addWidget(new QLabel(tr("Width:")));
155 sub_layout->addWidget(surface_width_control);
156 main_layout->addLayout(sub_layout);
157 }
158 {
159 auto sub_layout = new QHBoxLayout;
160 sub_layout->addWidget(new QLabel(tr("Height:")));
161 sub_layout->addWidget(surface_height_control);
162 main_layout->addLayout(sub_layout);
163 }
164 {
165 auto sub_layout = new QHBoxLayout;
166 sub_layout->addWidget(new QLabel(tr("Format:")));
167 sub_layout->addWidget(surface_format_control);
168 main_layout->addLayout(sub_layout);
169 }
170 main_layout->addWidget(scroll_area);
171
172 auto info_layout = new QHBoxLayout;
173 {
174 auto xy_layout = new QVBoxLayout;
175 {
176 {
177 auto sub_layout = new QHBoxLayout;
178 sub_layout->addWidget(new QLabel(tr("X:")));
179 sub_layout->addWidget(surface_picker_x_control);
180 xy_layout->addLayout(sub_layout);
181 }
182 {
183 auto sub_layout = new QHBoxLayout;
184 sub_layout->addWidget(new QLabel(tr("Y:")));
185 sub_layout->addWidget(surface_picker_y_control);
186 xy_layout->addLayout(sub_layout);
187 }
188 }
189 info_layout->addLayout(xy_layout);
190 surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
191 info_layout->addWidget(surface_info_label);
192 }
193 main_layout->addLayout(info_layout);
194
195 main_layout->addWidget(save_surface);
196 main_widget->setLayout(main_layout);
197 setWidget(main_widget);
198
199 // Load current data - TODO: Make sure this works when emulation is not running
200 if (debug_context && debug_context->at_breakpoint) {
201 emit Update();
202 widget()->setEnabled(debug_context->at_breakpoint);
203 } else {
204 widget()->setEnabled(false);
205 }
206}
207
208void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
209 emit Update();
210 widget()->setEnabled(true);
211}
212
213void GraphicsSurfaceWidget::OnResumed() {
214 widget()->setEnabled(false);
215}
216
217void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
218 surface_source = static_cast<Source>(new_value);
219 emit Update();
220}
221
222void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
223 if (surface_address != new_value) {
224 surface_address = static_cast<unsigned>(new_value);
225
226 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
227 emit Update();
228 }
229}
230
231void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
232 if (surface_width != static_cast<unsigned>(new_value)) {
233 surface_width = static_cast<unsigned>(new_value);
234
235 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
236 emit Update();
237 }
238}
239
240void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
241 if (surface_height != static_cast<unsigned>(new_value)) {
242 surface_height = static_cast<unsigned>(new_value);
243
244 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
245 emit Update();
246 }
247}
248
249void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
250 if (surface_format != static_cast<Format>(new_value)) {
251 surface_format = static_cast<Format>(new_value);
252
253 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
254 emit Update();
255 }
256}
257
258void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
259 if (surface_picker_x != new_value) {
260 surface_picker_x = new_value;
261 Pick(surface_picker_x, surface_picker_y);
262 }
263}
264
265void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
266 if (surface_picker_y != new_value) {
267 surface_picker_y = new_value;
268 Pick(surface_picker_x, surface_picker_y);
269 }
270}
271
272void GraphicsSurfaceWidget::Pick(int x, int y) {
273 surface_picker_x_control->setValue(x);
274 surface_picker_y_control->setValue(y);
275
276 if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
277 y >= static_cast<int>(surface_height)) {
278 surface_info_label->setText(tr("Pixel out of bounds"));
279 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
280 return;
281 }
282
283 u8* buffer = Memory::GetPhysicalPointer(surface_address);
284 if (buffer == nullptr) {
285 surface_info_label->setText(tr("(unable to access pixel data)"));
286 surface_info_label->setAlignment(Qt::AlignCenter);
287 return;
288 }
289
290 unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
291 unsigned stride = nibbles_per_pixel * surface_width / 2;
292
293 unsigned bytes_per_pixel;
294 bool nibble_mode = (nibbles_per_pixel == 1);
295 if (nibble_mode) {
296 // As nibbles are contained in a byte we still need to access one byte per nibble
297 bytes_per_pixel = 1;
298 } else {
299 bytes_per_pixel = nibbles_per_pixel / 2;
300 }
301
302 const u32 coarse_y = y & ~7;
303 u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
304 const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
305
306 auto GetText = [offset](Format format, const u8* pixel) {
307 switch (format) {
308 case Format::RGBA8: {
309 auto value = Color::DecodeRGBA8(pixel) / 255.0f;
310 return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
311 .arg(QString::number(value.r(), 'f', 2))
312 .arg(QString::number(value.g(), 'f', 2))
313 .arg(QString::number(value.b(), 'f', 2))
314 .arg(QString::number(value.a(), 'f', 2));
315 }
316 case Format::RGB8: {
317 auto value = Color::DecodeRGB8(pixel) / 255.0f;
318 return QString("Red: %1, Green: %2, Blue: %3")
319 .arg(QString::number(value.r(), 'f', 2))
320 .arg(QString::number(value.g(), 'f', 2))
321 .arg(QString::number(value.b(), 'f', 2));
322 }
323 case Format::RGB5A1: {
324 auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
325 return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
326 .arg(QString::number(value.r(), 'f', 2))
327 .arg(QString::number(value.g(), 'f', 2))
328 .arg(QString::number(value.b(), 'f', 2))
329 .arg(QString::number(value.a(), 'f', 2));
330 }
331 case Format::RGB565: {
332 auto value = Color::DecodeRGB565(pixel) / 255.0f;
333 return QString("Red: %1, Green: %2, Blue: %3")
334 .arg(QString::number(value.r(), 'f', 2))
335 .arg(QString::number(value.g(), 'f', 2))
336 .arg(QString::number(value.b(), 'f', 2));
337 }
338 case Format::RGBA4: {
339 auto value = Color::DecodeRGBA4(pixel) / 255.0f;
340 return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
341 .arg(QString::number(value.r(), 'f', 2))
342 .arg(QString::number(value.g(), 'f', 2))
343 .arg(QString::number(value.b(), 'f', 2))
344 .arg(QString::number(value.a(), 'f', 2));
345 }
346 case Format::IA8:
347 return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
348 case Format::RG8: {
349 auto value = Color::DecodeRG8(pixel) / 255.0f;
350 return QString("Red: %1, Green: %2")
351 .arg(QString::number(value.r(), 'f', 2))
352 .arg(QString::number(value.g(), 'f', 2));
353 }
354 case Format::I8:
355 return QString("Index: %1").arg(*pixel);
356 case Format::A8:
357 return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
358 case Format::IA4:
359 return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
360 case Format::I4: {
361 u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
362 return QString("Index: %1").arg(i);
363 }
364 case Format::A4: {
365 u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
366 return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
367 }
368 case Format::ETC1:
369 case Format::ETC1A4:
370 // TODO: Display block information or channel values?
371 return QString("Compressed data");
372 case Format::D16: {
373 auto value = Color::DecodeD16(pixel);
374 return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
375 }
376 case Format::D24: {
377 auto value = Color::DecodeD24(pixel);
378 return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
379 }
380 case Format::D24X8:
381 case Format::X24S8: {
382 auto values = Color::DecodeD24S8(pixel);
383 return QString("Depth: %1, Stencil: %2")
384 .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
385 .arg(values[1]);
386 }
387 case Format::Unknown:
388 return QString("Unknown format");
389 default:
390 return QString("Unhandled format");
391 }
392 return QString("");
393 };
394
395 QString nibbles = "";
396 for (unsigned i = 0; i < nibbles_per_pixel; i++) {
397 unsigned nibble_index = i;
398 if (nibble_mode) {
399 nibble_index += (offset % 2) ? 0 : 1;
400 }
401 u8 byte = pixel[nibble_index / 2];
402 u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
403 nibbles.append(QString::number(nibble, 16).toUpper());
404 }
405
406 surface_info_label->setText(
407 QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
408 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
409}
410
411void GraphicsSurfaceWidget::OnUpdate() {
412 QPixmap pixmap;
413
414 switch (surface_source) {
415 case Source::ColorBuffer: {
416 // TODO: Store a reference to the registers in the debug context instead of accessing them
417 // directly...
418
419 const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
420
421 surface_address = framebuffer.GetColorBufferPhysicalAddress();
422 surface_width = framebuffer.GetWidth();
423 surface_height = framebuffer.GetHeight();
424
425 switch (framebuffer.color_format) {
426 case Pica::FramebufferRegs::ColorFormat::RGBA8:
427 surface_format = Format::RGBA8;
428 break;
429
430 case Pica::FramebufferRegs::ColorFormat::RGB8:
431 surface_format = Format::RGB8;
432 break;
433
434 case Pica::FramebufferRegs::ColorFormat::RGB5A1:
435 surface_format = Format::RGB5A1;
436 break;
437
438 case Pica::FramebufferRegs::ColorFormat::RGB565:
439 surface_format = Format::RGB565;
440 break;
441
442 case Pica::FramebufferRegs::ColorFormat::RGBA4:
443 surface_format = Format::RGBA4;
444 break;
445
446 default:
447 surface_format = Format::Unknown;
448 break;
449 }
450
451 break;
452 }
453
454 case Source::DepthBuffer: {
455 const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
456
457 surface_address = framebuffer.GetDepthBufferPhysicalAddress();
458 surface_width = framebuffer.GetWidth();
459 surface_height = framebuffer.GetHeight();
460
461 switch (framebuffer.depth_format) {
462 case Pica::FramebufferRegs::DepthFormat::D16:
463 surface_format = Format::D16;
464 break;
465
466 case Pica::FramebufferRegs::DepthFormat::D24:
467 surface_format = Format::D24;
468 break;
469
470 case Pica::FramebufferRegs::DepthFormat::D24S8:
471 surface_format = Format::D24X8;
472 break;
473
474 default:
475 surface_format = Format::Unknown;
476 break;
477 }
478
479 break;
480 }
481
482 case Source::StencilBuffer: {
483 const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
484
485 surface_address = framebuffer.GetDepthBufferPhysicalAddress();
486 surface_width = framebuffer.GetWidth();
487 surface_height = framebuffer.GetHeight();
488
489 switch (framebuffer.depth_format) {
490 case Pica::FramebufferRegs::DepthFormat::D24S8:
491 surface_format = Format::X24S8;
492 break;
493
494 default:
495 surface_format = Format::Unknown;
496 break;
497 }
498
499 break;
500 }
501
502 case Source::Texture0:
503 case Source::Texture1:
504 case Source::Texture2: {
505 unsigned texture_index;
506 if (surface_source == Source::Texture0)
507 texture_index = 0;
508 else if (surface_source == Source::Texture1)
509 texture_index = 1;
510 else if (surface_source == Source::Texture2)
511 texture_index = 2;
512 else {
513 qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
514 break;
515 }
516
517 const auto texture = Pica::g_state.regs.texturing.GetTextures()[texture_index];
518 auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
519
520 surface_address = info.physical_address;
521 surface_width = info.width;
522 surface_height = info.height;
523 surface_format = static_cast<Format>(info.format);
524
525 if (surface_format > Format::MaxTextureFormat) {
526 qDebug() << "Unknown texture format " << static_cast<int>(info.format);
527 }
528 break;
529 }
530
531 case Source::Custom: {
532 // Keep user-specified values
533 break;
534 }
535
536 default:
537 qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
538 break;
539 }
540
541 surface_address_control->SetValue(surface_address);
542 surface_width_control->setValue(surface_width);
543 surface_height_control->setValue(surface_height);
544 surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
545
546 // TODO: Implement a good way to visualize alpha components!
547
548 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
549 u8* buffer = Memory::GetPhysicalPointer(surface_address);
550
551 if (buffer == nullptr) {
552 surface_picture_label->hide();
553 surface_info_label->setText(tr("(invalid surface address)"));
554 surface_info_label->setAlignment(Qt::AlignCenter);
555 surface_picker_x_control->setEnabled(false);
556 surface_picker_y_control->setEnabled(false);
557 save_surface->setEnabled(false);
558 return;
559 }
560
561 if (surface_format == Format::Unknown) {
562 surface_picture_label->hide();
563 surface_info_label->setText(tr("(unknown surface format)"));
564 surface_info_label->setAlignment(Qt::AlignCenter);
565 surface_picker_x_control->setEnabled(false);
566 surface_picker_y_control->setEnabled(false);
567 save_surface->setEnabled(false);
568 return;
569 }
570
571 surface_picture_label->show();
572
573 if (surface_format <= Format::MaxTextureFormat) {
574 // Generate a virtual texture
575 Pica::Texture::TextureInfo info;
576 info.physical_address = surface_address;
577 info.width = surface_width;
578 info.height = surface_height;
579 info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface_format);
580 info.SetDefaultStride();
581
582 for (unsigned int y = 0; y < surface_height; ++y) {
583 for (unsigned int x = 0; x < surface_width; ++x) {
584 Math::Vec4<u8> color = Pica::Texture::LookupTexture(buffer, x, y, info, true);
585 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
586 }
587 }
588 } else {
589 // We handle depth formats here because DebugUtils only supports TextureFormats
590
591 // TODO(yuriks): Convert to newer tile-based addressing
592 unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
593 unsigned stride = nibbles_per_pixel * surface_width / 2;
594
595 ASSERT_MSG(nibbles_per_pixel >= 2,
596 "Depth decoder only supports formats with at least one byte per pixel");
597 unsigned bytes_per_pixel = nibbles_per_pixel / 2;
598
599 for (unsigned int y = 0; y < surface_height; ++y) {
600 for (unsigned int x = 0; x < surface_width; ++x) {
601 const u32 coarse_y = y & ~7;
602 u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
603 const u8* pixel = buffer + offset;
604 Math::Vec4<u8> color = {0, 0, 0, 0};
605
606 switch (surface_format) {
607 case Format::D16: {
608 u32 data = Color::DecodeD16(pixel);
609 color.r() = data & 0xFF;
610 color.g() = (data >> 8) & 0xFF;
611 break;
612 }
613 case Format::D24: {
614 u32 data = Color::DecodeD24(pixel);
615 color.r() = data & 0xFF;
616 color.g() = (data >> 8) & 0xFF;
617 color.b() = (data >> 16) & 0xFF;
618 break;
619 }
620 case Format::D24X8: {
621 Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
622 color.r() = data.x & 0xFF;
623 color.g() = (data.x >> 8) & 0xFF;
624 color.b() = (data.x >> 16) & 0xFF;
625 break;
626 }
627 case Format::X24S8: {
628 Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
629 color.r() = color.g() = color.b() = data.y;
630 break;
631 }
632 default:
633 qDebug() << "Unknown surface format " << static_cast<int>(surface_format);
634 break;
635 }
636
637 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
638 }
639 }
640 }
641
642 pixmap = QPixmap::fromImage(decoded_image);
643 surface_picture_label->setPixmap(pixmap);
644 surface_picture_label->resize(pixmap.size());
645
646 // Update the info with pixel data
647 surface_picker_x_control->setEnabled(true);
648 surface_picker_y_control->setEnabled(true);
649 Pick(surface_picker_x, surface_picker_y);
650
651 // Enable saving the converted pixmap to file
652 save_surface->setEnabled(true);
653}
654
655void GraphicsSurfaceWidget::SaveSurface() {
656 QString png_filter = tr("Portable Network Graphic (*.png)");
657 QString bin_filter = tr("Binary data (*.bin)");
658
659 QString selectedFilter;
660 QString filename = QFileDialog::getSaveFileName(
661 this, tr("Save Surface"),
662 QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
663 QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
664
665 if (filename.isEmpty()) {
666 // If the user canceled the dialog, don't save anything.
667 return;
668 }
669
670 if (selectedFilter == png_filter) {
671 const QPixmap* pixmap = surface_picture_label->pixmap();
672 ASSERT_MSG(pixmap != nullptr, "No pixmap set");
673
674 QFile file(filename);
675 file.open(QIODevice::WriteOnly);
676 if (pixmap)
677 pixmap->save(&file, "PNG");
678 } else if (selectedFilter == bin_filter) {
679 const u8* buffer = Memory::GetPhysicalPointer(surface_address);
680 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
681
682 QFile file(filename);
683 file.open(QIODevice::WriteOnly);
684 int size = surface_width * surface_height * NibblesPerPixel(surface_format) / 2;
685 QByteArray data(reinterpret_cast<const char*>(buffer), size);
686 file.write(data);
687 } else {
688 UNREACHABLE_MSG("Unhandled filter selected");
689 }
690}
691
692unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
693 if (format <= Format::MaxTextureFormat) {
694 return Pica::TexturingRegs::NibblesPerPixel(
695 static_cast<Pica::TexturingRegs::TextureFormat>(format));
696 }
697
698 switch (format) {
699 case Format::D24X8:
700 case Format::X24S8:
701 return 4 * 2;
702 case Format::D24:
703 return 3 * 2;
704 case Format::D16:
705 return 2 * 2;
706 default:
707 UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this "
708 "function should be given a format which is in "
709 "GraphicsSurfaceWidget::Format. Instead got %i",
710 static_cast<int>(format));
711 return 0;
712 }
713}
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
deleted file mode 100644
index 28f5650a7..000000000
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ /dev/null
@@ -1,118 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QLabel>
8#include <QPushButton>
9#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h"
10
11class QComboBox;
12class QSpinBox;
13class CSpinBox;
14
15class GraphicsSurfaceWidget;
16
17class SurfacePicture : public QLabel {
18 Q_OBJECT
19
20public:
21 explicit SurfacePicture(QWidget* parent = nullptr,
22 GraphicsSurfaceWidget* surface_widget = nullptr);
23 ~SurfacePicture();
24
25protected slots:
26 virtual void mouseMoveEvent(QMouseEvent* event);
27 virtual void mousePressEvent(QMouseEvent* event);
28
29private:
30 GraphicsSurfaceWidget* surface_widget;
31};
32
33class GraphicsSurfaceWidget : public BreakPointObserverDock {
34 Q_OBJECT
35
36 using Event = Pica::DebugContext::Event;
37
38 enum class Source {
39 ColorBuffer = 0,
40 DepthBuffer = 1,
41 StencilBuffer = 2,
42 Texture0 = 3,
43 Texture1 = 4,
44 Texture2 = 5,
45 Custom = 6,
46 };
47
48 enum class Format {
49 // These must match the TextureFormat type!
50 RGBA8 = 0,
51 RGB8 = 1,
52 RGB5A1 = 2,
53 RGB565 = 3,
54 RGBA4 = 4,
55 IA8 = 5,
56 RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
57 I8 = 7,
58 A8 = 8,
59 IA4 = 9,
60 I4 = 10,
61 A4 = 11,
62 ETC1 = 12, // compressed
63 ETC1A4 = 13,
64 MaxTextureFormat = 13,
65 D16 = 14,
66 D24 = 15,
67 D24X8 = 16,
68 X24S8 = 17,
69 Unknown = 18,
70 };
71
72 static unsigned int NibblesPerPixel(Format format);
73
74public:
75 explicit GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context,
76 QWidget* parent = nullptr);
77 void Pick(int x, int y);
78
79public slots:
80 void OnSurfaceSourceChanged(int new_value);
81 void OnSurfaceAddressChanged(qint64 new_value);
82 void OnSurfaceWidthChanged(int new_value);
83 void OnSurfaceHeightChanged(int new_value);
84 void OnSurfaceFormatChanged(int new_value);
85 void OnSurfacePickerXChanged(int new_value);
86 void OnSurfacePickerYChanged(int new_value);
87 void OnUpdate();
88
89private slots:
90 void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
91 void OnResumed() override;
92
93 void SaveSurface();
94
95signals:
96 void Update();
97
98private:
99 QComboBox* surface_source_list;
100 CSpinBox* surface_address_control;
101 QSpinBox* surface_width_control;
102 QSpinBox* surface_height_control;
103 QComboBox* surface_format_control;
104
105 SurfacePicture* surface_picture_label;
106 QSpinBox* surface_picker_x_control;
107 QSpinBox* surface_picker_y_control;
108 QLabel* surface_info_label;
109 QPushButton* save_surface;
110
111 Source surface_source;
112 unsigned surface_address;
113 unsigned surface_width;
114 unsigned surface_height;
115 Format surface_format;
116 int surface_picker_x = 0;
117 int surface_picker_y = 0;
118};
diff --git a/src/yuzu/debugger/graphics/graphics_tracing.cpp b/src/yuzu/debugger/graphics/graphics_tracing.cpp
deleted file mode 100644
index 40d5bed51..000000000
--- a/src/yuzu/debugger/graphics/graphics_tracing.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <iterator>
8#include <memory>
9#include <QBoxLayout>
10#include <QComboBox>
11#include <QFileDialog>
12#include <QMessageBox>
13#include <QPushButton>
14#include <boost/range/algorithm/copy.hpp>
15#include "citra_qt/debugger/graphics/graphics_tracing.h"
16#include "common/common_types.h"
17#include "core/hw/gpu.h"
18#include "core/hw/lcd.h"
19#include "core/tracer/recorder.h"
20#include "nihstro/float24.h"
21#include "video_core/pica_state.h"
22
23GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
24 QWidget* parent)
25 : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) {
26
27 setObjectName("CiTracing");
28
29 QPushButton* start_recording = new QPushButton(tr("Start Recording"));
30 QPushButton* stop_recording =
31 new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
32 QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
33
34 connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording,
35 SLOT(setVisible(bool)));
36 connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording,
37 SLOT(setVisible(bool)));
38 connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording,
39 SLOT(setVisible(bool)));
40 connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
41 connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
42 connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
43
44 stop_recording->setVisible(false);
45 abort_recording->setVisible(false);
46
47 auto main_widget = new QWidget;
48 auto main_layout = new QVBoxLayout;
49 {
50 auto sub_layout = new QHBoxLayout;
51 sub_layout->addWidget(start_recording);
52 sub_layout->addWidget(stop_recording);
53 sub_layout->addWidget(abort_recording);
54 main_layout->addLayout(sub_layout);
55 }
56 main_widget->setLayout(main_layout);
57 setWidget(main_widget);
58}
59
60void GraphicsTracingWidget::StartRecording() {
61 auto context = context_weak.lock();
62 if (!context)
63 return;
64
65 auto shader_binary = Pica::g_state.vs.program_code;
66 auto swizzle_data = Pica::g_state.vs.swizzle_data;
67
68 // Encode floating point numbers to 24-bit values
69 // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally.
70 std::array<u32, 4 * 16> default_attributes;
71 for (unsigned i = 0; i < 16; ++i) {
72 for (unsigned comp = 0; comp < 3; ++comp) {
73 default_attributes[4 * i + comp] = nihstro::to_float24(
74 Pica::g_state.input_default_attributes.attr[i][comp].ToFloat32());
75 }
76 }
77
78 std::array<u32, 4 * 96> vs_float_uniforms;
79 for (unsigned i = 0; i < 96; ++i)
80 for (unsigned comp = 0; comp < 3; ++comp)
81 vs_float_uniforms[4 * i + comp] =
82 nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
83
84 CiTrace::Recorder::InitialState state;
85 std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32),
86 std::back_inserter(state.gpu_registers));
87 std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32),
88 std::back_inserter(state.lcd_registers));
89 std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32),
90 std::back_inserter(state.pica_registers));
91 boost::copy(default_attributes, std::back_inserter(state.default_attributes));
92 boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
93 boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
94 boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
95 // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
96 // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
97 // boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
98
99 auto recorder = new CiTrace::Recorder(state);
100 context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
101
102 emit SetStartTracingButtonEnabled(false);
103 emit SetStopTracingButtonEnabled(true);
104 emit SetAbortTracingButtonEnabled(true);
105}
106
107void GraphicsTracingWidget::StopRecording() {
108 auto context = context_weak.lock();
109 if (!context)
110 return;
111
112 QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf",
113 tr("CiTrace File (*.ctf)"));
114
115 if (filename.isEmpty()) {
116 // If the user canceled the dialog, keep recording
117 return;
118 }
119
120 context->recorder->Finish(filename.toStdString());
121 context->recorder = nullptr;
122
123 emit SetStopTracingButtonEnabled(false);
124 emit SetAbortTracingButtonEnabled(false);
125 emit SetStartTracingButtonEnabled(true);
126}
127
128void GraphicsTracingWidget::AbortRecording() {
129 auto context = context_weak.lock();
130 if (!context)
131 return;
132
133 context->recorder = nullptr;
134
135 emit SetStopTracingButtonEnabled(false);
136 emit SetAbortTracingButtonEnabled(false);
137 emit SetStartTracingButtonEnabled(true);
138}
139
140void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
141 widget()->setEnabled(true);
142}
143
144void GraphicsTracingWidget::OnResumed() {
145 widget()->setEnabled(false);
146}
147
148void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) {
149 // Disable tracing starting/stopping until a GPU breakpoint is reached
150 widget()->setEnabled(false);
151}
152
153void GraphicsTracingWidget::OnEmulationStopping() {
154 // TODO: Is it safe to access the context here?
155
156 auto context = context_weak.lock();
157 if (!context)
158 return;
159
160 if (context->recorder) {
161 auto reply =
162 QMessageBox::question(this, tr("CiTracing still active"),
163 tr("A CiTrace is still being recorded. Do you want to save it? "
164 "If not, all recorded data will be discarded."),
165 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
166
167 if (reply == QMessageBox::Yes) {
168 StopRecording();
169 } else {
170 AbortRecording();
171 }
172 }
173
174 // If the widget was disabled before, enable it now to allow starting
175 // tracing before starting the next emulation session
176 widget()->setEnabled(true);
177}
diff --git a/src/yuzu/debugger/graphics/graphics_tracing.h b/src/yuzu/debugger/graphics/graphics_tracing.h
deleted file mode 100644
index eb1292c29..000000000
--- a/src/yuzu/debugger/graphics/graphics_tracing.h
+++ /dev/null
@@ -1,33 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h"
8
9class EmuThread;
10
11class GraphicsTracingWidget : public BreakPointObserverDock {
12 Q_OBJECT
13
14public:
15 explicit GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
16 QWidget* parent = nullptr);
17
18 void OnEmulationStarting(EmuThread* emu_thread);
19 void OnEmulationStopping();
20
21private slots:
22 void StartRecording();
23 void StopRecording();
24 void AbortRecording();
25
26 void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
27 void OnResumed() override;
28
29signals:
30 void SetStartTracingButtonEnabled(bool enable);
31 void SetStopTracingButtonEnabled(bool enable);
32 void SetAbortTracingButtonEnabled(bool enable);
33};
diff --git a/src/yuzu/debugger/graphics/graphics_vertex_shader.cpp b/src/yuzu/debugger/graphics/graphics_vertex_shader.cpp
deleted file mode 100644
index 7f4ec0c52..000000000
--- a/src/yuzu/debugger/graphics/graphics_vertex_shader.cpp
+++ /dev/null
@@ -1,622 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <iomanip>
6#include <sstream>
7#include <QBoxLayout>
8#include <QFileDialog>
9#include <QFormLayout>
10#include <QGroupBox>
11#include <QLabel>
12#include <QLineEdit>
13#include <QPushButton>
14#include <QSignalMapper>
15#include <QSpinBox>
16#include <QTreeView>
17#include "citra_qt/debugger/graphics/graphics_vertex_shader.h"
18#include "citra_qt/util/util.h"
19#include "video_core/pica_state.h"
20#include "video_core/shader/debug_data.h"
21#include "video_core/shader/shader.h"
22#include "video_core/shader/shader_interpreter.h"
23
24using nihstro::OpCode;
25using nihstro::Instruction;
26using nihstro::SourceRegister;
27using nihstro::SwizzlePattern;
28
29GraphicsVertexShaderModel::GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent)
30 : QAbstractTableModel(parent), par(parent) {}
31
32int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const {
33 return 3;
34}
35
36int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const {
37 return static_cast<int>(par->info.code.size());
38}
39
40QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation,
41 int role) const {
42 switch (role) {
43 case Qt::DisplayRole: {
44 if (section == 0) {
45 return tr("Offset");
46 } else if (section == 1) {
47 return tr("Raw");
48 } else if (section == 2) {
49 return tr("Disassembly");
50 }
51
52 break;
53 }
54 }
55
56 return QVariant();
57}
58
59static std::string SelectorToString(u32 selector) {
60 std::string ret;
61 for (int i = 0; i < 4; ++i) {
62 int component = (selector >> ((3 - i) * 2)) & 3;
63 ret += "xyzw"[component];
64 }
65 return ret;
66}
67
68// e.g. "-c92[a0.x].xyzw"
69static void print_input(std::ostringstream& output, const SourceRegister& input, bool negate,
70 const std::string& swizzle_mask, bool align = true,
71 const std::string& address_register_name = std::string()) {
72 if (align)
73 output << std::setw(4) << std::right;
74 output << ((negate ? "-" : "") + input.GetName());
75
76 if (!address_register_name.empty())
77 output << '[' << address_register_name << ']';
78 output << '.' << swizzle_mask;
79};
80
81QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const {
82 switch (role) {
83 case Qt::DisplayRole: {
84 switch (index.column()) {
85 case 0:
86 if (par->info.HasLabel(index.row()))
87 return QString::fromStdString(par->info.GetLabel(index.row()));
88
89 return QString("%1").arg(4 * index.row(), 4, 16, QLatin1Char('0'));
90
91 case 1:
92 return QString("%1").arg(par->info.code[index.row()].hex, 8, 16, QLatin1Char('0'));
93
94 case 2: {
95 std::ostringstream output;
96 output.flags(std::ios::uppercase);
97
98 // To make the code aligning columns of assembly easier to keep track of, this function
99 // keeps track of the start of the start of the previous column, allowing alignment
100 // based on desired field widths.
101 int current_column = 0;
102 auto AlignToColumn = [&](int col_width) {
103 // Prints spaces to the output to pad previous column to size and advances the
104 // column marker.
105 current_column += col_width;
106 int to_add = std::max(1, current_column - (int)output.tellp());
107 for (int i = 0; i < to_add; ++i) {
108 output << ' ';
109 }
110 };
111
112 const Instruction instr = par->info.code[index.row()];
113 const OpCode opcode = instr.opcode;
114 const OpCode::Info opcode_info = opcode.GetInfo();
115 const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
116 ? instr.mad.operand_desc_id.Value()
117 : instr.common.operand_desc_id.Value();
118 const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
119
120 // longest known instruction name: "setemit "
121 int kOpcodeColumnWidth = 8;
122 // "rXX.xyzw "
123 int kOutputColumnWidth = 10;
124 // "-rXX.xyzw ", no attempt is made to align indexed inputs
125 int kInputOperandColumnWidth = 11;
126
127 output << opcode_info.name;
128
129 switch (opcode_info.type) {
130 case OpCode::Type::Trivial:
131 // Nothing to do here
132 break;
133
134 case OpCode::Type::Arithmetic:
135 case OpCode::Type::MultiplyAdd: {
136 // Use custom code for special instructions
137 switch (opcode.EffectiveOpCode()) {
138 case OpCode::Id::CMP: {
139 AlignToColumn(kOpcodeColumnWidth);
140
141 // NOTE: CMP always writes both cc components, so we do not consider the dest
142 // mask here.
143 output << " cc.xy";
144 AlignToColumn(kOutputColumnWidth);
145
146 SourceRegister src1 = instr.common.GetSrc1(false);
147 SourceRegister src2 = instr.common.GetSrc2(false);
148
149 output << ' ';
150 print_input(output, src1, swizzle.negate_src1,
151 swizzle.SelectorToString(false).substr(0, 1), false,
152 instr.common.AddressRegisterName());
153 output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.x)
154 << ' ';
155 print_input(output, src2, swizzle.negate_src2,
156 swizzle.SelectorToString(true).substr(0, 1), false);
157
158 output << ", ";
159
160 print_input(output, src1, swizzle.negate_src1,
161 swizzle.SelectorToString(false).substr(1, 1), false,
162 instr.common.AddressRegisterName());
163 output << ' ' << instr.common.compare_op.ToString(instr.common.compare_op.y)
164 << ' ';
165 print_input(output, src2, swizzle.negate_src2,
166 swizzle.SelectorToString(true).substr(1, 1), false);
167
168 break;
169 }
170
171 case OpCode::Id::MAD:
172 case OpCode::Id::MADI: {
173 AlignToColumn(kOpcodeColumnWidth);
174
175 bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
176 SourceRegister src1 = instr.mad.GetSrc1(src_is_inverted);
177 SourceRegister src2 = instr.mad.GetSrc2(src_is_inverted);
178 SourceRegister src3 = instr.mad.GetSrc3(src_is_inverted);
179
180 output << std::setw(3) << std::right << instr.mad.dest.Value().GetName() << '.'
181 << swizzle.DestMaskToString();
182 AlignToColumn(kOutputColumnWidth);
183 print_input(output, src1, swizzle.negate_src1,
184 SelectorToString(swizzle.src1_selector));
185 AlignToColumn(kInputOperandColumnWidth);
186 print_input(output, src2, swizzle.negate_src2,
187 SelectorToString(swizzle.src2_selector), true,
188 src_is_inverted ? "" : instr.mad.AddressRegisterName());
189 AlignToColumn(kInputOperandColumnWidth);
190 print_input(output, src3, swizzle.negate_src3,
191 SelectorToString(swizzle.src3_selector), true,
192 src_is_inverted ? instr.mad.AddressRegisterName() : "");
193 AlignToColumn(kInputOperandColumnWidth);
194 break;
195 }
196
197 default: {
198 AlignToColumn(kOpcodeColumnWidth);
199
200 bool src_is_inverted = 0 != (opcode_info.subtype & OpCode::Info::SrcInversed);
201
202 if (opcode_info.subtype & OpCode::Info::Dest) {
203 // e.g. "r12.xy__"
204 output << std::setw(3) << std::right << instr.common.dest.Value().GetName()
205 << '.' << swizzle.DestMaskToString();
206 } else if (opcode_info.subtype == OpCode::Info::MOVA) {
207 output << " a0." << swizzle.DestMaskToString();
208 }
209 AlignToColumn(kOutputColumnWidth);
210
211 if (opcode_info.subtype & OpCode::Info::Src1) {
212 SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
213 print_input(output, src1, swizzle.negate_src1,
214 swizzle.SelectorToString(false), true,
215 src_is_inverted ? "" : instr.common.AddressRegisterName());
216 AlignToColumn(kInputOperandColumnWidth);
217 }
218
219 if (opcode_info.subtype & OpCode::Info::Src2) {
220 SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
221 print_input(output, src2, swizzle.negate_src2,
222 swizzle.SelectorToString(true), true,
223 src_is_inverted ? instr.common.AddressRegisterName() : "");
224 AlignToColumn(kInputOperandColumnWidth);
225 }
226 break;
227 }
228 }
229
230 break;
231 }
232
233 case OpCode::Type::Conditional:
234 case OpCode::Type::UniformFlowControl: {
235 output << ' ';
236
237 switch (opcode.EffectiveOpCode()) {
238 case OpCode::Id::LOOP:
239 output << 'i' << instr.flow_control.int_uniform_id << " (end on 0x"
240 << std::setw(4) << std::right << std::setfill('0') << std::hex
241 << (4 * instr.flow_control.dest_offset) << ")";
242 break;
243
244 default:
245 if (opcode_info.subtype & OpCode::Info::HasCondition) {
246 output << '(';
247
248 if (instr.flow_control.op != instr.flow_control.JustY) {
249 if (!instr.flow_control.refx)
250 output << '!';
251 output << "cc.x";
252 }
253
254 if (instr.flow_control.op == instr.flow_control.Or) {
255 output << " || ";
256 } else if (instr.flow_control.op == instr.flow_control.And) {
257 output << " && ";
258 }
259
260 if (instr.flow_control.op != instr.flow_control.JustX) {
261 if (!instr.flow_control.refy)
262 output << '!';
263 output << "cc.y";
264 }
265
266 output << ") ";
267 } else if (opcode_info.subtype & OpCode::Info::HasUniformIndex) {
268 if (opcode.EffectiveOpCode() == OpCode::Id::JMPU &&
269 (instr.flow_control.num_instructions & 1) == 1) {
270 output << '!';
271 }
272 output << 'b' << instr.flow_control.bool_uniform_id << ' ';
273 }
274
275 if (opcode_info.subtype & OpCode::Info::HasAlternative) {
276 output << "else jump to 0x" << std::setw(4) << std::right
277 << std::setfill('0') << std::hex
278 << (4 * instr.flow_control.dest_offset);
279 } else if (opcode_info.subtype & OpCode::Info::HasExplicitDest) {
280 output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0')
281 << std::hex << (4 * instr.flow_control.dest_offset);
282 } else {
283 // TODO: Handle other cases
284 output << "(unknown destination)";
285 }
286
287 if (opcode_info.subtype & OpCode::Info::HasFinishPoint) {
288 output << " (return on 0x" << std::setw(4) << std::right
289 << std::setfill('0') << std::hex
290 << (4 * instr.flow_control.dest_offset +
291 4 * instr.flow_control.num_instructions)
292 << ')';
293 }
294
295 break;
296 }
297 break;
298 }
299
300 default:
301 output << " (unknown instruction format)";
302 break;
303 }
304
305 return QString::fromLatin1(output.str().c_str());
306 }
307
308 default:
309 break;
310 }
311 }
312
313 case Qt::FontRole:
314 return GetMonospaceFont();
315
316 case Qt::BackgroundRole: {
317 // Highlight current instruction
318 int current_record_index = par->cycle_index->value();
319 if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
320 const auto& current_record = par->debug_data.records[current_record_index];
321 if (index.row() == static_cast<int>(current_record.instruction_offset)) {
322 return QColor(255, 255, 63);
323 }
324 }
325
326 // Use a grey background for instructions which have no debug data associated to them
327 for (const auto& record : par->debug_data.records)
328 if (index.row() == static_cast<int>(record.instruction_offset))
329 return QVariant();
330
331 return QBrush(QColor(192, 192, 192));
332 }
333
334 // TODO: Draw arrows for each "reachable" instruction to visualize control flow
335
336 default:
337 break;
338 }
339
340 return QVariant();
341}
342
343void GraphicsVertexShaderWidget::DumpShader() {
344 QString filename = QFileDialog::getSaveFileName(
345 this, tr("Save Shader Dump"), "shader_dump.shbin", tr("Shader Binary (*.shbin)"));
346
347 if (filename.isEmpty()) {
348 // If the user canceled the dialog, don't dump anything.
349 return;
350 }
351
352 auto& setup = Pica::g_state.vs;
353 auto& config = Pica::g_state.regs.vs;
354
355 Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup,
356 Pica::g_state.regs.rasterizer.vs_output_attributes);
357}
358
359GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
360 std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent)
361 : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) {
362 setObjectName("PicaVertexShader");
363
364 // Clear input vertex data so that it contains valid float values in case a debug shader
365 // execution happens before the first Vertex Loaded breakpoint.
366 // TODO: This makes a crash in the interpreter much less likely, but not impossible. The
367 // interpreter should guard against out-of-bounds accesses to ensure crashes in it aren't
368 // possible.
369 std::memset(&input_vertex, 0, sizeof(input_vertex));
370
371 auto input_data_mapper = new QSignalMapper(this);
372
373 // TODO: Support inputting data in hexadecimal raw format
374 for (unsigned i = 0; i < ARRAY_SIZE(input_data); ++i) {
375 input_data[i] = new QLineEdit;
376 input_data[i]->setValidator(new QDoubleValidator(input_data[i]));
377 }
378
379 breakpoint_warning =
380 new QLabel(tr("(data only available at vertex shader invocation breakpoints)"));
381
382 // TODO: Add some button for jumping to the shader entry point
383
384 model = new GraphicsVertexShaderModel(this);
385 binary_list = new QTreeView;
386 binary_list->setModel(model);
387 binary_list->setRootIsDecorated(false);
388 binary_list->setAlternatingRowColors(true);
389
390 auto dump_shader = new QPushButton(QIcon::fromTheme("document-save"), tr("Dump"));
391
392 instruction_description = new QLabel;
393
394 cycle_index = new QSpinBox;
395
396 connect(dump_shader, SIGNAL(clicked()), this, SLOT(DumpShader()));
397
398 connect(cycle_index, SIGNAL(valueChanged(int)), this, SLOT(OnCycleIndexChanged(int)));
399
400 for (unsigned i = 0; i < ARRAY_SIZE(input_data); ++i) {
401 connect(input_data[i], SIGNAL(textEdited(const QString&)), input_data_mapper, SLOT(map()));
402 input_data_mapper->setMapping(input_data[i], i);
403 }
404 connect(input_data_mapper, SIGNAL(mapped(int)), this, SLOT(OnInputAttributeChanged(int)));
405
406 auto main_widget = new QWidget;
407 auto main_layout = new QVBoxLayout;
408 {
409 auto input_data_group = new QGroupBox(tr("Input Data"));
410
411 // For each vertex attribute, add a QHBoxLayout consisting of:
412 // - A QLabel denoting the source attribute index
413 // - Four QLineEdits for showing and manipulating attribute data
414 // - A QLabel denoting the shader input attribute index
415 auto sub_layout = new QVBoxLayout;
416 for (unsigned i = 0; i < 16; ++i) {
417 // Create an HBoxLayout to store the widgets used to specify a particular attribute
418 // and store it in a QWidget to allow for easy hiding and unhiding.
419 auto row_layout = new QHBoxLayout;
420 // Remove unnecessary padding between rows
421 row_layout->setContentsMargins(0, 0, 0, 0);
422
423 row_layout->addWidget(new QLabel(tr("Attribute %1").arg(i, 2)));
424 for (unsigned comp = 0; comp < 4; ++comp)
425 row_layout->addWidget(input_data[4 * i + comp]);
426
427 row_layout->addWidget(input_data_mapping[i] = new QLabel);
428
429 input_data_container[i] = new QWidget;
430 input_data_container[i]->setLayout(row_layout);
431 input_data_container[i]->hide();
432
433 sub_layout->addWidget(input_data_container[i]);
434 }
435
436 sub_layout->addWidget(breakpoint_warning);
437 breakpoint_warning->hide();
438
439 input_data_group->setLayout(sub_layout);
440 main_layout->addWidget(input_data_group);
441 }
442
443 // Make program listing expand to fill available space in the dialog
444 binary_list->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
445 main_layout->addWidget(binary_list);
446
447 main_layout->addWidget(dump_shader);
448 {
449 auto sub_layout = new QFormLayout;
450 sub_layout->addRow(tr("Cycle Index:"), cycle_index);
451
452 main_layout->addLayout(sub_layout);
453 }
454
455 // Set a minimum height so that the size of this label doesn't cause the rest of the bottom
456 // part of the UI to keep jumping up and down when cycling through instructions.
457 instruction_description->setMinimumHeight(instruction_description->fontMetrics().lineSpacing() *
458 6);
459 instruction_description->setAlignment(Qt::AlignLeft | Qt::AlignTop);
460 main_layout->addWidget(instruction_description);
461
462 main_widget->setLayout(main_layout);
463 setWidget(main_widget);
464
465 widget()->setEnabled(false);
466}
467
468void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
469 if (event == Pica::DebugContext::Event::VertexShaderInvocation) {
470 Reload(true, data);
471 } else {
472 // No vertex data is retrievable => invalidate currently stored vertex data
473 Reload(true, nullptr);
474 }
475 widget()->setEnabled(true);
476}
477
478void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_data) {
479 model->beginResetModel();
480
481 if (replace_vertex_data) {
482 if (vertex_data) {
483 memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
484 for (unsigned attr = 0; attr < 16; ++attr) {
485 for (unsigned comp = 0; comp < 4; ++comp) {
486 input_data[4 * attr + comp]->setText(
487 QString("%1").arg(input_vertex.attr[attr][comp].ToFloat32()));
488 }
489 }
490 breakpoint_warning->hide();
491 } else {
492 for (unsigned attr = 0; attr < 16; ++attr) {
493 for (unsigned comp = 0; comp < 4; ++comp) {
494 input_data[4 * attr + comp]->setText(QString("???"));
495 }
496 }
497 breakpoint_warning->show();
498 }
499 }
500
501 // Reload shader code
502 info.Clear();
503
504 auto& shader_setup = Pica::g_state.vs;
505 auto& shader_config = Pica::g_state.regs.vs;
506 for (auto instr : shader_setup.program_code)
507 info.code.push_back({instr});
508 int num_attributes = shader_config.max_input_attribute_index + 1;
509
510 for (auto pattern : shader_setup.swizzle_data)
511 info.swizzle_info.push_back({pattern});
512
513 u32 entry_point = Pica::g_state.regs.vs.main_offset;
514 info.labels.insert({entry_point, "main"});
515
516 // Generate debug information
517 Pica::Shader::InterpreterEngine shader_engine;
518 shader_engine.SetupBatch(shader_setup, entry_point);
519 debug_data = shader_engine.ProduceDebugInfo(shader_setup, input_vertex, shader_config);
520
521 // Reload widget state
522 for (int attr = 0; attr < num_attributes; ++attr) {
523 unsigned source_attr = shader_config.GetRegisterForAttribute(attr);
524 input_data_mapping[attr]->setText(QString("-> v%1").arg(source_attr));
525 input_data_container[attr]->setVisible(true);
526 }
527 // Only show input attributes which are used as input to the shader
528 for (unsigned int attr = num_attributes; attr < 16; ++attr) {
529 input_data_container[attr]->setVisible(false);
530 }
531
532 // Initialize debug info text for current cycle count
533 cycle_index->setMaximum(static_cast<int>(debug_data.records.size() - 1));
534 OnCycleIndexChanged(cycle_index->value());
535
536 model->endResetModel();
537}
538
539void GraphicsVertexShaderWidget::OnResumed() {
540 widget()->setEnabled(false);
541}
542
543void GraphicsVertexShaderWidget::OnInputAttributeChanged(int index) {
544 float value = input_data[index]->text().toFloat();
545 input_vertex.attr[index / 4][index % 4] = Pica::float24::FromFloat32(value);
546 // Re-execute shader with updated value
547 Reload();
548}
549
550void GraphicsVertexShaderWidget::OnCycleIndexChanged(int index) {
551 QString text;
552
553 auto& record = debug_data.records[index];
554 if (record.mask & Pica::Shader::DebugDataRecord::SRC1)
555 text += tr("SRC1: %1, %2, %3, %4\n")
556 .arg(record.src1.x.ToFloat32())
557 .arg(record.src1.y.ToFloat32())
558 .arg(record.src1.z.ToFloat32())
559 .arg(record.src1.w.ToFloat32());
560 if (record.mask & Pica::Shader::DebugDataRecord::SRC2)
561 text += tr("SRC2: %1, %2, %3, %4\n")
562 .arg(record.src2.x.ToFloat32())
563 .arg(record.src2.y.ToFloat32())
564 .arg(record.src2.z.ToFloat32())
565 .arg(record.src2.w.ToFloat32());
566 if (record.mask & Pica::Shader::DebugDataRecord::SRC3)
567 text += tr("SRC3: %1, %2, %3, %4\n")
568 .arg(record.src3.x.ToFloat32())
569 .arg(record.src3.y.ToFloat32())
570 .arg(record.src3.z.ToFloat32())
571 .arg(record.src3.w.ToFloat32());
572 if (record.mask & Pica::Shader::DebugDataRecord::DEST_IN)
573 text += tr("DEST_IN: %1, %2, %3, %4\n")
574 .arg(record.dest_in.x.ToFloat32())
575 .arg(record.dest_in.y.ToFloat32())
576 .arg(record.dest_in.z.ToFloat32())
577 .arg(record.dest_in.w.ToFloat32());
578 if (record.mask & Pica::Shader::DebugDataRecord::DEST_OUT)
579 text += tr("DEST_OUT: %1, %2, %3, %4\n")
580 .arg(record.dest_out.x.ToFloat32())
581 .arg(record.dest_out.y.ToFloat32())
582 .arg(record.dest_out.z.ToFloat32())
583 .arg(record.dest_out.w.ToFloat32());
584
585 if (record.mask & Pica::Shader::DebugDataRecord::ADDR_REG_OUT)
586 text += tr("Address Registers: %1, %2\n")
587 .arg(record.address_registers[0])
588 .arg(record.address_registers[1]);
589 if (record.mask & Pica::Shader::DebugDataRecord::CMP_RESULT)
590 text += tr("Compare Result: %1, %2\n")
591 .arg(record.conditional_code[0] ? "true" : "false")
592 .arg(record.conditional_code[1] ? "true" : "false");
593
594 if (record.mask & Pica::Shader::DebugDataRecord::COND_BOOL_IN)
595 text += tr("Static Condition: %1\n").arg(record.cond_bool ? "true" : "false");
596 if (record.mask & Pica::Shader::DebugDataRecord::COND_CMP_IN)
597 text += tr("Dynamic Conditions: %1, %2\n")
598 .arg(record.cond_cmp[0] ? "true" : "false")
599 .arg(record.cond_cmp[1] ? "true" : "false");
600 if (record.mask & Pica::Shader::DebugDataRecord::LOOP_INT_IN)
601 text += tr("Loop Parameters: %1 (repeats), %2 (initializer), %3 (increment), %4\n")
602 .arg(record.loop_int.x)
603 .arg(record.loop_int.y)
604 .arg(record.loop_int.z)
605 .arg(record.loop_int.w);
606
607 text +=
608 tr("Instruction offset: 0x%1").arg(4 * record.instruction_offset, 4, 16, QLatin1Char('0'));
609 if (record.mask & Pica::Shader::DebugDataRecord::NEXT_INSTR) {
610 text += tr(" -> 0x%2").arg(4 * record.next_instruction, 4, 16, QLatin1Char('0'));
611 } else {
612 text += tr(" (last instruction)");
613 }
614
615 instruction_description->setText(text);
616
617 // Emit model update notification and scroll to current instruction
618 QModelIndex instr_index = model->index(record.instruction_offset, 0);
619 emit model->dataChanged(instr_index,
620 model->index(record.instruction_offset, model->columnCount()));
621 binary_list->scrollTo(instr_index, QAbstractItemView::EnsureVisible);
622}
diff --git a/src/yuzu/debugger/graphics/graphics_vertex_shader.h b/src/yuzu/debugger/graphics/graphics_vertex_shader.h
deleted file mode 100644
index c249a2ff8..000000000
--- a/src/yuzu/debugger/graphics/graphics_vertex_shader.h
+++ /dev/null
@@ -1,88 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QAbstractTableModel>
8#include <QTreeView>
9#include "citra_qt/debugger/graphics/graphics_breakpoint_observer.h"
10#include "nihstro/parser_shbin.h"
11#include "video_core/shader/debug_data.h"
12#include "video_core/shader/shader.h"
13
14class QLabel;
15class QSpinBox;
16
17class GraphicsVertexShaderWidget;
18
19class GraphicsVertexShaderModel : public QAbstractTableModel {
20 Q_OBJECT
21
22public:
23 explicit GraphicsVertexShaderModel(GraphicsVertexShaderWidget* parent);
24
25 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
26 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
27 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
28 QVariant headerData(int section, Qt::Orientation orientation,
29 int role = Qt::DisplayRole) const override;
30
31private:
32 GraphicsVertexShaderWidget* par;
33
34 friend class GraphicsVertexShaderWidget;
35};
36
37class GraphicsVertexShaderWidget : public BreakPointObserverDock {
38 Q_OBJECT
39
40 using Event = Pica::DebugContext::Event;
41
42public:
43 GraphicsVertexShaderWidget(std::shared_ptr<Pica::DebugContext> debug_context,
44 QWidget* parent = nullptr);
45
46private slots:
47 void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
48 void OnResumed() override;
49
50 void OnInputAttributeChanged(int index);
51
52 void OnCycleIndexChanged(int index);
53
54 void DumpShader();
55
56 /**
57 * Reload widget based on the current PICA200 state
58 * @param replace_vertex_data If true, invalidate all current vertex data
59 * @param vertex_data New vertex data to use, as passed to OnBreakPointHit. May be nullptr to
60 * specify that no valid vertex data can be retrieved currently. Only used if
61 * replace_vertex_data is true.
62 */
63 void Reload(bool replace_vertex_data = false, void* vertex_data = nullptr);
64
65private:
66 QLabel* instruction_description;
67 QTreeView* binary_list;
68 GraphicsVertexShaderModel* model;
69
70 /// TODO: Move these into a single struct
71 std::array<QLineEdit*, 4 * 16>
72 input_data; // A text box for each of the 4 components of up to 16 vertex attributes
73 std::array<QWidget*, 16>
74 input_data_container; // QWidget containing the QLayout containing each vertex attribute
75 std::array<QLabel*, 16> input_data_mapping; // A QLabel denoting the shader input attribute
76 // which the vertex attribute maps to
77
78 // Text to be shown when input vertex data is not retrievable
79 QLabel* breakpoint_warning;
80
81 QSpinBox* cycle_index;
82
83 nihstro::ShaderInfo info;
84 Pica::Shader::DebugData<true> debug_data;
85 Pica::Shader::AttributeBuffer input_vertex;
86
87 friend class GraphicsVertexShaderModel;
88};
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index f060bbe08..cc9babe84 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -7,10 +7,10 @@
7#include <QMouseEvent> 7#include <QMouseEvent>
8#include <QPainter> 8#include <QPainter>
9#include <QString> 9#include <QString>
10#include "citra_qt/debugger/profiler.h"
11#include "citra_qt/util/util.h"
12#include "common/common_types.h" 10#include "common/common_types.h"
13#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "yuzu/debugger/profiler.h"
13#include "yuzu/util/util.h"
14 14
15// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the 15// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the
16// non-Qt frontends don't need it (and don't implement the UI drawing hooks either). 16// non-Qt frontends don't need it (and don't implement the UI drawing hooks either).
diff --git a/src/yuzu/debugger/registers.cpp b/src/yuzu/debugger/registers.cpp
index f9345c9f6..a52725075 100644
--- a/src/yuzu/debugger/registers.cpp
+++ b/src/yuzu/debugger/registers.cpp
@@ -3,10 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QTreeWidgetItem> 5#include <QTreeWidgetItem>
6#include "citra_qt/debugger/registers.h"
7#include "citra_qt/util/util.h"
8#include "core/arm/arm_interface.h" 6#include "core/arm/arm_interface.h"
9#include "core/core.h" 7#include "core/core.h"
8#include "yuzu/debugger/registers.h"
9#include "yuzu/util/util.h"
10
10 11
11RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { 12RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
12 cpu_regs_ui.setupUi(this); 13 cpu_regs_ui.setupUi(this);
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index eefbcb9f1..70471923e 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/debugger/wait_tree.h" 5#include "yuzu/debugger/wait_tree.h"
6#include "citra_qt/util/util.h" 6#include "yuzu/util/util.h"
7 7
8#include "core/hle/kernel/condition_variable.h" 8#include "core/hle/kernel/condition_variable.h"
9#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 9881296d9..e40cd38b3 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -9,7 +9,7 @@
9#include <QRunnable> 9#include <QRunnable>
10#include <QStandardItem> 10#include <QStandardItem>
11#include <QString> 11#include <QString>
12#include "citra_qt/util/util.h" 12#include "yuzu/util/util.h"
13#include "common/string_util.h" 13#include "common/string_util.h"
14 14
15/** 15/**
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 158ed506f..42f026464 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -6,8 +6,8 @@
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QShortcut> 7#include <QShortcut>
8#include <QtGlobal> 8#include <QtGlobal>
9#include "citra_qt/hotkeys.h" 9#include "yuzu/hotkeys.h"
10#include "citra_qt/ui_settings.h" 10#include "yuzu/ui_settings.h"
11 11
12struct Hotkey { 12struct Hotkey {
13 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} 13 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 943aee30d..dc9bcab6f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -13,22 +13,6 @@
13#include <QMessageBox> 13#include <QMessageBox>
14#include <QtGui> 14#include <QtGui>
15#include <QtWidgets> 15#include <QtWidgets>
16#include "citra_qt/bootmanager.h"
17#include "citra_qt/configuration/config.h"
18#include "citra_qt/configuration/configure_dialog.h"
19#include "citra_qt/debugger/graphics/graphics.h"
20#include "citra_qt/debugger/graphics/graphics_breakpoints.h"
21#include "citra_qt/debugger/graphics/graphics_cmdlists.h"
22#include "citra_qt/debugger/graphics/graphics_surface.h"
23#include "citra_qt/debugger/graphics/graphics_tracing.h"
24#include "citra_qt/debugger/graphics/graphics_vertex_shader.h"
25#include "citra_qt/debugger/profiler.h"
26#include "citra_qt/debugger/registers.h"
27#include "citra_qt/debugger/wait_tree.h"
28#include "citra_qt/game_list.h"
29#include "citra_qt/hotkeys.h"
30#include "citra_qt/main.h"
31#include "citra_qt/ui_settings.h"
32#include "common/logging/backend.h" 16#include "common/logging/backend.h"
33#include "common/logging/filter.h" 17#include "common/logging/filter.h"
34#include "common/logging/log.h" 18#include "common/logging/log.h"
@@ -42,6 +26,16 @@
42#include "core/gdbstub/gdbstub.h" 26#include "core/gdbstub/gdbstub.h"
43#include "core/loader/loader.h" 27#include "core/loader/loader.h"
44#include "core/settings.h" 28#include "core/settings.h"
29#include "yuzu/bootmanager.h"
30#include "yuzu/configuration/config.h"
31#include "yuzu/configuration/configure_dialog.h"
32#include "yuzu/debugger/profiler.h"
33#include "yuzu/debugger/registers.h"
34#include "yuzu/debugger/wait_tree.h"
35#include "yuzu/game_list.h"
36#include "yuzu/hotkeys.h"
37#include "yuzu/main.h"
38#include "yuzu/ui_settings.h"
45 39
46#ifdef QT_STATICPLUGIN 40#ifdef QT_STATICPLUGIN
47Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 41Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
@@ -89,7 +83,6 @@ void GMainWindow::ShowCallouts() {
89} 83}
90 84
91GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 85GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
92 Pica::g_debug_context = Pica::DebugContext::Construct();
93 setAcceptDrops(true); 86 setAcceptDrops(true);
94 ui.setupUi(this); 87 ui.setupUi(this);
95 statusBar()->hide(); 88 statusBar()->hide();
@@ -126,8 +119,6 @@ GMainWindow::~GMainWindow() {
126 // will get automatically deleted otherwise 119 // will get automatically deleted otherwise
127 if (render_window->parent() == nullptr) 120 if (render_window->parent() == nullptr)
128 delete render_window; 121 delete render_window;
129
130 Pica::g_debug_context.reset();
131} 122}
132 123
133void GMainWindow::InitializeWidgets() { 124void GMainWindow::InitializeWidgets() {
@@ -188,35 +179,6 @@ void GMainWindow::InitializeDebugWidgets() {
188 connect(this, &GMainWindow::EmulationStopping, registersWidget, 179 connect(this, &GMainWindow::EmulationStopping, registersWidget,
189 &RegistersWidget::OnEmulationStopping); 180 &RegistersWidget::OnEmulationStopping);
190 181
191 graphicsWidget = new GPUCommandStreamWidget(this);
192 addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
193 graphicsWidget->hide();
194 debug_menu->addAction(graphicsWidget->toggleViewAction());
195
196 graphicsCommandsWidget = new GPUCommandListWidget(this);
197 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
198 graphicsCommandsWidget->hide();
199 debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
200
201 graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
202 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
203 graphicsBreakpointsWidget->hide();
204 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
205
206 graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
207 addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
208 graphicsVertexShaderWidget->hide();
209 debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
210
211 graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
212 addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
213 graphicsTracingWidget->hide();
214 debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
215 connect(this, &GMainWindow::EmulationStarting, graphicsTracingWidget,
216 &GraphicsTracingWidget::OnEmulationStarting);
217 connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
218 &GraphicsTracingWidget::OnEmulationStopping);
219
220 waitTreeWidget = new WaitTreeWidget(this); 182 waitTreeWidget = new WaitTreeWidget(this);
221 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); 183 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
222 waitTreeWidget->hide(); 184 waitTreeWidget->hide();
@@ -465,13 +427,6 @@ void GMainWindow::BootGame(const QString& filename) {
465void GMainWindow::ShutdownGame() { 427void GMainWindow::ShutdownGame() {
466 emu_thread->RequestStop(); 428 emu_thread->RequestStop();
467 429
468 // Release emu threads from any breakpoints
469 // This belongs after RequestStop() and before wait() because if emulation stops on a GPU
470 // breakpoint after (or before) RequestStop() is called, the emulation would never be able
471 // to continue out to the main loop and terminate. Thus wait() would hang forever.
472 // TODO(bunnei): This function is not thread safe, but it's being used as if it were
473 Pica::g_debug_context->ClearBreakpoints();
474
475 emit EmulationStopping(); 430 emit EmulationStopping();
476 431
477 // Wait for emulation thread to complete and delete it 432 // Wait for emulation thread to complete and delete it
@@ -661,12 +616,7 @@ void GMainWindow::OnSwapScreens() {
661 Settings::Apply(); 616 Settings::Apply();
662} 617}
663 618
664void GMainWindow::OnCreateGraphicsSurfaceViewer() { 619void GMainWindow::OnCreateGraphicsSurfaceViewer() {}
665 auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this);
666 addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget);
667 // TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
668 graphicsSurfaceViewerWidget->show();
669}
670 620
671void GMainWindow::UpdateStatusBar() { 621void GMainWindow::UpdateStatusBar() {
672 if (emu_thread == nullptr) { 622 if (emu_thread == nullptr) {
diff --git a/src/yuzu/util/spinbox.cpp b/src/yuzu/util/spinbox.cpp
index 212709007..ede6fe0a8 100644
--- a/src/yuzu/util/spinbox.cpp
+++ b/src/yuzu/util/spinbox.cpp
@@ -31,7 +31,7 @@
31#include <cstdlib> 31#include <cstdlib>
32#include <QLineEdit> 32#include <QLineEdit>
33#include <QRegExpValidator> 33#include <QRegExpValidator>
34#include "citra_qt/util/spinbox.h" 34#include "yuzu/util/spinbox.h"
35#include "common/assert.h" 35#include "common/assert.h"
36 36
37CSpinBox::CSpinBox(QWidget* parent) 37CSpinBox::CSpinBox(QWidget* parent)
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index 02be92bbd..91d3f7def 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -4,7 +4,7 @@
4 4
5#include <array> 5#include <array>
6#include <cmath> 6#include <cmath>
7#include "citra_qt/util/util.h" 7#include "yuzu/util/util.h"
8 8
9QFont GetMonospaceFont() { 9QFont GetMonospaceFont() {
10 QFont font("monospace"); 10 QFont font("monospace");