diff options
Diffstat (limited to 'src')
23 files changed, 92 insertions, 652 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 | ||
| 583 | void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { | ||
| 584 | impl->debug_context = std::move(context); | ||
| 585 | } | ||
| 586 | |||
| 587 | Tegra::DebugContext* System::GetGPUDebugContext() const { | ||
| 588 | return impl->debug_context.get(); | ||
| 589 | } | ||
| 590 | |||
| 591 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 581 | void 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 | ||
| 13 | namespace Service::NIFM { | 14 | namespace 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/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 13f9848bc..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 |
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 | |||
| 9 | namespace Tegra { | ||
| 10 | |||
| 11 | void 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 | |||
| 33 | void 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 | |||
| 13 | namespace Tegra { | ||
| 14 | |||
| 15 | class DebugContext { | ||
| 16 | public: | ||
| 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 | |||
| 140 | private: | ||
| 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 | ||
| 275 | void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | 274 | void 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 | ||
| 433 | void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { | 422 | void 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/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 | ||
| 140 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | 154 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, |
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 { | |||
| 392 | using Meta = | 392 | using Meta = |
| 393 | std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>; | 393 | std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>; |
| 394 | 394 | ||
| 395 | class AmendNode { | ||
| 396 | public: | ||
| 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 | |||
| 412 | private: | ||
| 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 |
| 396 | class OperationNode final { | 418 | class OperationNode final : public AmendNode { |
| 397 | public: | 419 | public: |
| 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 |
| 436 | class ConditionalNode final { | 458 | class ConditionalNode final : public AmendNode { |
| 437 | public: | 459 | public: |
| 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 | ||
| 449 | std::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 | |||
| 179 | private: | 183 | private: |
| 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 | |||
| 8 | BreakPointObserverDock::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 | |||
| 21 | void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||
| 22 | emit BreakPointHit(event, data); | ||
| 23 | } | ||
| 24 | |||
| 25 | void 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 | */ | ||
| 15 | class BreakPointObserverDock : public QDockWidget, | ||
| 16 | protected Tegra::DebugContext::BreakPointObserver { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 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 | |||
| 26 | signals: | ||
| 27 | void Resumed(); | ||
| 28 | void BreakPointHit(Tegra::DebugContext::Event event, void* data); | ||
| 29 | |||
| 30 | private: | ||
| 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 | |||
| 14 | BreakPointModel::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 | |||
| 20 | int BreakPointModel::columnCount(const QModelIndex& parent) const { | ||
| 21 | return 1; | ||
| 22 | } | ||
| 23 | |||
| 24 | int BreakPointModel::rowCount(const QModelIndex& parent) const { | ||
| 25 | return static_cast<int>(Tegra::DebugContext::Event::NumEvents); | ||
| 26 | } | ||
| 27 | |||
| 28 | QVariant 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 | |||
| 63 | Qt::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 | |||
| 73 | bool 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 | |||
| 95 | void 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 | |||
| 106 | void 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 | |||
| 117 | QString 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 | |||
| 134 | GraphicsBreakPointsWidget::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 | |||
| 186 | void GraphicsBreakPointsWidget::OnMaxwellBreakPointHit(Event event, void* data) { | ||
| 187 | // Process in GUI thread | ||
| 188 | emit BreakPointHit(event, data); | ||
| 189 | } | ||
| 190 | |||
| 191 | void GraphicsBreakPointsWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||
| 192 | status_text->setText(tr("Emulation halted at breakpoint")); | ||
| 193 | resume_button->setEnabled(true); | ||
| 194 | } | ||
| 195 | |||
| 196 | void GraphicsBreakPointsWidget::OnMaxwellResume() { | ||
| 197 | // Process in GUI thread | ||
| 198 | emit Resumed(); | ||
| 199 | } | ||
| 200 | |||
| 201 | void GraphicsBreakPointsWidget::OnResumed() { | ||
| 202 | status_text->setText(tr("Emulation running")); | ||
| 203 | resume_button->setEnabled(false); | ||
| 204 | } | ||
| 205 | |||
| 206 | void GraphicsBreakPointsWidget::OnResumeRequested() { | ||
| 207 | if (auto context = context_weak.lock()) | ||
| 208 | context->Resume(); | ||
| 209 | } | ||
| 210 | |||
| 211 | void 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 | |||
| 11 | class QLabel; | ||
| 12 | class QPushButton; | ||
| 13 | class QTreeView; | ||
| 14 | |||
| 15 | class BreakPointModel; | ||
| 16 | |||
| 17 | class GraphicsBreakPointsWidget : public QDockWidget, Tegra::DebugContext::BreakPointObserver { | ||
| 18 | Q_OBJECT | ||
| 19 | |||
| 20 | using Event = Tegra::DebugContext::Event; | ||
| 21 | |||
| 22 | public: | ||
| 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 | |||
| 29 | signals: | ||
| 30 | void Resumed(); | ||
| 31 | void BreakPointHit(Tegra::DebugContext::Event event, void* data); | ||
| 32 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||
| 33 | |||
| 34 | private: | ||
| 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 | |||
| 11 | class BreakPointModel : public QAbstractListModel { | ||
| 12 | Q_OBJECT | ||
| 13 | |||
| 14 | public: | ||
| 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 | |||
| 31 | private: | ||
| 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; | |||
| 22 | class EmuThread; | 22 | class EmuThread; |
| 23 | class GameList; | 23 | class GameList; |
| 24 | class GImageInfo; | 24 | class GImageInfo; |
| 25 | class GraphicsBreakPointsWidget; | ||
| 26 | class GRenderWindow; | 25 | class GRenderWindow; |
| 27 | class LoadingScreen; | 26 | class LoadingScreen; |
| 28 | class MicroProfileDialog; | 27 | class MicroProfileDialog; |
| @@ -42,10 +41,6 @@ class ManualContentProvider; | |||
| 42 | class VfsFilesystem; | 41 | class VfsFilesystem; |
| 43 | } // namespace FileSys | 42 | } // namespace FileSys |
| 44 | 43 | ||
| 45 | namespace Tegra { | ||
| 46 | class DebugContext; | ||
| 47 | } | ||
| 48 | |||
| 49 | enum class EmulatedDirectoryTarget { | 44 | enum 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]; |