summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp10
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/hle/service/nifm/nifm.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp2
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp49
-rw-r--r--src/video_core/debug_utils/debug_utils.h157
-rw-r--r--src/video_core/engines/maxwell_3d.cpp31
-rw-r--r--src/video_core/gpu.cpp7
-rw-r--r--src/video_core/gpu.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp89
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.h56
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp8
-rw-r--r--src/video_core/shader/node.h26
-rw-r--r--src/video_core/shader/shader_ir.cpp6
-rw-r--r--src/video_core/shader/shader_ir.h8
-rw-r--r--src/yuzu/CMakeLists.txt5
-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.cpp221
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.h45
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h37
-rw-r--r--src/yuzu/main.cpp11
-rw-r--r--src/yuzu/main.h8
28 files changed, 248 insertions, 657 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c45fb960c..d697b80ef 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -46,7 +46,6 @@
46#include "core/settings.h" 46#include "core/settings.h"
47#include "core/telemetry_session.h" 47#include "core/telemetry_session.h"
48#include "core/tools/freezer.h" 48#include "core/tools/freezer.h"
49#include "video_core/debug_utils/debug_utils.h"
50#include "video_core/renderer_base.h" 49#include "video_core/renderer_base.h"
51#include "video_core/video_core.h" 50#include "video_core/video_core.h"
52 51
@@ -341,7 +340,6 @@ struct System::Impl {
341 std::unique_ptr<Loader::AppLoader> app_loader; 340 std::unique_ptr<Loader::AppLoader> app_loader;
342 std::unique_ptr<VideoCore::RendererBase> renderer; 341 std::unique_ptr<VideoCore::RendererBase> renderer;
343 std::unique_ptr<Tegra::GPU> gpu_core; 342 std::unique_ptr<Tegra::GPU> gpu_core;
344 std::shared_ptr<Tegra::DebugContext> debug_context;
345 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 343 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
346 Memory::Memory memory; 344 Memory::Memory memory;
347 CpuCoreManager cpu_core_manager; 345 CpuCoreManager cpu_core_manager;
@@ -580,14 +578,6 @@ Loader::AppLoader& System::GetAppLoader() const {
580 return *impl->app_loader; 578 return *impl->app_loader;
581} 579}
582 580
583void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
584 impl->debug_context = std::move(context);
585}
586
587Tegra::DebugContext* System::GetGPUDebugContext() const {
588 return impl->debug_context.get();
589}
590
591void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 581void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
592 impl->virtual_filesystem = std::move(vfs); 582 impl->virtual_filesystem = std::move(vfs);
593} 583}
diff --git a/src/core/core.h b/src/core/core.h
index 91184e433..e240c5c58 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -307,10 +307,6 @@ public:
307 Service::SM::ServiceManager& ServiceManager(); 307 Service::SM::ServiceManager& ServiceManager();
308 const Service::SM::ServiceManager& ServiceManager() const; 308 const Service::SM::ServiceManager& ServiceManager() const;
309 309
310 void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
311
312 Tegra::DebugContext* GetGPUDebugContext() const;
313
314 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); 310 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
315 311
316 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 312 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 2e53b3221..767158444 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -9,6 +9,7 @@
9#include "core/hle/kernel/writable_event.h" 9#include "core/hle/kernel/writable_event.h"
10#include "core/hle/service/nifm/nifm.h" 10#include "core/hle/service/nifm/nifm.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/settings.h"
12 13
13namespace Service::NIFM { 14namespace Service::NIFM {
14 15
@@ -86,7 +87,12 @@ private:
86 87
87 IPC::ResponseBuilder rb{ctx, 3}; 88 IPC::ResponseBuilder rb{ctx, 3};
88 rb.Push(RESULT_SUCCESS); 89 rb.Push(RESULT_SUCCESS);
89 rb.PushEnum(RequestState::Connected); 90
91 if (Settings::values.bcat_backend == "none") {
92 rb.PushEnum(RequestState::NotSubmitted);
93 } else {
94 rb.PushEnum(RequestState::Connected);
95 }
90 } 96 }
91 97
92 void GetResult(Kernel::HLERequestContext& ctx) { 98 void GetResult(Kernel::HLERequestContext& ctx) {
@@ -194,14 +200,22 @@ private:
194 200
195 IPC::ResponseBuilder rb{ctx, 3}; 201 IPC::ResponseBuilder rb{ctx, 3};
196 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
197 rb.Push<u8>(1); 203 if (Settings::values.bcat_backend == "none") {
204 rb.Push<u8>(0);
205 } else {
206 rb.Push<u8>(1);
207 }
198 } 208 }
199 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 209 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
200 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 210 LOG_WARNING(Service_NIFM, "(STUBBED) called");
201 211
202 IPC::ResponseBuilder rb{ctx, 3}; 212 IPC::ResponseBuilder rb{ctx, 3};
203 rb.Push(RESULT_SUCCESS); 213 rb.Push(RESULT_SUCCESS);
204 rb.Push<u8>(1); 214 if (Settings::values.bcat_backend == "none") {
215 rb.Push<u8>(0);
216 } else {
217 rb.Push<u8>(1);
218 }
205 } 219 }
206 Core::System& system; 220 Core::System& system;
207}; 221};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 07c88465e..195421cc0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -104,10 +104,12 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
104 104
105 ASSERT(object->status == nvmap::Object::Status::Allocated); 105 ASSERT(object->status == nvmap::Object::Status::Allocated);
106 106
107 u64 size = static_cast<u64>(entry.pages) << 0x10; 107 const u64 size = static_cast<u64>(entry.pages) << 0x10;
108 ASSERT(size <= object->size); 108 ASSERT(size <= object->size);
109 const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10;
109 110
110 GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); 111 const GPUVAddr returned =
112 gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
111 ASSERT(returned == offset); 113 ASSERT(returned == offset);
112 } 114 }
113 std::memcpy(output.data(), entries.data(), output.size()); 115 std::memcpy(output.data(), entries.data(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 169fb8f0e..f79fcc065 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -62,7 +62,7 @@ private:
62 u16_le flags; 62 u16_le flags;
63 u16_le kind; 63 u16_le kind;
64 u32_le nvmap_handle; 64 u32_le nvmap_handle;
65 INSERT_PADDING_WORDS(1); 65 u32_le map_offset;
66 u32_le offset; 66 u32_le offset;
67 u32_le pages; 67 u32_le pages;
68 }; 68 };
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 52623cf89..9810d2c64 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -192,7 +192,7 @@ void NVFlinger::Compose() {
192 192
193 const auto& igbp_buffer = buffer->get().igbp_buffer; 193 const auto& igbp_buffer = buffer->get().igbp_buffer;
194 194
195 const auto& gpu = system.GPU(); 195 auto& gpu = system.GPU();
196 const auto& multi_fence = buffer->get().multi_fence; 196 const auto& multi_fence = buffer->get().multi_fence;
197 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 197 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
198 const auto& fence = multi_fence.fences[fence_id]; 198 const auto& fence = multi_fence.fences[fence_id];
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 2594cd0bd..abbf218f5 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -4,8 +4,6 @@ add_library(video_core STATIC
4 buffer_cache/map_interval.h 4 buffer_cache/map_interval.h
5 dma_pusher.cpp 5 dma_pusher.cpp
6 dma_pusher.h 6 dma_pusher.h
7 debug_utils/debug_utils.cpp
8 debug_utils/debug_utils.h
9 engines/const_buffer_engine_interface.h 7 engines/const_buffer_engine_interface.h
10 engines/const_buffer_info.h 8 engines/const_buffer_info.h
11 engines/engine_upload.cpp 9 engines/engine_upload.cpp
@@ -157,6 +155,8 @@ if (ENABLE_VULKAN)
157 renderer_vulkan/maxwell_to_vk.h 155 renderer_vulkan/maxwell_to_vk.h
158 renderer_vulkan/vk_buffer_cache.cpp 156 renderer_vulkan/vk_buffer_cache.cpp
159 renderer_vulkan/vk_buffer_cache.h 157 renderer_vulkan/vk_buffer_cache.h
158 renderer_vulkan/vk_descriptor_pool.cpp
159 renderer_vulkan/vk_descriptor_pool.h
160 renderer_vulkan/vk_device.cpp 160 renderer_vulkan/vk_device.cpp
161 renderer_vulkan/vk_device.h 161 renderer_vulkan/vk_device.h
162 renderer_vulkan/vk_image.cpp 162 renderer_vulkan/vk_image.cpp
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
deleted file mode 100644
index f0ef67535..000000000
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <mutex>
6
7#include "video_core/debug_utils/debug_utils.h"
8
9namespace Tegra {
10
11void DebugContext::DoOnEvent(Event event, void* data) {
12 {
13 std::unique_lock lock{breakpoint_mutex};
14
15 // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
16 // show on debug widgets
17
18 // TODO: Should stop the CPU thread here once we multithread emulation.
19
20 active_breakpoint = event;
21 at_breakpoint = true;
22
23 // Tell all observers that we hit a breakpoint
24 for (auto& breakpoint_observer : breakpoint_observers) {
25 breakpoint_observer->OnMaxwellBreakPointHit(event, data);
26 }
27
28 // Wait until another thread tells us to Resume()
29 resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
30 }
31}
32
33void DebugContext::Resume() {
34 {
35 std::lock_guard lock{breakpoint_mutex};
36
37 // Tell all observers that we are about to resume
38 for (auto& breakpoint_observer : breakpoint_observers) {
39 breakpoint_observer->OnMaxwellResume();
40 }
41
42 // Resume the waiting thread (i.e. OnEvent())
43 at_breakpoint = false;
44 }
45
46 resume_from_breakpoint.notify_one();
47}
48
49} // namespace Tegra
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
deleted file mode 100644
index ac3a2eb01..000000000
--- a/src/video_core/debug_utils/debug_utils.h
+++ /dev/null
@@ -1,157 +0,0 @@
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 <array>
8#include <condition_variable>
9#include <list>
10#include <memory>
11#include <mutex>
12
13namespace Tegra {
14
15class DebugContext {
16public:
17 enum class Event {
18 FirstEvent = 0,
19
20 MaxwellCommandLoaded = FirstEvent,
21 MaxwellCommandProcessed,
22 IncomingPrimitiveBatch,
23 FinishedPrimitiveBatch,
24
25 NumEvents
26 };
27
28 /**
29 * Inherit from this class to be notified of events registered to some debug context.
30 * Most importantly this is used for our debugger GUI.
31 *
32 * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
33 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
34 * access
35 * @todo Evaluate an alternative interface, in which there is only one managing observer and
36 * multiple child observers running (by design) on the same thread.
37 */
38 class BreakPointObserver {
39 public:
40 /// Constructs the object such that it observes events of the given DebugContext.
41 explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
42 : context_weak(debug_context) {
43 std::unique_lock lock{debug_context->breakpoint_mutex};
44 debug_context->breakpoint_observers.push_back(this);
45 }
46
47 virtual ~BreakPointObserver() {
48 auto context = context_weak.lock();
49 if (context) {
50 {
51 std::unique_lock lock{context->breakpoint_mutex};
52 context->breakpoint_observers.remove(this);
53 }
54
55 // If we are the last observer to be destroyed, tell the debugger context that
56 // it is free to continue. In particular, this is required for a proper yuzu
57 // shutdown, when the emulation thread is waiting at a breakpoint.
58 if (context->breakpoint_observers.empty())
59 context->Resume();
60 }
61 }
62
63 /**
64 * Action to perform when a breakpoint was reached.
65 * @param event Type of event which triggered the breakpoint
66 * @param data Optional data pointer (if unused, this is a nullptr)
67 * @note This function will perform nothing unless it is overridden in the child class.
68 */
69 virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
70
71 /**
72 * Action to perform when emulation is resumed from a breakpoint.
73 * @note This function will perform nothing unless it is overridden in the child class.
74 */
75 virtual void OnMaxwellResume() {}
76
77 protected:
78 /**
79 * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
80 * context_weak.lock(), always compare the result against nullptr.
81 */
82 std::weak_ptr<DebugContext> context_weak;
83 };
84
85 /**
86 * Simple structure defining a breakpoint state
87 */
88 struct BreakPoint {
89 bool enabled = false;
90 };
91
92 /**
93 * Static constructor used to create a shared_ptr of a DebugContext.
94 */
95 static std::shared_ptr<DebugContext> Construct() {
96 return std::shared_ptr<DebugContext>(new DebugContext);
97 }
98
99 /**
100 * Used by the emulation core when a given event has happened. If a breakpoint has been set
101 * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
102 * The current thread then is halted until Resume() is called from another thread (or until
103 * emulation is stopped).
104 * @param event Event which has happened
105 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
106 * Resume() is called.
107 */
108 void OnEvent(Event event, void* data) {
109 // This check is left in the header to allow the compiler to inline it.
110 if (!breakpoints[(int)event].enabled)
111 return;
112 // For the rest of event handling, call a separate function.
113 DoOnEvent(event, data);
114 }
115
116 void DoOnEvent(Event event, void* data);
117
118 /**
119 * Resume from the current breakpoint.
120 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
121 * Calling from any other thread is safe.
122 */
123 void Resume();
124
125 /**
126 * Delete all set breakpoints and resume emulation.
127 */
128 void ClearBreakpoints() {
129 for (auto& bp : breakpoints) {
130 bp.enabled = false;
131 }
132 Resume();
133 }
134
135 // TODO: Evaluate if access to these members should be hidden behind a public interface.
136 std::array<BreakPoint, static_cast<int>(Event::NumEvents)> breakpoints;
137 Event active_breakpoint{};
138 bool at_breakpoint = false;
139
140private:
141 /**
142 * Private default constructor to make sure people always construct this through Construct()
143 * instead.
144 */
145 DebugContext() = default;
146
147 /// Mutex protecting current breakpoint state and the observer list.
148 std::mutex breakpoint_mutex;
149
150 /// Used by OnEvent to wait for resumption.
151 std::condition_variable resume_from_breakpoint;
152
153 /// List of registered observers
154 std::list<BreakPointObserver*> breakpoint_observers;
155};
156
157} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index e1cb8b0b0..1d1f780e7 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -7,7 +7,6 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "video_core/debug_utils/debug_utils.h"
11#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
12#include "video_core/engines/shader_type.h" 11#include "video_core/engines/shader_type.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
@@ -273,8 +272,6 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
273} 272}
274 273
275void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { 274void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
276 auto debug_context = system.GetGPUDebugContext();
277
278 const u32 method = method_call.method; 275 const u32 method = method_call.method;
279 276
280 if (method == cb_data_state.current) { 277 if (method == cb_data_state.current) {
@@ -315,10 +312,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
315 ASSERT_MSG(method < Regs::NUM_REGS, 312 ASSERT_MSG(method < Regs::NUM_REGS,
316 "Invalid Maxwell3D register, increase the size of the Regs structure"); 313 "Invalid Maxwell3D register, increase the size of the Regs structure");
317 314
318 if (debug_context) {
319 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
320 }
321
322 if (regs.reg_array[method] != method_call.argument) { 315 if (regs.reg_array[method] != method_call.argument) {
323 regs.reg_array[method] = method_call.argument; 316 regs.reg_array[method] = method_call.argument;
324 const std::size_t dirty_reg = dirty_pointers[method]; 317 const std::size_t dirty_reg = dirty_pointers[method];
@@ -424,10 +417,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
424 default: 417 default:
425 break; 418 break;
426 } 419 }
427
428 if (debug_context) {
429 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
430 }
431} 420}
432 421
433void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { 422void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
@@ -485,12 +474,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
485 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); 474 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
486 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); 475 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
487 476
488 auto debug_context = system.GetGPUDebugContext();
489
490 if (debug_context) {
491 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
492 }
493
494 // Both instance configuration registers can not be set at the same time. 477 // Both instance configuration registers can not be set at the same time.
495 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, 478 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
496 "Illegal combination of instancing parameters"); 479 "Illegal combination of instancing parameters");
@@ -500,10 +483,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
500 rasterizer.DrawMultiBatch(is_indexed); 483 rasterizer.DrawMultiBatch(is_indexed);
501 } 484 }
502 485
503 if (debug_context) {
504 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
505 }
506
507 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 486 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
508 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - 487 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
509 // it's possible that it is incorrect and that there is some other register used to specify the 488 // it's possible that it is incorrect and that there is some other register used to specify the
@@ -650,12 +629,6 @@ void Maxwell3D::DrawArrays() {
650 regs.vertex_buffer.count); 629 regs.vertex_buffer.count);
651 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); 630 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
652 631
653 auto debug_context = system.GetGPUDebugContext();
654
655 if (debug_context) {
656 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
657 }
658
659 // Both instance configuration registers can not be set at the same time. 632 // Both instance configuration registers can not be set at the same time.
660 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, 633 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
661 "Illegal combination of instancing parameters"); 634 "Illegal combination of instancing parameters");
@@ -673,10 +646,6 @@ void Maxwell3D::DrawArrays() {
673 rasterizer.DrawBatch(is_indexed); 646 rasterizer.DrawBatch(is_indexed);
674 } 647 }
675 648
676 if (debug_context) {
677 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
678 }
679
680 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 649 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
681 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - 650 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
682 // it's possible that it is incorrect and that there is some other register used to specify the 651 // it's possible that it is incorrect and that there is some other register used to specify the
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 095660115..b9c5c41a2 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -66,19 +66,20 @@ const DmaPusher& GPU::DmaPusher() const {
66 return *dma_pusher; 66 return *dma_pusher;
67} 67}
68 68
69void GPU::WaitFence(u32 syncpoint_id, u32 value) const { 69void GPU::WaitFence(u32 syncpoint_id, u32 value) {
70 // Synced GPU, is always in sync 70 // Synced GPU, is always in sync
71 if (!is_async) { 71 if (!is_async) {
72 return; 72 return;
73 } 73 }
74 MICROPROFILE_SCOPE(GPU_wait); 74 MICROPROFILE_SCOPE(GPU_wait);
75 while (syncpoints[syncpoint_id].load(std::memory_order_relaxed) < value) { 75 std::unique_lock lock{sync_mutex};
76 } 76 sync_cv.wait(lock, [=]() { return syncpoints[syncpoint_id].load() >= value; });
77} 77}
78 78
79void GPU::IncrementSyncPoint(const u32 syncpoint_id) { 79void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
80 syncpoints[syncpoint_id]++; 80 syncpoints[syncpoint_id]++;
81 std::lock_guard lock{sync_mutex}; 81 std::lock_guard lock{sync_mutex};
82 sync_cv.notify_all();
82 if (!syncpt_interrupts[syncpoint_id].empty()) { 83 if (!syncpt_interrupts[syncpoint_id].empty()) {
83 u32 value = syncpoints[syncpoint_id].load(); 84 u32 value = syncpoints[syncpoint_id].load();
84 auto it = syncpt_interrupts[syncpoint_id].begin(); 85 auto it = syncpt_interrupts[syncpoint_id].begin();
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ecc338ae9..b648317bb 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <condition_variable>
9#include <list> 10#include <list>
10#include <memory> 11#include <memory>
11#include <mutex> 12#include <mutex>
@@ -181,7 +182,7 @@ public:
181 virtual void WaitIdle() const = 0; 182 virtual void WaitIdle() const = 0;
182 183
183 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. 184 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
184 void WaitFence(u32 syncpoint_id, u32 value) const; 185 void WaitFence(u32 syncpoint_id, u32 value);
185 186
186 void IncrementSyncPoint(u32 syncpoint_id); 187 void IncrementSyncPoint(u32 syncpoint_id);
187 188
@@ -312,6 +313,8 @@ private:
312 313
313 std::mutex sync_mutex; 314 std::mutex sync_mutex;
314 315
316 std::condition_variable sync_cv;
317
315 const bool is_async; 318 const bool is_async;
316}; 319};
317 320
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index a311dbcfe..f9f7a97b5 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -751,6 +751,9 @@ private:
751 751
752 Expression Visit(const Node& node) { 752 Expression Visit(const Node& node) {
753 if (const auto operation = std::get_if<OperationNode>(&*node)) { 753 if (const auto operation = std::get_if<OperationNode>(&*node)) {
754 if (const auto amend_index = operation->GetAmendIndex()) {
755 Visit(ir.GetAmendNode(*amend_index)).CheckVoid();
756 }
754 const auto operation_index = static_cast<std::size_t>(operation->GetCode()); 757 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
755 if (operation_index >= operation_decompilers.size()) { 758 if (operation_index >= operation_decompilers.size()) {
756 UNREACHABLE_MSG("Out of bounds operation: {}", operation_index); 759 UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
@@ -872,6 +875,9 @@ private:
872 } 875 }
873 876
874 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { 877 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
878 if (const auto amend_index = conditional->GetAmendIndex()) {
879 Visit(ir.GetAmendNode(*amend_index)).CheckVoid();
880 }
875 // It's invalid to call conditional on nested nodes, use an operation instead 881 // It's invalid to call conditional on nested nodes, use an operation instead
876 code.AddLine("if ({}) {{", Visit(conditional->GetCondition()).AsBool()); 882 code.AddLine("if ({}) {{", Visit(conditional->GetCondition()).AsBool());
877 ++code.scope; 883 ++code.scope;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 9ed738171..ea4f35663 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -120,6 +120,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
120 return GL_POINTS; 120 return GL_POINTS;
121 case Maxwell::PrimitiveTopology::Lines: 121 case Maxwell::PrimitiveTopology::Lines:
122 return GL_LINES; 122 return GL_LINES;
123 case Maxwell::PrimitiveTopology::LineLoop:
124 return GL_LINE_LOOP;
123 case Maxwell::PrimitiveTopology::LineStrip: 125 case Maxwell::PrimitiveTopology::LineStrip:
124 return GL_LINE_STRIP; 126 return GL_LINE_STRIP;
125 case Maxwell::PrimitiveTopology::Triangles: 127 case Maxwell::PrimitiveTopology::Triangles:
@@ -130,11 +132,23 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
130 return GL_TRIANGLE_FAN; 132 return GL_TRIANGLE_FAN;
131 case Maxwell::PrimitiveTopology::Quads: 133 case Maxwell::PrimitiveTopology::Quads:
132 return GL_QUADS; 134 return GL_QUADS;
133 default: 135 case Maxwell::PrimitiveTopology::QuadStrip:
134 LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); 136 return GL_QUAD_STRIP;
135 UNREACHABLE(); 137 case Maxwell::PrimitiveTopology::Polygon:
136 return {}; 138 return GL_POLYGON;
139 case Maxwell::PrimitiveTopology::LinesAdjacency:
140 return GL_LINES_ADJACENCY;
141 case Maxwell::PrimitiveTopology::LineStripAdjacency:
142 return GL_LINE_STRIP_ADJACENCY;
143 case Maxwell::PrimitiveTopology::TrianglesAdjacency:
144 return GL_TRIANGLES_ADJACENCY;
145 case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
146 return GL_TRIANGLE_STRIP_ADJACENCY;
147 case Maxwell::PrimitiveTopology::Patches:
148 return GL_PATCHES;
137 } 149 }
150 UNREACHABLE_MSG("Invalid topology={}", static_cast<int>(topology));
151 return GL_POINTS;
138} 152}
139 153
140inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, 154inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
new file mode 100644
index 000000000..cc7c281a0
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -0,0 +1,89 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <vector>
7
8#include "common/common_types.h"
9#include "video_core/renderer_vulkan/declarations.h"
10#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
11#include "video_core/renderer_vulkan/vk_device.h"
12#include "video_core/renderer_vulkan/vk_resource_manager.h"
13
14namespace Vulkan {
15
16// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
17constexpr std::size_t SETS_GROW_RATE = 0x20;
18
19DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool,
20 vk::DescriptorSetLayout layout)
21 : VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {}
22
23DescriptorAllocator::~DescriptorAllocator() = default;
24
25vk::DescriptorSet DescriptorAllocator::Commit(VKFence& fence) {
26 return *descriptors[CommitResource(fence)];
27}
28
29void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) {
30 auto new_sets = descriptor_pool.AllocateDescriptors(layout, end - begin);
31 descriptors.insert(descriptors.end(), std::make_move_iterator(new_sets.begin()),
32 std::make_move_iterator(new_sets.end()));
33}
34
35VKDescriptorPool::VKDescriptorPool(const VKDevice& device)
36 : device{device}, active_pool{AllocateNewPool()} {}
37
38VKDescriptorPool::~VKDescriptorPool() = default;
39
40vk::DescriptorPool VKDescriptorPool::AllocateNewPool() {
41 static constexpr u32 num_sets = 0x20000;
42 static constexpr vk::DescriptorPoolSize pool_sizes[] = {
43 {vk::DescriptorType::eUniformBuffer, num_sets * 90},
44 {vk::DescriptorType::eStorageBuffer, num_sets * 60},
45 {vk::DescriptorType::eUniformTexelBuffer, num_sets * 64},
46 {vk::DescriptorType::eCombinedImageSampler, num_sets * 64},
47 {vk::DescriptorType::eStorageImage, num_sets * 40}};
48
49 const vk::DescriptorPoolCreateInfo create_info(
50 vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, num_sets,
51 static_cast<u32>(std::size(pool_sizes)), std::data(pool_sizes));
52 const auto dev = device.GetLogical();
53 return *pools.emplace_back(
54 dev.createDescriptorPoolUnique(create_info, nullptr, device.GetDispatchLoader()));
55}
56
57std::vector<UniqueDescriptorSet> VKDescriptorPool::AllocateDescriptors(
58 vk::DescriptorSetLayout layout, std::size_t count) {
59 std::vector layout_copies(count, layout);
60 vk::DescriptorSetAllocateInfo allocate_info(active_pool, static_cast<u32>(count),
61 layout_copies.data());
62
63 std::vector<vk::DescriptorSet> sets(count);
64 const auto dev = device.GetLogical();
65 const auto& dld = device.GetDispatchLoader();
66 switch (const auto result = dev.allocateDescriptorSets(&allocate_info, sets.data(), dld)) {
67 case vk::Result::eSuccess:
68 break;
69 case vk::Result::eErrorOutOfPoolMemory:
70 active_pool = AllocateNewPool();
71 allocate_info.descriptorPool = active_pool;
72 if (dev.allocateDescriptorSets(&allocate_info, sets.data(), dld) == vk::Result::eSuccess) {
73 break;
74 }
75 [[fallthrough]];
76 default:
77 vk::throwResultException(result, "vk::Device::allocateDescriptorSetsUnique");
78 }
79
80 vk::PoolFree deleter(dev, active_pool, dld);
81 std::vector<UniqueDescriptorSet> unique_sets;
82 unique_sets.reserve(count);
83 for (const auto set : sets) {
84 unique_sets.push_back(UniqueDescriptorSet{set, deleter});
85 }
86 return unique_sets;
87}
88
89} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
new file mode 100644
index 000000000..a441dbc0f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
@@ -0,0 +1,56 @@
1// Copyright 2019 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 <memory>
8#include <vector>
9
10#include "common/common_types.h"
11#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/vk_resource_manager.h"
13
14namespace Vulkan {
15
16class VKDescriptorPool;
17
18class DescriptorAllocator final : public VKFencedPool {
19public:
20 explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, vk::DescriptorSetLayout layout);
21 ~DescriptorAllocator() override;
22
23 DescriptorAllocator(const DescriptorAllocator&) = delete;
24
25 vk::DescriptorSet Commit(VKFence& fence);
26
27protected:
28 void Allocate(std::size_t begin, std::size_t end) override;
29
30private:
31 VKDescriptorPool& descriptor_pool;
32 const vk::DescriptorSetLayout layout;
33
34 std::vector<UniqueDescriptorSet> descriptors;
35};
36
37class VKDescriptorPool final {
38 friend DescriptorAllocator;
39
40public:
41 explicit VKDescriptorPool(const VKDevice& device);
42 ~VKDescriptorPool();
43
44private:
45 vk::DescriptorPool AllocateNewPool();
46
47 std::vector<UniqueDescriptorSet> AllocateDescriptors(vk::DescriptorSetLayout layout,
48 std::size_t count);
49
50 const VKDevice& device;
51
52 std::vector<UniqueDescriptorPool> pools;
53 vk::DescriptorPool active_pool;
54};
55
56} // namespace Vulkan \ No newline at end of file
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a8baf91de..8fe852ce8 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -954,6 +954,10 @@ private:
954 954
955 Expression Visit(const Node& node) { 955 Expression Visit(const Node& node) {
956 if (const auto operation = std::get_if<OperationNode>(&*node)) { 956 if (const auto operation = std::get_if<OperationNode>(&*node)) {
957 if (const auto amend_index = operation->GetAmendIndex()) {
958 [[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type;
959 ASSERT(type == Type::Void);
960 }
957 const auto operation_index = static_cast<std::size_t>(operation->GetCode()); 961 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
958 const auto decompiler = operation_decompilers[operation_index]; 962 const auto decompiler = operation_decompilers[operation_index];
959 if (decompiler == nullptr) { 963 if (decompiler == nullptr) {
@@ -1142,6 +1146,10 @@ private:
1142 } 1146 }
1143 1147
1144 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { 1148 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
1149 if (const auto amend_index = conditional->GetAmendIndex()) {
1150 [[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type;
1151 ASSERT(type == Type::Void);
1152 }
1145 // It's invalid to call conditional on nested nodes, use an operation instead 1153 // It's invalid to call conditional on nested nodes, use an operation instead
1146 const Id true_label = OpLabel(); 1154 const Id true_label = OpLabel();
1147 const Id skip_label = OpLabel(); 1155 const Id skip_label = OpLabel();
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index 4d2f4d6a8..4e155542a 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -392,8 +392,30 @@ struct MetaImage {
392using Meta = 392using Meta =
393 std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>; 393 std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>;
394 394
395class AmendNode {
396public:
397 std::optional<std::size_t> GetAmendIndex() const {
398 if (amend_index == amend_null_index) {
399 return std::nullopt;
400 }
401 return {amend_index};
402 }
403
404 void SetAmendIndex(std::size_t index) {
405 amend_index = index;
406 }
407
408 void ClearAmend() {
409 amend_index = amend_null_index;
410 }
411
412private:
413 static constexpr std::size_t amend_null_index = 0xFFFFFFFFFFFFFFFFULL;
414 std::size_t amend_index{amend_null_index};
415};
416
395/// Holds any kind of operation that can be done in the IR 417/// Holds any kind of operation that can be done in the IR
396class OperationNode final { 418class OperationNode final : public AmendNode {
397public: 419public:
398 explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {} 420 explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {}
399 421
@@ -433,7 +455,7 @@ private:
433}; 455};
434 456
435/// Encloses inside any kind of node that returns a boolean conditionally-executed code 457/// Encloses inside any kind of node that returns a boolean conditionally-executed code
436class ConditionalNode final { 458class ConditionalNode final : public AmendNode {
437public: 459public:
438 explicit ConditionalNode(Node condition, std::vector<Node>&& code) 460 explicit ConditionalNode(Node condition, std::vector<Node>&& code)
439 : condition{std::move(condition)}, code{std::move(code)} {} 461 : condition{std::move(condition)}, code{std::move(code)} {}
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 1d9825c76..31eecb3f4 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -446,4 +446,10 @@ Node ShaderIR::BitfieldInsert(Node base, Node insert, u32 offset, u32 bits) {
446 Immediate(bits)); 446 Immediate(bits));
447} 447}
448 448
449std::size_t ShaderIR::DeclareAmend(Node new_amend) {
450 const std::size_t id = amend_code.size();
451 amend_code.push_back(new_amend);
452 return id;
453}
454
449} // namespace VideoCommon::Shader 455} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index baed06ccd..aacd0a0da 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -176,6 +176,10 @@ public:
176 /// Returns a condition code evaluated from internal flags 176 /// Returns a condition code evaluated from internal flags
177 Node GetConditionCode(Tegra::Shader::ConditionCode cc) const; 177 Node GetConditionCode(Tegra::Shader::ConditionCode cc) const;
178 178
179 const Node& GetAmendNode(std::size_t index) const {
180 return amend_code[index];
181 }
182
179private: 183private:
180 friend class ASTDecoder; 184 friend class ASTDecoder;
181 185
@@ -392,6 +396,9 @@ private:
392 Tegra::Shader::Instruction instr, 396 Tegra::Shader::Instruction instr,
393 bool is_write); 397 bool is_write);
394 398
399 /// Register new amending code and obtain the reference id.
400 std::size_t DeclareAmend(Node new_amend);
401
395 const ProgramCode& program_code; 402 const ProgramCode& program_code;
396 const u32 main_offset; 403 const u32 main_offset;
397 const CompilerSettings settings; 404 const CompilerSettings settings;
@@ -406,6 +413,7 @@ private:
406 std::map<u32, NodeBlock> basic_blocks; 413 std::map<u32, NodeBlock> basic_blocks;
407 NodeBlock global_code; 414 NodeBlock global_code;
408 ASTManager program_manager{true, true}; 415 ASTManager program_manager{true, true};
416 std::vector<Node> amend_code;
409 417
410 std::set<u32> used_registers; 418 std::set<u32> used_registers;
411 std::set<Tegra::Shader::Pred> used_predicates; 419 std::set<Tegra::Shader::Pred> used_predicates;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index ff1c1d985..11ae1e66e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -78,11 +78,6 @@ add_executable(yuzu
78 configuration/configure_web.cpp 78 configuration/configure_web.cpp
79 configuration/configure_web.h 79 configuration/configure_web.h
80 configuration/configure_web.ui 80 configuration/configure_web.ui
81 debugger/graphics/graphics_breakpoint_observer.cpp
82 debugger/graphics/graphics_breakpoint_observer.h
83 debugger/graphics/graphics_breakpoints.cpp
84 debugger/graphics/graphics_breakpoints.h
85 debugger/graphics/graphics_breakpoints_p.h
86 debugger/console.cpp 81 debugger/console.cpp
87 debugger/console.h 82 debugger/console.h
88 debugger/profiler.cpp 83 debugger/profiler.cpp
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 5f459ccfb..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 "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, &BreakPointObserverDock::Resumed, this, &BreakPointObserverDock::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, &BreakPointObserverDock::BreakPointHit, this,
18 &BreakPointObserverDock::OnBreakPointHit, 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
deleted file mode 100644
index ab32f0115..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 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
26signals:
27 void Resumed();
28 void BreakPointHit(Tegra::DebugContext::Event event, void* data);
29
30private:
31 virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
32 virtual void OnResumed() = 0;
33};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
deleted file mode 100644
index 1c80082a4..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ /dev/null
@@ -1,221 +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 "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 return DebugContextEventToString(event);
35 }
36 break;
37 }
38
39 case Qt::CheckStateRole: {
40 if (index.column() == 0)
41 return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
42 break;
43 }
44
45 case Qt::BackgroundRole: {
46 if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
47 return QBrush(QColor(0xE0, 0xE0, 0x10));
48 }
49 break;
50 }
51
52 case Role_IsEnabled: {
53 auto context = context_weak.lock();
54 return context && context->breakpoints[(int)event].enabled;
55 }
56
57 default:
58 break;
59 }
60 return QVariant();
61}
62
63Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const {
64 if (!index.isValid())
65 return 0;
66
67 Qt::ItemFlags flags = Qt::ItemIsEnabled;
68 if (index.column() == 0)
69 flags |= Qt::ItemIsUserCheckable;
70 return flags;
71}
72
73bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) {
74 const auto event = static_cast<Tegra::DebugContext::Event>(index.row());
75
76 switch (role) {
77 case Qt::CheckStateRole: {
78 if (index.column() != 0)
79 return false;
80
81 auto context = context_weak.lock();
82 if (!context)
83 return false;
84
85 context->breakpoints[(int)event].enabled = value == Qt::Checked;
86 QModelIndex changed_index = createIndex(index.row(), 0);
87 emit dataChanged(changed_index, changed_index);
88 return true;
89 }
90 }
91
92 return false;
93}
94
95void BreakPointModel::OnBreakPointHit(Tegra::DebugContext::Event event) {
96 auto context = context_weak.lock();
97 if (!context)
98 return;
99
100 active_breakpoint = context->active_breakpoint;
101 at_breakpoint = context->at_breakpoint;
102 emit dataChanged(createIndex(static_cast<int>(event), 0),
103 createIndex(static_cast<int>(event), 0));
104}
105
106void BreakPointModel::OnResumed() {
107 auto context = context_weak.lock();
108 if (!context)
109 return;
110
111 at_breakpoint = context->at_breakpoint;
112 emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
113 createIndex(static_cast<int>(active_breakpoint), 0));
114 active_breakpoint = context->active_breakpoint;
115}
116
117QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) {
118 switch (event) {
119 case Tegra::DebugContext::Event::MaxwellCommandLoaded:
120 return tr("Maxwell command loaded");
121 case Tegra::DebugContext::Event::MaxwellCommandProcessed:
122 return tr("Maxwell command processed");
123 case Tegra::DebugContext::Event::IncomingPrimitiveBatch:
124 return tr("Incoming primitive batch");
125 case Tegra::DebugContext::Event::FinishedPrimitiveBatch:
126 return tr("Finished primitive batch");
127 case Tegra::DebugContext::Event::NumEvents:
128 break;
129 }
130
131 return tr("Unknown debug context event");
132}
133
134GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
137 debug_context) {
138 setObjectName(QStringLiteral("TegraBreakPointsWidget"));
139
140 status_text = new QLabel(tr("Emulation running"));
141 resume_button = new QPushButton(tr("Resume"));
142 resume_button->setEnabled(false);
143
144 breakpoint_model = new BreakPointModel(debug_context, this);
145 breakpoint_list = new QTreeView;
146 breakpoint_list->setRootIsDecorated(false);
147 breakpoint_list->setHeaderHidden(true);
148 breakpoint_list->setModel(breakpoint_model);
149
150 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
151
152 connect(breakpoint_list, &QTreeView::doubleClicked, this,
153 &GraphicsBreakPointsWidget::OnItemDoubleClicked);
154
155 connect(resume_button, &QPushButton::clicked, this,
156 &GraphicsBreakPointsWidget::OnResumeRequested);
157
158 connect(this, &GraphicsBreakPointsWidget::BreakPointHit, this,
159 &GraphicsBreakPointsWidget::OnBreakPointHit, Qt::BlockingQueuedConnection);
160 connect(this, &GraphicsBreakPointsWidget::Resumed, this, &GraphicsBreakPointsWidget::OnResumed);
161
162 connect(this, &GraphicsBreakPointsWidget::BreakPointHit, breakpoint_model,
163 &BreakPointModel::OnBreakPointHit, Qt::BlockingQueuedConnection);
164 connect(this, &GraphicsBreakPointsWidget::Resumed, breakpoint_model,
165 &BreakPointModel::OnResumed);
166
167 connect(this, &GraphicsBreakPointsWidget::BreakPointsChanged,
168 [this](const QModelIndex& top_left, const QModelIndex& bottom_right) {
169 breakpoint_model->dataChanged(top_left, bottom_right);
170 });
171
172 QWidget* main_widget = new QWidget;
173 auto main_layout = new QVBoxLayout;
174 {
175 auto sub_layout = new QHBoxLayout;
176 sub_layout->addWidget(status_text);
177 sub_layout->addWidget(resume_button);
178 main_layout->addLayout(sub_layout);
179 }
180 main_layout->addWidget(breakpoint_list);
181 main_widget->setLayout(main_layout);
182
183 setWidget(main_widget);
184}
185
186void GraphicsBreakPointsWidget::OnMaxwellBreakPointHit(Event event, void* data) {
187 // Process in GUI thread
188 emit BreakPointHit(event, data);
189}
190
191void GraphicsBreakPointsWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
192 status_text->setText(tr("Emulation halted at breakpoint"));
193 resume_button->setEnabled(true);
194}
195
196void GraphicsBreakPointsWidget::OnMaxwellResume() {
197 // Process in GUI thread
198 emit Resumed();
199}
200
201void GraphicsBreakPointsWidget::OnResumed() {
202 status_text->setText(tr("Emulation running"));
203 resume_button->setEnabled(false);
204}
205
206void GraphicsBreakPointsWidget::OnResumeRequested() {
207 if (auto context = context_weak.lock())
208 context->Resume();
209}
210
211void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) {
212 if (!index.isValid())
213 return;
214
215 QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0);
216 QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole);
217 QVariant new_state = Qt::Unchecked;
218 if (enabled == Qt::Unchecked)
219 new_state = Qt::Checked;
220 breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole);
221}
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h
deleted file mode 100644
index a920a2ae5..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.h
+++ /dev/null
@@ -1,45 +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, 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
29signals:
30 void Resumed();
31 void BreakPointHit(Tegra::DebugContext::Event event, void* data);
32 void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
33
34private:
35 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
36 void OnItemDoubleClicked(const QModelIndex&);
37 void OnResumeRequested();
38 void OnResumed();
39
40 QLabel* status_text;
41 QPushButton* resume_button;
42
43 BreakPointModel* breakpoint_model;
44 QTreeView* breakpoint_list;
45};
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 fb488e38f..000000000
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ /dev/null
@@ -1,37 +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<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
28 void OnBreakPointHit(Tegra::DebugContext::Event event);
29 void OnResumed();
30
31private:
32 static QString DebugContextEventToString(Tegra::DebugContext::Event event);
33
34 std::weak_ptr<Tegra::DebugContext> context_weak;
35 bool at_breakpoint;
36 Tegra::DebugContext::Event active_breakpoint;
37};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 867f8e913..b21fbf826 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -93,7 +93,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
93#include "core/perf_stats.h" 93#include "core/perf_stats.h"
94#include "core/settings.h" 94#include "core/settings.h"
95#include "core/telemetry_session.h" 95#include "core/telemetry_session.h"
96#include "video_core/debug_utils/debug_utils.h"
97#include "yuzu/about_dialog.h" 96#include "yuzu/about_dialog.h"
98#include "yuzu/bootmanager.h" 97#include "yuzu/bootmanager.h"
99#include "yuzu/compatdb.h" 98#include "yuzu/compatdb.h"
@@ -101,7 +100,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
101#include "yuzu/configuration/config.h" 100#include "yuzu/configuration/config.h"
102#include "yuzu/configuration/configure_dialog.h" 101#include "yuzu/configuration/configure_dialog.h"
103#include "yuzu/debugger/console.h" 102#include "yuzu/debugger/console.h"
104#include "yuzu/debugger/graphics/graphics_breakpoints.h"
105#include "yuzu/debugger/profiler.h" 103#include "yuzu/debugger/profiler.h"
106#include "yuzu/debugger/wait_tree.h" 104#include "yuzu/debugger/wait_tree.h"
107#include "yuzu/discord.h" 105#include "yuzu/discord.h"
@@ -187,8 +185,6 @@ GMainWindow::GMainWindow()
187 provider(std::make_unique<FileSys::ManualContentProvider>()) { 185 provider(std::make_unique<FileSys::ManualContentProvider>()) {
188 InitializeLogging(); 186 InitializeLogging();
189 187
190 debug_context = Tegra::DebugContext::Construct();
191
192 setAcceptDrops(true); 188 setAcceptDrops(true);
193 ui.setupUi(this); 189 ui.setupUi(this);
194 statusBar()->hide(); 190 statusBar()->hide();
@@ -495,11 +491,6 @@ void GMainWindow::InitializeDebugWidgets() {
495 debug_menu->addAction(microProfileDialog->toggleViewAction()); 491 debug_menu->addAction(microProfileDialog->toggleViewAction());
496#endif 492#endif
497 493
498 graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
499 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
500 graphicsBreakpointsWidget->hide();
501 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
502
503 waitTreeWidget = new WaitTreeWidget(this); 494 waitTreeWidget = new WaitTreeWidget(this);
504 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); 495 addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
505 waitTreeWidget->hide(); 496 waitTreeWidget->hide();
@@ -869,8 +860,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
869 Core::System& system{Core::System::GetInstance()}; 860 Core::System& system{Core::System::GetInstance()};
870 system.SetFilesystem(vfs); 861 system.SetFilesystem(vfs);
871 862
872 system.SetGPUDebugContext(debug_context);
873
874 system.SetAppletFrontendSet({ 863 system.SetAppletFrontendSet({
875 nullptr, // Parental Controls 864 nullptr, // Parental Controls
876 std::make_unique<QtErrorDisplay>(*this), // 865 std::make_unique<QtErrorDisplay>(*this), //
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7f46bea2b..a56f9a981 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -22,7 +22,6 @@ class Config;
22class EmuThread; 22class EmuThread;
23class GameList; 23class GameList;
24class GImageInfo; 24class GImageInfo;
25class GraphicsBreakPointsWidget;
26class GRenderWindow; 25class GRenderWindow;
27class LoadingScreen; 26class LoadingScreen;
28class MicroProfileDialog; 27class MicroProfileDialog;
@@ -42,10 +41,6 @@ class ManualContentProvider;
42class VfsFilesystem; 41class VfsFilesystem;
43} // namespace FileSys 42} // namespace FileSys
44 43
45namespace Tegra {
46class DebugContext;
47}
48
49enum class EmulatedDirectoryTarget { 44enum class EmulatedDirectoryTarget {
50 NAND, 45 NAND,
51 SDMC, 46 SDMC,
@@ -223,8 +218,6 @@ private:
223 218
224 Ui::MainWindow ui; 219 Ui::MainWindow ui;
225 220
226 std::shared_ptr<Tegra::DebugContext> debug_context;
227
228 GRenderWindow* render_window; 221 GRenderWindow* render_window;
229 GameList* game_list; 222 GameList* game_list;
230 LoadingScreen* loading_screen; 223 LoadingScreen* loading_screen;
@@ -255,7 +248,6 @@ private:
255 // Debugger panes 248 // Debugger panes
256 ProfilerWidget* profilerWidget; 249 ProfilerWidget* profilerWidget;
257 MicroProfileDialog* microProfileDialog; 250 MicroProfileDialog* microProfileDialog;
258 GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
259 WaitTreeWidget* waitTreeWidget; 251 WaitTreeWidget* waitTreeWidget;
260 252
261 QAction* actions_recent_files[max_recent_files_item]; 253 QAction* actions_recent_files[max_recent_files_item];