summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-03-25 16:28:24 -0400
committerGravatar GitHub2018-03-25 16:28:24 -0400
commite9315ace9f7f541d251a995ff2d4d3513ddc16c4 (patch)
treeba32de7358ed98e1230c1f522a5c7ba35d7ab19e /src
parentMerge pull request #281 from mailwl/sockets-services (diff)
parentGPU: Make the debug_context variable a member of the frontend instead of a gl... (diff)
downloadyuzu-e9315ace9f7f541d251a995ff2d4d3513ddc16c4.tar.gz
yuzu-e9315ace9f7f541d251a995ff2d4d3513ddc16c4.tar.xz
yuzu-e9315ace9f7f541d251a995ff2d4d3513ddc16c4.zip
Merge pull request #273 from Subv/textures
GPU: Added code to unswizzle textures and ported the surface viewer from citra
Diffstat (limited to 'src')
-rw-r--r--src/core/core.h11
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp64
-rw-r--r--src/video_core/debug_utils/debug_utils.h163
-rw-r--r--src/video_core/engines/maxwell_3d.cpp69
-rw-r--r--src/video_core/engines/maxwell_3d.h16
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/gpu.h9
-rw-r--r--src/video_core/textures/decoders.cpp105
-rw-r--r--src/video_core/textures/decoders.h26
-rw-r--r--src/video_core/textures/texture.h61
-rw-r--r--src/yuzu/CMakeLists.txt7
-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.cpp212
-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_surface.cpp452
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h97
-rw-r--r--src/yuzu/main.cpp18
-rw-r--r--src/yuzu/main.h13
21 files changed, 1464 insertions, 10 deletions
diff --git a/src/core/core.h b/src/core/core.h
index 552c8f5ee..ade456cfc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -13,6 +13,7 @@
13#include "core/memory.h" 13#include "core/memory.h"
14#include "core/perf_stats.h" 14#include "core/perf_stats.h"
15#include "core/telemetry_session.h" 15#include "core/telemetry_session.h"
16#include "video_core/debug_utils/debug_utils.h"
16#include "video_core/gpu.h" 17#include "video_core/gpu.h"
17 18
18class EmuWindow; 19class EmuWindow;
@@ -135,6 +136,14 @@ public:
135 return *app_loader; 136 return *app_loader;
136 } 137 }
137 138
139 void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
140 debug_context = std::move(context);
141 }
142
143 std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
144 return debug_context;
145 }
146
138private: 147private:
139 /** 148 /**
140 * Initialize the emulated system. 149 * Initialize the emulated system.
@@ -154,6 +163,8 @@ private:
154 std::unique_ptr<Kernel::Scheduler> scheduler; 163 std::unique_ptr<Kernel::Scheduler> scheduler;
155 std::unique_ptr<Tegra::GPU> gpu_core; 164 std::unique_ptr<Tegra::GPU> gpu_core;
156 165
166 std::shared_ptr<Tegra::DebugContext> debug_context;
167
157 Kernel::SharedPtr<Kernel::Process> current_process; 168 Kernel::SharedPtr<Kernel::Process> current_process;
158 169
159 /// When true, signals that a reschedule should happen 170 /// When true, signals that a reschedule should happen
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e56253c4c..3dab81769 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,6 +1,8 @@
1add_library(video_core STATIC 1add_library(video_core STATIC
2 command_processor.cpp 2 command_processor.cpp
3 command_processor.h 3 command_processor.h
4 debug_utils/debug_utils.cpp
5 debug_utils/debug_utils.h
4 engines/fermi_2d.cpp 6 engines/fermi_2d.cpp
5 engines/fermi_2d.h 7 engines/fermi_2d.h
6 engines/maxwell_3d.cpp 8 engines/maxwell_3d.cpp
@@ -31,6 +33,9 @@ add_library(video_core STATIC
31 renderer_opengl/gl_stream_buffer.h 33 renderer_opengl/gl_stream_buffer.h
32 renderer_opengl/renderer_opengl.cpp 34 renderer_opengl/renderer_opengl.cpp
33 renderer_opengl/renderer_opengl.h 35 renderer_opengl/renderer_opengl.h
36 textures/decoders.cpp
37 textures/decoders.h
38 textures/texture.h
34 utils.h 39 utils.h
35 video_core.cpp 40 video_core.cpp
36 video_core.h 41 video_core.h
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
new file mode 100644
index 000000000..22d44aab2
--- /dev/null
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -0,0 +1,64 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <condition_variable>
7#include <cstdint>
8#include <cstring>
9#include <fstream>
10#include <map>
11#include <mutex>
12#include <string>
13
14#include "common/assert.h"
15#include "common/bit_field.h"
16#include "common/color.h"
17#include "common/common_types.h"
18#include "common/file_util.h"
19#include "common/logging/log.h"
20#include "common/math_util.h"
21#include "common/vector_math.h"
22#include "video_core/debug_utils/debug_utils.h"
23
24namespace Tegra {
25
26void DebugContext::DoOnEvent(Event event, void* data) {
27 {
28 std::unique_lock<std::mutex> lock(breakpoint_mutex);
29
30 // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
31 // show on debug widgets
32
33 // TODO: Should stop the CPU thread here once we multithread emulation.
34
35 active_breakpoint = event;
36 at_breakpoint = true;
37
38 // Tell all observers that we hit a breakpoint
39 for (auto& breakpoint_observer : breakpoint_observers) {
40 breakpoint_observer->OnMaxwellBreakPointHit(event, data);
41 }
42
43 // Wait until another thread tells us to Resume()
44 resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
45 }
46}
47
48void DebugContext::Resume() {
49 {
50 std::lock_guard<std::mutex> lock(breakpoint_mutex);
51
52 // Tell all observers that we are about to resume
53 for (auto& breakpoint_observer : breakpoint_observers) {
54 breakpoint_observer->OnMaxwellResume();
55 }
56
57 // Resume the waiting thread (i.e. OnEvent())
58 at_breakpoint = false;
59 }
60
61 resume_from_breakpoint.notify_one();
62}
63
64} // namespace Tegra
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
new file mode 100644
index 000000000..bbba8e380
--- /dev/null
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -0,0 +1,163 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9#include <condition_variable>
10#include <iterator>
11#include <list>
12#include <map>
13#include <memory>
14#include <mutex>
15#include <string>
16#include <utility>
17#include <vector>
18#include "common/common_types.h"
19#include "common/vector_math.h"
20
21namespace Tegra {
22
23class DebugContext {
24public:
25 enum class Event {
26 FirstEvent = 0,
27
28 MaxwellCommandLoaded = FirstEvent,
29 MaxwellCommandProcessed,
30 IncomingPrimitiveBatch,
31 FinishedPrimitiveBatch,
32
33 NumEvents
34 };
35
36 /**
37 * Inherit from this class to be notified of events registered to some debug context.
38 * Most importantly this is used for our debugger GUI.
39 *
40 * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
41 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
42 * access
43 * @todo Evaluate an alternative interface, in which there is only one managing observer and
44 * multiple child observers running (by design) on the same thread.
45 */
46 class BreakPointObserver {
47 public:
48 /// Constructs the object such that it observes events of the given DebugContext.
49 BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
50 : context_weak(debug_context) {
51 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
52 debug_context->breakpoint_observers.push_back(this);
53 }
54
55 virtual ~BreakPointObserver() {
56 auto context = context_weak.lock();
57 if (context) {
58 std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
59 context->breakpoint_observers.remove(this);
60
61 // If we are the last observer to be destroyed, tell the debugger context that
62 // it is free to continue. In particular, this is required for a proper yuzu
63 // shutdown, when the emulation thread is waiting at a breakpoint.
64 if (context->breakpoint_observers.empty())
65 context->Resume();
66 }
67 }
68
69 /**
70 * Action to perform when a breakpoint was reached.
71 * @param event Type of event which triggered the breakpoint
72 * @param data Optional data pointer (if unused, this is a nullptr)
73 * @note This function will perform nothing unless it is overridden in the child class.
74 */
75 virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
76
77 /**
78 * Action to perform when emulation is resumed from a breakpoint.
79 * @note This function will perform nothing unless it is overridden in the child class.
80 */
81 virtual void OnMaxwellResume() {}
82
83 protected:
84 /**
85 * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
86 * context_weak.lock(), always compare the result against nullptr.
87 */
88 std::weak_ptr<DebugContext> context_weak;
89 };
90
91 /**
92 * Simple structure defining a breakpoint state
93 */
94 struct BreakPoint {
95 bool enabled = false;
96 };
97
98 /**
99 * Static constructor used to create a shared_ptr of a DebugContext.
100 */
101 static std::shared_ptr<DebugContext> Construct() {
102 return std::shared_ptr<DebugContext>(new DebugContext);
103 }
104
105 /**
106 * Used by the emulation core when a given event has happened. If a breakpoint has been set
107 * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
108 * The current thread then is halted until Resume() is called from another thread (or until
109 * emulation is stopped).
110 * @param event Event which has happened
111 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
112 * Resume() is called.
113 */
114 void OnEvent(Event event, void* data) {
115 // This check is left in the header to allow the compiler to inline it.
116 if (!breakpoints[(int)event].enabled)
117 return;
118 // For the rest of event handling, call a separate function.
119 DoOnEvent(event, data);
120 }
121
122 void DoOnEvent(Event event, void* data);
123
124 /**
125 * Resume from the current breakpoint.
126 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
127 * Calling from any other thread is safe.
128 */
129 void Resume();
130
131 /**
132 * Delete all set breakpoints and resume emulation.
133 */
134 void ClearBreakpoints() {
135 for (auto& bp : breakpoints) {
136 bp.enabled = false;
137 }
138 Resume();
139 }
140
141 // TODO: Evaluate if access to these members should be hidden behind a public interface.
142 std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
143 Event active_breakpoint;
144 bool at_breakpoint = false;
145
146private:
147 /**
148 * Private default constructor to make sure people always construct this through Construct()
149 * instead.
150 */
151 DebugContext() = default;
152
153 /// Mutex protecting current breakpoint state and the observer list.
154 std::mutex breakpoint_mutex;
155
156 /// Used by OnEvent to wait for resumption.
157 std::condition_variable resume_from_breakpoint;
158
159 /// List of registered observers
160 std::list<BreakPointObserver*> breakpoint_observers;
161};
162
163} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 4d9745e48..986165c6d 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -2,8 +2,13 @@
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 <cinttypes>
5#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h"
8#include "video_core/debug_utils/debug_utils.h"
6#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/textures/decoders.h"
11#include "video_core/textures/texture.h"
7 12
8namespace Tegra { 13namespace Tegra {
9namespace Engines { 14namespace Engines {
@@ -46,6 +51,8 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
46 ASSERT_MSG(method < Regs::NUM_REGS, 51 ASSERT_MSG(method < Regs::NUM_REGS,
47 "Invalid Maxwell3D register, increase the size of the Regs structure"); 52 "Invalid Maxwell3D register, increase the size of the Regs structure");
48 53
54 auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
55
49 // It is an error to write to a register other than the current macro's ARG register before it 56 // It is an error to write to a register other than the current macro's ARG register before it
50 // has finished execution. 57 // has finished execution.
51 if (executing_macro != 0) { 58 if (executing_macro != 0) {
@@ -72,6 +79,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
72 return; 79 return;
73 } 80 }
74 81
82 if (debug_context) {
83 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
84 }
85
75 regs.reg_array[method] = value; 86 regs.reg_array[method] = value;
76 87
77#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32)) 88#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
@@ -137,6 +148,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
137 } 148 }
138 149
139#undef MAXWELL3D_REG_INDEX 150#undef MAXWELL3D_REG_INDEX
151
152 if (debug_context) {
153 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
154 }
140} 155}
141 156
142void Maxwell3D::ProcessQueryGet() { 157void Maxwell3D::ProcessQueryGet() {
@@ -160,6 +175,15 @@ void Maxwell3D::ProcessQueryGet() {
160 175
161void Maxwell3D::DrawArrays() { 176void Maxwell3D::DrawArrays() {
162 LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring"); 177 LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
178 auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
179
180 if (debug_context) {
181 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
182 }
183
184 if (debug_context) {
185 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
186 }
163} 187}
164 188
165void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) { 189void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
@@ -270,5 +294,50 @@ void Maxwell3D::ProcessCBData(u32 value) {
270 regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4; 294 regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
271} 295}
272 296
297std::vector<Texture::TICEntry> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) {
298 std::vector<Texture::TICEntry> textures;
299
300 auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)];
301 auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index];
302 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
303
304 GPUVAddr tic_base_address = regs.tic.TICAddress();
305
306 GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size;
307
308 // Offset into the texture constbuffer where the texture info begins.
309 static constexpr size_t TextureInfoOffset = 0x20;
310
311 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
312 current_texture < tex_info_buffer_end; current_texture += 4) {
313
314 Texture::TextureHandle tex_info{
315 Memory::Read32(memory_manager.PhysicalToVirtualAddress(current_texture))};
316
317 if (tex_info.tic_id != 0 || tex_info.tsc_id != 0) {
318 GPUVAddr tic_address_gpu =
319 tic_base_address + tex_info.tic_id * sizeof(Texture::TICEntry);
320 VAddr tic_address_cpu = memory_manager.PhysicalToVirtualAddress(tic_address_gpu);
321
322 Texture::TICEntry tic_entry;
323 Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
324
325 auto r_type = tic_entry.r_type.Value();
326 auto g_type = tic_entry.g_type.Value();
327 auto b_type = tic_entry.b_type.Value();
328 auto a_type = tic_entry.a_type.Value();
329
330 // TODO(Subv): Different data types for separate components are not supported
331 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
332
333 auto format = tic_entry.format.Value();
334
335 textures.push_back(tic_entry);
336 }
337 }
338
339 return textures;
340}
341
273} // namespace Engines 342} // namespace Engines
274} // namespace Tegra 343} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 545d7ff35..441cc0c19 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -12,6 +12,7 @@
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
15#include "video_core/textures/texture.h"
15 16
16namespace Tegra { 17namespace Tegra {
17namespace Engines { 18namespace Engines {
@@ -21,12 +22,6 @@ public:
21 explicit Maxwell3D(MemoryManager& memory_manager); 22 explicit Maxwell3D(MemoryManager& memory_manager);
22 ~Maxwell3D() = default; 23 ~Maxwell3D() = default;
23 24
24 /// Write the value to the register identified by method.
25 void WriteReg(u32 method, u32 value, u32 remaining_params);
26
27 /// Uploads the code for a GPU macro program associated with the specified entry.
28 void SubmitMacroCode(u32 entry, std::vector<u32> code);
29
30 /// Register structure of the Maxwell3D engine. 25 /// Register structure of the Maxwell3D engine.
31 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. 26 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
32 struct Regs { 27 struct Regs {
@@ -430,6 +425,15 @@ public:
430 425
431 State state{}; 426 State state{};
432 427
428 /// Write the value to the register identified by method.
429 void WriteReg(u32 method, u32 value, u32 remaining_params);
430
431 /// Uploads the code for a GPU macro program associated with the specified entry.
432 void SubmitMacroCode(u32 entry, std::vector<u32> code);
433
434 /// Returns a list of enabled textures for the specified shader stage.
435 std::vector<Texture::TICEntry> GetStageTextures(Regs::ShaderStage stage);
436
433private: 437private:
434 MemoryManager& memory_manager; 438 MemoryManager& memory_manager;
435 439
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index c384d236e..9463cd5d6 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -18,4 +18,8 @@ GPU::GPU() {
18 18
19GPU::~GPU() = default; 19GPU::~GPU() = default;
20 20
21const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
22 return *maxwell_3d;
23}
24
21} // namespace Tegra 25} // namespace Tegra
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 206b3e05e..8183b12e9 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -13,6 +13,12 @@
13 13
14namespace Tegra { 14namespace Tegra {
15 15
16enum class RenderTargetFormat {
17 RGBA8_UNORM = 0xD5,
18};
19
20class DebugContext;
21
16/** 22/**
17 * Struct describing framebuffer configuration 23 * Struct describing framebuffer configuration
18 */ 24 */
@@ -66,6 +72,9 @@ public:
66 /// Processes a command list stored at the specified address in GPU memory. 72 /// Processes a command list stored at the specified address in GPU memory.
67 void ProcessCommandList(GPUVAddr address, u32 size); 73 void ProcessCommandList(GPUVAddr address, u32 size);
68 74
75 /// Returns a reference to the Maxwell3D GPU engine.
76 const Engines::Maxwell3D& Get3DEngine() const;
77
69 std::unique_ptr<MemoryManager> memory_manager; 78 std::unique_ptr<MemoryManager> memory_manager;
70 79
71 Engines::Maxwell3D& Maxwell3D() { 80 Engines::Maxwell3D& Maxwell3D() {
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
new file mode 100644
index 000000000..2e87281eb
--- /dev/null
+++ b/src/video_core/textures/decoders.cpp
@@ -0,0 +1,105 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/assert.h"
7#include "video_core/textures/decoders.h"
8#include "video_core/textures/texture.h"
9
10namespace Tegra {
11namespace Texture {
12
13/**
14 * Calculates the offset of an (x, y) position within a swizzled texture.
15 * Taken from the Tegra X1 TRM.
16 */
17static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) {
18 u32 image_width_in_gobs = image_width * bytes_per_pixel / 64;
19 u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
20 (x * bytes_per_pixel / 64) * 512 * block_height +
21 (y % (8 * block_height) / 8) * 512;
22 x *= bytes_per_pixel;
23 u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
24 (y % 2) * 16 + (x % 16);
25
26 return address;
27}
28
29static void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
30 u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
31 u32 block_height) {
32 u8* data_ptrs[2];
33 for (unsigned y = 0; y < height; ++y) {
34 for (unsigned x = 0; x < width; ++x) {
35 u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height);
36 u32 pixel_index = (x + y * width) * out_bytes_per_pixel;
37
38 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
39 data_ptrs[!unswizzle] = &unswizzled_data[pixel_index];
40
41 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
42 }
43 }
44}
45
46u32 BytesPerPixel(TextureFormat format) {
47 switch (format) {
48 case TextureFormat::DXT1:
49 // In this case a 'pixel' actually refers to a 4x4 tile.
50 return 8;
51 case TextureFormat::A8R8G8B8:
52 return 4;
53 default:
54 UNIMPLEMENTED_MSG("Format not implemented");
55 break;
56 }
57}
58
59std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height) {
60 u8* data = Memory::GetPointer(address);
61 u32 bytes_per_pixel = BytesPerPixel(format);
62
63 static constexpr u32 DefaultBlockHeight = 16;
64
65 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
66
67 switch (format) {
68 case TextureFormat::DXT1:
69 // In the DXT1 format, each 4x4 tile is swizzled instead of just individual pixel values.
70 CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
71 unswizzled_data.data(), true, DefaultBlockHeight);
72 break;
73 case TextureFormat::A8R8G8B8:
74 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
75 unswizzled_data.data(), true, DefaultBlockHeight);
76 break;
77 default:
78 UNIMPLEMENTED_MSG("Format not implemented");
79 break;
80 }
81
82 return unswizzled_data;
83}
84
85std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
86 u32 height) {
87 std::vector<u8> rgba_data;
88
89 // TODO(Subv): Implement.
90 switch (format) {
91 case TextureFormat::DXT1:
92 case TextureFormat::A8R8G8B8:
93 // TODO(Subv): For the time being just forward the same data without any decoding.
94 rgba_data = texture_data;
95 break;
96 default:
97 UNIMPLEMENTED_MSG("Format not implemented");
98 break;
99 }
100
101 return rgba_data;
102}
103
104} // namespace Texture
105} // namespace Tegra
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
new file mode 100644
index 000000000..0c21694ff
--- /dev/null
+++ b/src/video_core/textures/decoders.h
@@ -0,0 +1,26 @@
1// Copyright 2018 yuzu 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 <vector>
8#include "common/common_types.h"
9#include "video_core/textures/texture.h"
10
11namespace Tegra {
12namespace Texture {
13
14/**
15 * Unswizzles a swizzled texture without changing its format.
16 */
17std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height);
18
19/**
20 * Decodes an unswizzled texture into a A8R8G8B8 texture.
21 */
22std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
23 u32 height);
24
25} // namespace Texture
26} // namespace Tegra
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
new file mode 100644
index 000000000..d969bcdd9
--- /dev/null
+++ b/src/video_core/textures/texture.h
@@ -0,0 +1,61 @@
1// Copyright 2018 yuzu 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 "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "video_core/memory_manager.h"
11
12namespace Tegra {
13namespace Texture {
14
15enum class TextureFormat : u32 {
16 A8R8G8B8 = 8,
17 DXT1 = 0x24,
18};
19
20union TextureHandle {
21 u32 raw;
22 BitField<0, 20, u32> tic_id;
23 BitField<20, 12, u32> tsc_id;
24};
25
26struct TICEntry {
27 union {
28 u32 raw;
29 BitField<0, 7, TextureFormat> format;
30 BitField<7, 3, u32> r_type;
31 BitField<10, 3, u32> g_type;
32 BitField<13, 3, u32> b_type;
33 BitField<16, 3, u32> a_type;
34 };
35 u32 address_low;
36 u16 address_high;
37 INSERT_PADDING_BYTES(6);
38 u16 width_minus_1;
39 INSERT_PADDING_BYTES(2);
40 u16 height_minus_1;
41 INSERT_PADDING_BYTES(10);
42
43 GPUVAddr Address() const {
44 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
45 }
46
47 u32 Width() const {
48 return width_minus_1 + 1;
49 }
50
51 u32 Height() const {
52 return height_minus_1 + 1;
53 }
54};
55static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
56
57/// Returns the number of bytes per pixel of the input texture format.
58u32 BytesPerPixel(TextureFormat format);
59
60} // namespace Texture
61} // namespace Tegra
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 0c4056c49..5af3154d7 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -23,6 +23,13 @@ add_executable(yuzu
23 configuration/configure_input.h 23 configuration/configure_input.h
24 configuration/configure_system.cpp 24 configuration/configure_system.cpp
25 configuration/configure_system.h 25 configuration/configure_system.h
26 debugger/graphics/graphics_breakpoint_observer.cpp
27 debugger/graphics/graphics_breakpoint_observer.h
28 debugger/graphics/graphics_breakpoints.cpp
29 debugger/graphics/graphics_breakpoints.h
30 debugger/graphics/graphics_breakpoints_p.h
31 debugger/graphics/graphics_surface.cpp
32 debugger/graphics/graphics_surface.h
26 debugger/profiler.cpp 33 debugger/profiler.cpp
27 debugger/profiler.h 34 debugger/profiler.h
28 debugger/registers.cpp 35 debugger/registers.cpp
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
new file mode 100644
index 000000000..d6d61a739
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
@@ -0,0 +1,27 @@
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 "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
7
8BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context,
9 const QString& title, QWidget* parent)
10 : QDockWidget(title, parent), BreakPointObserver(debug_context) {
11 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::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(Tegra::DebugContext::Event, void*)), this,
18 SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
19}
20
21void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) {
22 emit BreakPointHit(event, data);
23}
24
25void BreakPointObserverDock::OnMaxwellResume() {
26 emit Resumed();
27}
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
new file mode 100644
index 000000000..9d05493cf
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
@@ -0,0 +1,33 @@
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 OnMaxwellBreakPointHit and OnMaxwellResume to public slots.
12 * This is because the Maxwell 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 Tegra::DebugContext::BreakPointObserver {
17 Q_OBJECT
18
19public:
20 BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context, const QString& title,
21 QWidget* parent = nullptr);
22
23 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
24 void OnMaxwellResume() override;
25
26private slots:
27 virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
28 virtual void OnResumed() = 0;
29
30signals:
31 void Resumed();
32 void BreakPointHit(Tegra::DebugContext::Event event, void* data);
33};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
new file mode 100644
index 000000000..f98cc8152
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -0,0 +1,212 @@
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 "common/assert.h"
11#include "yuzu/debugger/graphics/graphics_breakpoints.h"
12#include "yuzu/debugger/graphics/graphics_breakpoints_p.h"
13
14BreakPointModel::BreakPointModel(std::shared_ptr<Tegra::DebugContext> debug_context,
15 QObject* parent)
16 : QAbstractListModel(parent), context_weak(debug_context),
17 at_breakpoint(debug_context->at_breakpoint),
18 active_breakpoint(debug_context->active_breakpoint) {}
19
20int BreakPointModel::columnCount(const QModelIndex& parent) const {
21 return 1;
22}
23
24int BreakPointModel::rowCount(const QModelIndex& parent) const {
25 return static_cast<int>(Tegra::DebugContext::Event::NumEvents);
26}
27
28QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
29 const auto event = static_cast<Tegra::DebugContext::Event>(index.row());
30
31 switch (role) {
32 case Qt::DisplayRole: {
33 if (index.column() == 0) {
34 static const std::map<Tegra::DebugContext::Event, QString> map = {
35 {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")},
36 {Tegra::DebugContext::Event::MaxwellCommandProcessed,
37 tr("Maxwell command processed")},
38 {Tegra::DebugContext::Event::IncomingPrimitiveBatch,
39 tr("Incoming primitive batch")},
40 {Tegra::DebugContext::Event::FinishedPrimitiveBatch,
41 tr("Finished primitive batch")},
42 };
43
44 DEBUG_ASSERT(map.size() == static_cast<size_t>(Tegra::DebugContext::Event::NumEvents));
45 return (map.find(event) != map.end()) ? map.at(event) : QString();
46 }
47
48 break;
49 }
50
51 case Qt::CheckStateRole: {
52 if (index.column() == 0)
53 return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
54 break;
55 }
56
57 case Qt::BackgroundRole: {
58 if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
59 return QBrush(QColor(0xE0, 0xE0, 0x10));
60 }
61 break;
62 }
63
64 case Role_IsEnabled: {
65 auto context = context_weak.lock();
66 return context && context->breakpoints[(int)event].enabled;
67 }
68
69 default:
70 break;
71 }
72 return QVariant();
73}
74
75Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
76 if (!index.isValid())
77 return 0;
78
79 Qt::ItemFlags flags = Qt::ItemIsEnabled;
80 if (index.column() == 0)
81 flags |= Qt::ItemIsUserCheckable;
82 return flags;
83}
84
85bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
86 const auto event = static_cast<Tegra::DebugContext::Event>(index.row());
87
88 switch (role) {
89 case Qt::CheckStateRole: {
90 if (index.column() != 0)
91 return false;
92
93 auto context = context_weak.lock();
94 if (!context)
95 return false;
96
97 context->breakpoints[(int)event].enabled = value == Qt::Checked;
98 QModelIndex changed_index = createIndex(index.row(), 0);
99 emit dataChanged(changed_index, changed_index);
100 return true;
101 }
102 }
103
104 return false;
105}
106
107void BreakPointModel::OnBreakPointHit(Tegra::DebugContext::Event event) {
108 auto context = context_weak.lock();
109 if (!context)
110 return;
111
112 active_breakpoint = context->active_breakpoint;
113 at_breakpoint = context->at_breakpoint;
114 emit dataChanged(createIndex(static_cast<int>(event), 0),
115 createIndex(static_cast<int>(event), 0));
116}
117
118void BreakPointModel::OnResumed() {
119 auto context = context_weak.lock();
120 if (!context)
121 return;
122
123 at_breakpoint = context->at_breakpoint;
124 emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
125 createIndex(static_cast<int>(active_breakpoint), 0));
126 active_breakpoint = context->active_breakpoint;
127}
128
129GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
130 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
131 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
132 debug_context) {
133 setObjectName("TegraBreakPointsWidget");
134
135 status_text = new QLabel(tr("Emulation running"));
136 resume_button = new QPushButton(tr("Resume"));
137 resume_button->setEnabled(false);
138
139 breakpoint_model = new BreakPointModel(debug_context, this);
140 breakpoint_list = new QTreeView;
141 breakpoint_list->setRootIsDecorated(false);
142 breakpoint_list->setHeaderHidden(true);
143 breakpoint_list->setModel(breakpoint_model);
144
145 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
146
147 connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this,
148 SLOT(OnItemDoubleClicked(const QModelIndex&)));
149
150 connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
151
152 connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this,
153 SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection);
154 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
155
156 connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), breakpoint_model,
157 SLOT(OnBreakPointHit(Tegra::DebugContext::Event)), Qt::BlockingQueuedConnection);
158 connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
159
160 connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)),
161 breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
162
163 QWidget* main_widget = new QWidget;
164 auto main_layout = new QVBoxLayout;
165 {
166 auto sub_layout = new QHBoxLayout;
167 sub_layout->addWidget(status_text);
168 sub_layout->addWidget(resume_button);
169 main_layout->addLayout(sub_layout);
170 }
171 main_layout->addWidget(breakpoint_list);
172 main_widget->setLayout(main_layout);
173
174 setWidget(main_widget);
175}
176
177void GraphicsBreakPointsWidget::OnMaxwellBreakPointHit(Event event, void* data) {
178 // Process in GUI thread
179 emit BreakPointHit(event, data);
180}
181
182void GraphicsBreakPointsWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
183 status_text->setText(tr("Emulation halted at breakpoint"));
184 resume_button->setEnabled(true);
185}
186
187void GraphicsBreakPointsWidget::OnMaxwellResume() {
188 // Process in GUI thread
189 emit Resumed();
190}
191
192void GraphicsBreakPointsWidget::OnResumed() {
193 status_text->setText(tr("Emulation running"));
194 resume_button->setEnabled(false);
195}
196
197void GraphicsBreakPointsWidget::OnResumeRequested() {
198 if (auto context = context_weak.lock())
199 context->Resume();
200}
201
202void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
203 if (!index.isValid())
204 return;
205
206 QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0);
207 QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole);
208 QVariant new_state = Qt::Unchecked;
209 if (enabled == Qt::Unchecked)
210 new_state = Qt::Checked;
211 breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole);
212}
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h
new file mode 100644
index 000000000..ae0ede2e8
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.h
@@ -0,0 +1,46 @@
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, Tegra::DebugContext::BreakPointObserver {
18 Q_OBJECT
19
20 using Event = Tegra::DebugContext::Event;
21
22public:
23 explicit GraphicsBreakPointsWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
24 QWidget* parent = nullptr);
25
26 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
27 void OnMaxwellResume() override;
28
29public slots:
30 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
31 void OnItemDoubleClicked(const QModelIndex&);
32 void OnResumeRequested();
33 void OnResumed();
34
35signals:
36 void Resumed();
37 void BreakPointHit(Tegra::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
new file mode 100644
index 000000000..35a6876ae
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
@@ -0,0 +1,36 @@
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<Tegra::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(Tegra::DebugContext::Event event);
30 void OnResumed();
31
32private:
33 std::weak_ptr<Tegra::DebugContext> context_weak;
34 bool at_breakpoint;
35 Tegra::DebugContext::Event active_breakpoint;
36};
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
new file mode 100644
index 000000000..8e6509adc
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -0,0 +1,452 @@
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 "core/core.h"
15#include "video_core/engines/maxwell_3d.h"
16#include "video_core/gpu.h"
17#include "video_core/textures/decoders.h"
18#include "video_core/textures/texture.h"
19#include "video_core/utils.h"
20#include "yuzu/debugger/graphics/graphics_surface.h"
21#include "yuzu/util/spinbox.h"
22
23static Tegra::Texture::TextureFormat ConvertToTextureFormat(
24 Tegra::RenderTargetFormat render_target_format) {
25 switch (render_target_format) {
26 case Tegra::RenderTargetFormat::RGBA8_UNORM:
27 return Tegra::Texture::TextureFormat::A8R8G8B8;
28 default:
29 UNIMPLEMENTED_MSG("Unimplemented RT format");
30 }
31}
32
33SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
34 : QLabel(parent), surface_widget(surface_widget_) {}
35SurfacePicture::~SurfacePicture() {}
36
37void SurfacePicture::mousePressEvent(QMouseEvent* event) {
38 // Only do something while the left mouse button is held down
39 if (!(event->buttons() & Qt::LeftButton))
40 return;
41
42 if (pixmap() == nullptr)
43 return;
44
45 if (surface_widget)
46 surface_widget->Pick(event->x() * pixmap()->width() / width(),
47 event->y() * pixmap()->height() / height());
48}
49
50void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
51 // We also want to handle the event if the user moves the mouse while holding down the LMB
52 mousePressEvent(event);
53}
54
55GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
56 QWidget* parent)
57 : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent),
58 surface_source(Source::RenderTarget0) {
59 setObjectName("MaxwellSurface");
60
61 surface_source_list = new QComboBox;
62 surface_source_list->addItem(tr("Render Target 0"));
63 surface_source_list->addItem(tr("Render Target 1"));
64 surface_source_list->addItem(tr("Render Target 2"));
65 surface_source_list->addItem(tr("Render Target 3"));
66 surface_source_list->addItem(tr("Render Target 4"));
67 surface_source_list->addItem(tr("Render Target 5"));
68 surface_source_list->addItem(tr("Render Target 6"));
69 surface_source_list->addItem(tr("Render Target 7"));
70 surface_source_list->addItem(tr("Z Buffer"));
71 surface_source_list->addItem(tr("Custom"));
72 surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
73
74 surface_address_control = new CSpinBox;
75 surface_address_control->SetBase(16);
76 surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF);
77 surface_address_control->SetPrefix("0x");
78
79 unsigned max_dimension = 16384; // TODO: Find actual maximum
80
81 surface_width_control = new QSpinBox;
82 surface_width_control->setRange(0, max_dimension);
83
84 surface_height_control = new QSpinBox;
85 surface_height_control->setRange(0, max_dimension);
86
87 surface_picker_x_control = new QSpinBox;
88 surface_picker_x_control->setRange(0, max_dimension - 1);
89
90 surface_picker_y_control = new QSpinBox;
91 surface_picker_y_control->setRange(0, max_dimension - 1);
92
93 surface_format_control = new QComboBox;
94
95 // Color formats sorted by Maxwell texture format index
96 surface_format_control->addItem(tr("None"));
97 surface_format_control->addItem(tr("Unknown"));
98 surface_format_control->addItem(tr("Unknown"));
99 surface_format_control->addItem(tr("Unknown"));
100 surface_format_control->addItem(tr("Unknown"));
101 surface_format_control->addItem(tr("Unknown"));
102 surface_format_control->addItem(tr("Unknown"));
103 surface_format_control->addItem(tr("Unknown"));
104 surface_format_control->addItem(tr("A8R8G8B8"));
105 surface_format_control->addItem(tr("Unknown"));
106 surface_format_control->addItem(tr("Unknown"));
107 surface_format_control->addItem(tr("Unknown"));
108 surface_format_control->addItem(tr("Unknown"));
109 surface_format_control->addItem(tr("Unknown"));
110 surface_format_control->addItem(tr("Unknown"));
111 surface_format_control->addItem(tr("Unknown"));
112 surface_format_control->addItem(tr("Unknown"));
113 surface_format_control->addItem(tr("Unknown"));
114 surface_format_control->addItem(tr("Unknown"));
115 surface_format_control->addItem(tr("Unknown"));
116 surface_format_control->addItem(tr("Unknown"));
117 surface_format_control->addItem(tr("Unknown"));
118 surface_format_control->addItem(tr("Unknown"));
119 surface_format_control->addItem(tr("Unknown"));
120 surface_format_control->addItem(tr("Unknown"));
121 surface_format_control->addItem(tr("Unknown"));
122 surface_format_control->addItem(tr("Unknown"));
123 surface_format_control->addItem(tr("Unknown"));
124 surface_format_control->addItem(tr("Unknown"));
125 surface_format_control->addItem(tr("Unknown"));
126 surface_format_control->addItem(tr("Unknown"));
127 surface_format_control->addItem(tr("Unknown"));
128 surface_format_control->addItem(tr("Unknown"));
129 surface_format_control->addItem(tr("Unknown"));
130 surface_format_control->addItem(tr("Unknown"));
131 surface_format_control->addItem(tr("Unknown"));
132 surface_format_control->addItem(tr("DXT1"));
133 surface_format_control->addItem(tr("DXT23"));
134 surface_format_control->addItem(tr("DXT45"));
135 surface_format_control->addItem(tr("DXN1"));
136 surface_format_control->addItem(tr("DXN2"));
137
138 surface_info_label = new QLabel();
139 surface_info_label->setWordWrap(true);
140
141 surface_picture_label = new SurfacePicture(0, this);
142 surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
143 surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
144 surface_picture_label->setScaledContents(false);
145
146 auto scroll_area = new QScrollArea();
147 scroll_area->setBackgroundRole(QPalette::Dark);
148 scroll_area->setWidgetResizable(false);
149 scroll_area->setWidget(surface_picture_label);
150
151 save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
152
153 // Connections
154 connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
155 connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this,
156 SLOT(OnSurfaceSourceChanged(int)));
157 connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this,
158 SLOT(OnSurfaceAddressChanged(qint64)));
159 connect(surface_width_control, SIGNAL(valueChanged(int)), this,
160 SLOT(OnSurfaceWidthChanged(int)));
161 connect(surface_height_control, SIGNAL(valueChanged(int)), this,
162 SLOT(OnSurfaceHeightChanged(int)));
163 connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this,
164 SLOT(OnSurfaceFormatChanged(int)));
165 connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this,
166 SLOT(OnSurfacePickerXChanged(int)));
167 connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this,
168 SLOT(OnSurfacePickerYChanged(int)));
169 connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface()));
170
171 auto main_widget = new QWidget;
172 auto main_layout = new QVBoxLayout;
173 {
174 auto sub_layout = new QHBoxLayout;
175 sub_layout->addWidget(new QLabel(tr("Source:")));
176 sub_layout->addWidget(surface_source_list);
177 main_layout->addLayout(sub_layout);
178 }
179 {
180 auto sub_layout = new QHBoxLayout;
181 sub_layout->addWidget(new QLabel(tr("GPU Address:")));
182 sub_layout->addWidget(surface_address_control);
183 main_layout->addLayout(sub_layout);
184 }
185 {
186 auto sub_layout = new QHBoxLayout;
187 sub_layout->addWidget(new QLabel(tr("Width:")));
188 sub_layout->addWidget(surface_width_control);
189 main_layout->addLayout(sub_layout);
190 }
191 {
192 auto sub_layout = new QHBoxLayout;
193 sub_layout->addWidget(new QLabel(tr("Height:")));
194 sub_layout->addWidget(surface_height_control);
195 main_layout->addLayout(sub_layout);
196 }
197 {
198 auto sub_layout = new QHBoxLayout;
199 sub_layout->addWidget(new QLabel(tr("Format:")));
200 sub_layout->addWidget(surface_format_control);
201 main_layout->addLayout(sub_layout);
202 }
203 main_layout->addWidget(scroll_area);
204
205 auto info_layout = new QHBoxLayout;
206 {
207 auto xy_layout = new QVBoxLayout;
208 {
209 {
210 auto sub_layout = new QHBoxLayout;
211 sub_layout->addWidget(new QLabel(tr("X:")));
212 sub_layout->addWidget(surface_picker_x_control);
213 xy_layout->addLayout(sub_layout);
214 }
215 {
216 auto sub_layout = new QHBoxLayout;
217 sub_layout->addWidget(new QLabel(tr("Y:")));
218 sub_layout->addWidget(surface_picker_y_control);
219 xy_layout->addLayout(sub_layout);
220 }
221 }
222 info_layout->addLayout(xy_layout);
223 surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
224 info_layout->addWidget(surface_info_label);
225 }
226 main_layout->addLayout(info_layout);
227
228 main_layout->addWidget(save_surface);
229 main_widget->setLayout(main_layout);
230 setWidget(main_widget);
231
232 // Load current data - TODO: Make sure this works when emulation is not running
233 if (debug_context && debug_context->at_breakpoint) {
234 emit Update();
235 widget()->setEnabled(debug_context->at_breakpoint);
236 } else {
237 widget()->setEnabled(false);
238 }
239}
240
241void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
242 emit Update();
243 widget()->setEnabled(true);
244}
245
246void GraphicsSurfaceWidget::OnResumed() {
247 widget()->setEnabled(false);
248}
249
250void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
251 surface_source = static_cast<Source>(new_value);
252 emit Update();
253}
254
255void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
256 if (surface_address != new_value) {
257 surface_address = static_cast<Tegra::GPUVAddr>(new_value);
258
259 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
260 emit Update();
261 }
262}
263
264void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
265 if (surface_width != static_cast<unsigned>(new_value)) {
266 surface_width = static_cast<unsigned>(new_value);
267
268 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
269 emit Update();
270 }
271}
272
273void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
274 if (surface_height != static_cast<unsigned>(new_value)) {
275 surface_height = static_cast<unsigned>(new_value);
276
277 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
278 emit Update();
279 }
280}
281
282void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
283 if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) {
284 surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value);
285
286 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
287 emit Update();
288 }
289}
290
291void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
292 if (surface_picker_x != new_value) {
293 surface_picker_x = new_value;
294 Pick(surface_picker_x, surface_picker_y);
295 }
296}
297
298void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
299 if (surface_picker_y != new_value) {
300 surface_picker_y = new_value;
301 Pick(surface_picker_x, surface_picker_y);
302 }
303}
304
305void GraphicsSurfaceWidget::Pick(int x, int y) {
306 surface_picker_x_control->setValue(x);
307 surface_picker_y_control->setValue(y);
308
309 if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
310 y >= static_cast<int>(surface_height)) {
311 surface_info_label->setText(tr("Pixel out of bounds"));
312 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
313 return;
314 }
315
316 surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>"));
317 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
318}
319
320void GraphicsSurfaceWidget::OnUpdate() {
321 auto& gpu = Core::System::GetInstance().GPU();
322
323 QPixmap pixmap;
324
325 switch (surface_source) {
326 case Source::RenderTarget0:
327 case Source::RenderTarget1:
328 case Source::RenderTarget2:
329 case Source::RenderTarget3:
330 case Source::RenderTarget4:
331 case Source::RenderTarget5:
332 case Source::RenderTarget6:
333 case Source::RenderTarget7: {
334 // TODO: Store a reference to the registers in the debug context instead of accessing them
335 // directly...
336
337 auto& registers = gpu.Get3DEngine().regs;
338 auto& rt = registers.rt[static_cast<size_t>(surface_source) -
339 static_cast<size_t>(Source::RenderTarget0)];
340
341 surface_address = rt.Address();
342 surface_width = rt.horiz;
343 surface_height = rt.vert;
344 if (rt.format != 0) {
345 surface_format =
346 ConvertToTextureFormat(static_cast<Tegra::RenderTargetFormat>(rt.format));
347 }
348
349 break;
350 }
351
352 case Source::Custom: {
353 // Keep user-specified values
354 break;
355 }
356
357 default:
358 qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
359 break;
360 }
361
362 surface_address_control->SetValue(surface_address);
363 surface_width_control->setValue(surface_width);
364 surface_height_control->setValue(surface_height);
365 surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
366
367 if (surface_address == 0) {
368 surface_picture_label->hide();
369 surface_info_label->setText(tr("(invalid surface address)"));
370 surface_info_label->setAlignment(Qt::AlignCenter);
371 surface_picker_x_control->setEnabled(false);
372 surface_picker_y_control->setEnabled(false);
373 save_surface->setEnabled(false);
374 return;
375 }
376
377 // TODO: Implement a good way to visualize alpha components!
378
379 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
380 VAddr address = gpu.memory_manager->PhysicalToVirtualAddress(surface_address);
381
382 auto unswizzled_data =
383 Tegra::Texture::UnswizzleTexture(address, surface_format, surface_width, surface_height);
384
385 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
386 surface_width, surface_height);
387
388 surface_picture_label->show();
389
390 for (unsigned int y = 0; y < surface_height; ++y) {
391 for (unsigned int x = 0; x < surface_width; ++x) {
392 Math::Vec4<u8> color;
393 color[0] = texture_data[x + y * surface_width + 0];
394 color[1] = texture_data[x + y * surface_width + 1];
395 color[2] = texture_data[x + y * surface_width + 2];
396 color[3] = texture_data[x + y * surface_width + 3];
397 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
398 }
399 }
400
401 pixmap = QPixmap::fromImage(decoded_image);
402 surface_picture_label->setPixmap(pixmap);
403 surface_picture_label->resize(pixmap.size());
404
405 // Update the info with pixel data
406 surface_picker_x_control->setEnabled(true);
407 surface_picker_y_control->setEnabled(true);
408 Pick(surface_picker_x, surface_picker_y);
409
410 // Enable saving the converted pixmap to file
411 save_surface->setEnabled(true);
412}
413
414void GraphicsSurfaceWidget::SaveSurface() {
415 QString png_filter = tr("Portable Network Graphic (*.png)");
416 QString bin_filter = tr("Binary data (*.bin)");
417
418 QString selectedFilter;
419 QString filename = QFileDialog::getSaveFileName(
420 this, tr("Save Surface"),
421 QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
422 QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
423
424 if (filename.isEmpty()) {
425 // If the user canceled the dialog, don't save anything.
426 return;
427 }
428
429 if (selectedFilter == png_filter) {
430 const QPixmap* pixmap = surface_picture_label->pixmap();
431 ASSERT_MSG(pixmap != nullptr, "No pixmap set");
432
433 QFile file(filename);
434 file.open(QIODevice::WriteOnly);
435 if (pixmap)
436 pixmap->save(&file, "PNG");
437 } else if (selectedFilter == bin_filter) {
438 auto& gpu = Core::System::GetInstance().GPU();
439 VAddr address = gpu.memory_manager->PhysicalToVirtualAddress(surface_address);
440
441 const u8* buffer = Memory::GetPointer(address);
442 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
443
444 QFile file(filename);
445 file.open(QIODevice::WriteOnly);
446 int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
447 QByteArray data(reinterpret_cast<const char*>(buffer), size);
448 file.write(data);
449 } else {
450 UNREACHABLE_MSG("Unhandled filter selected");
451 }
452}
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
new file mode 100644
index 000000000..6a344bdfc
--- /dev/null
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -0,0 +1,97 @@
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 "video_core/memory_manager.h"
10#include "video_core/textures/texture.h"
11#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
12
13class QComboBox;
14class QSpinBox;
15class CSpinBox;
16
17class GraphicsSurfaceWidget;
18
19class SurfacePicture : public QLabel {
20 Q_OBJECT
21
22public:
23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture();
26
27protected slots:
28 virtual void mouseMoveEvent(QMouseEvent* event);
29 virtual void mousePressEvent(QMouseEvent* event);
30
31private:
32 GraphicsSurfaceWidget* surface_widget;
33};
34
35class GraphicsSurfaceWidget : public BreakPointObserverDock {
36 Q_OBJECT
37
38 using Event = Tegra::DebugContext::Event;
39
40 enum class Source {
41 RenderTarget0 = 0,
42 RenderTarget1 = 1,
43 RenderTarget2 = 2,
44 RenderTarget3 = 3,
45 RenderTarget4 = 4,
46 RenderTarget5 = 5,
47 RenderTarget6 = 6,
48 RenderTarget7 = 7,
49 ZBuffer = 8,
50 Custom = 9,
51 };
52
53public:
54 explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
55 QWidget* parent = nullptr);
56 void Pick(int x, int y);
57
58public slots:
59 void OnSurfaceSourceChanged(int new_value);
60 void OnSurfaceAddressChanged(qint64 new_value);
61 void OnSurfaceWidthChanged(int new_value);
62 void OnSurfaceHeightChanged(int new_value);
63 void OnSurfaceFormatChanged(int new_value);
64 void OnSurfacePickerXChanged(int new_value);
65 void OnSurfacePickerYChanged(int new_value);
66 void OnUpdate();
67
68private slots:
69 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
70 void OnResumed() override;
71
72 void SaveSurface();
73
74signals:
75 void Update();
76
77private:
78 QComboBox* surface_source_list;
79 CSpinBox* surface_address_control;
80 QSpinBox* surface_width_control;
81 QSpinBox* surface_height_control;
82 QComboBox* surface_format_control;
83
84 SurfacePicture* surface_picture_label;
85 QSpinBox* surface_picker_x_control;
86 QSpinBox* surface_picker_y_control;
87 QLabel* surface_info_label;
88 QPushButton* save_surface;
89
90 Source surface_source;
91 Tegra::GPUVAddr surface_address;
92 unsigned surface_width;
93 unsigned surface_height;
94 Tegra::Texture::TextureFormat surface_format;
95 int surface_picker_x = 0;
96 int surface_picker_y = 0;
97};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index eb22a8ccf..bd323870b 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -25,10 +25,13 @@
25#include "core/gdbstub/gdbstub.h" 25#include "core/gdbstub/gdbstub.h"
26#include "core/loader/loader.h" 26#include "core/loader/loader.h"
27#include "core/settings.h" 27#include "core/settings.h"
28#include "video_core/debug_utils/debug_utils.h"
28#include "yuzu/about_dialog.h" 29#include "yuzu/about_dialog.h"
29#include "yuzu/bootmanager.h" 30#include "yuzu/bootmanager.h"
30#include "yuzu/configuration/config.h" 31#include "yuzu/configuration/config.h"
31#include "yuzu/configuration/configure_dialog.h" 32#include "yuzu/configuration/configure_dialog.h"
33#include "yuzu/debugger/graphics/graphics_breakpoints.h"
34#include "yuzu/debugger/graphics/graphics_surface.h"
32#include "yuzu/debugger/profiler.h" 35#include "yuzu/debugger/profiler.h"
33#include "yuzu/debugger/registers.h" 36#include "yuzu/debugger/registers.h"
34#include "yuzu/debugger/wait_tree.h" 37#include "yuzu/debugger/wait_tree.h"
@@ -68,6 +71,9 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
68void GMainWindow::ShowCallouts() {} 71void GMainWindow::ShowCallouts() {}
69 72
70GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 73GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
74
75 debug_context = Tegra::DebugContext::Construct();
76
71 setAcceptDrops(true); 77 setAcceptDrops(true);
72 ui.setupUi(this); 78 ui.setupUi(this);
73 statusBar()->hide(); 79 statusBar()->hide();
@@ -160,6 +166,16 @@ void GMainWindow::InitializeDebugWidgets() {
160 connect(this, &GMainWindow::EmulationStopping, registersWidget, 166 connect(this, &GMainWindow::EmulationStopping, registersWidget,
161 &RegistersWidget::OnEmulationStopping); 167 &RegistersWidget::OnEmulationStopping);
162 168
169 graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
170 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
171 graphicsBreakpointsWidget->hide();
172 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
173
174 graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
175 addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
176 graphicsSurfaceWidget->hide();
177 debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
178
163 waitTreeWidget = new WaitTreeWidget(this); 179 waitTreeWidget = new WaitTreeWidget(this);
164 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); 180 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
165 waitTreeWidget->hide(); 181 waitTreeWidget->hide();
@@ -324,6 +340,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
324 340
325 Core::System& system{Core::System::GetInstance()}; 341 Core::System& system{Core::System::GetInstance()};
326 342
343 system.SetGPUDebugContext(debug_context);
344
327 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; 345 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
328 346
329 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); 347 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 4a0d912bb..2471caf83 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -15,17 +15,18 @@ class Config;
15class EmuThread; 15class EmuThread;
16class GameList; 16class GameList;
17class GImageInfo; 17class GImageInfo;
18class GPUCommandStreamWidget;
19class GPUCommandListWidget;
20class GraphicsBreakPointsWidget; 18class GraphicsBreakPointsWidget;
21class GraphicsTracingWidget; 19class GraphicsSurfaceWidget;
22class GraphicsVertexShaderWidget;
23class GRenderWindow; 20class GRenderWindow;
24class MicroProfileDialog; 21class MicroProfileDialog;
25class ProfilerWidget; 22class ProfilerWidget;
26class RegistersWidget; 23class RegistersWidget;
27class WaitTreeWidget; 24class WaitTreeWidget;
28 25
26namespace Tegra {
27class DebugContext;
28}
29
29class GMainWindow : public QMainWindow { 30class GMainWindow : public QMainWindow {
30 Q_OBJECT 31 Q_OBJECT
31 32
@@ -138,6 +139,8 @@ private:
138 139
139 Ui::MainWindow ui; 140 Ui::MainWindow ui;
140 141
142 std::shared_ptr<Tegra::DebugContext> debug_context;
143
141 GRenderWindow* render_window; 144 GRenderWindow* render_window;
142 GameList* game_list; 145 GameList* game_list;
143 146
@@ -158,6 +161,8 @@ private:
158 ProfilerWidget* profilerWidget; 161 ProfilerWidget* profilerWidget;
159 MicroProfileDialog* microProfileDialog; 162 MicroProfileDialog* microProfileDialog;
160 RegistersWidget* registersWidget; 163 RegistersWidget* registersWidget;
164 GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
165 GraphicsSurfaceWidget* graphicsSurfaceWidget;
161 WaitTreeWidget* waitTreeWidget; 166 WaitTreeWidget* waitTreeWidget;
162 167
163 QAction* actions_recent_files[max_recent_files_item]; 168 QAction* actions_recent_files[max_recent_files_item];