diff options
| author | 2018-03-22 15:19:35 -0500 | |
|---|---|---|
| committer | 2018-03-24 11:31:49 -0500 | |
| commit | 77fd0d47e70968bcbc87a3b5607cd29e6211f656 (patch) | |
| tree | 54e91cede780bbd5bec2612547a61bdd799e36af /src/video_core/debug_utils | |
| parent | GPU: Added a method to unswizzle a texture without decoding it. (diff) | |
| download | yuzu-77fd0d47e70968bcbc87a3b5607cd29e6211f656.tar.gz yuzu-77fd0d47e70968bcbc87a3b5607cd29e6211f656.tar.xz yuzu-77fd0d47e70968bcbc87a3b5607cd29e6211f656.zip | |
Frontend: Ported the GPU breakpoints and surface viewer widgets from citra.
Diffstat (limited to 'src/video_core/debug_utils')
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.cpp | 66 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.h | 165 |
2 files changed, 231 insertions, 0 deletions
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp new file mode 100644 index 000000000..73fd4d7a3 --- /dev/null +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <condition_variable> | ||
| 7 | #include <cstdint> | ||
| 8 | #include <cstring> | ||
| 9 | #include <fstream> | ||
| 10 | #include <map> | ||
| 11 | #include <mutex> | ||
| 12 | #include <string> | ||
| 13 | |||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/bit_field.h" | ||
| 16 | #include "common/color.h" | ||
| 17 | #include "common/common_types.h" | ||
| 18 | #include "common/file_util.h" | ||
| 19 | #include "common/logging/log.h" | ||
| 20 | #include "common/math_util.h" | ||
| 21 | #include "common/vector_math.h" | ||
| 22 | #include "video_core/debug_utils/debug_utils.h" | ||
| 23 | |||
| 24 | namespace Tegra { | ||
| 25 | |||
| 26 | std::shared_ptr<DebugContext> g_debug_context; | ||
| 27 | |||
| 28 | void DebugContext::DoOnEvent(Event event, void* data) { | ||
| 29 | { | ||
| 30 | std::unique_lock<std::mutex> lock(breakpoint_mutex); | ||
| 31 | |||
| 32 | // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will | ||
| 33 | // show on debug widgets | ||
| 34 | |||
| 35 | // TODO: Should stop the CPU thread here once we multithread emulation. | ||
| 36 | |||
| 37 | active_breakpoint = event; | ||
| 38 | at_breakpoint = true; | ||
| 39 | |||
| 40 | // Tell all observers that we hit a breakpoint | ||
| 41 | for (auto& breakpoint_observer : breakpoint_observers) { | ||
| 42 | breakpoint_observer->OnMaxwellBreakPointHit(event, data); | ||
| 43 | } | ||
| 44 | |||
| 45 | // Wait until another thread tells us to Resume() | ||
| 46 | resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; }); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void DebugContext::Resume() { | ||
| 51 | { | ||
| 52 | std::lock_guard<std::mutex> lock(breakpoint_mutex); | ||
| 53 | |||
| 54 | // Tell all observers that we are about to resume | ||
| 55 | for (auto& breakpoint_observer : breakpoint_observers) { | ||
| 56 | breakpoint_observer->OnMaxwellResume(); | ||
| 57 | } | ||
| 58 | |||
| 59 | // Resume the waiting thread (i.e. OnEvent()) | ||
| 60 | at_breakpoint = false; | ||
| 61 | } | ||
| 62 | |||
| 63 | resume_from_breakpoint.notify_one(); | ||
| 64 | } | ||
| 65 | |||
| 66 | } // namespace Tegra | ||
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h new file mode 100644 index 000000000..98461d6d9 --- /dev/null +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <condition_variable> | ||
| 10 | #include <iterator> | ||
| 11 | #include <list> | ||
| 12 | #include <map> | ||
| 13 | #include <memory> | ||
| 14 | #include <mutex> | ||
| 15 | #include <string> | ||
| 16 | #include <utility> | ||
| 17 | #include <vector> | ||
| 18 | #include "common/common_types.h" | ||
| 19 | #include "common/vector_math.h" | ||
| 20 | |||
| 21 | namespace Tegra { | ||
| 22 | |||
| 23 | class DebugContext { | ||
| 24 | public: | ||
| 25 | enum class Event { | ||
| 26 | FirstEvent = 0, | ||
| 27 | |||
| 28 | MaxwellCommandLoaded = FirstEvent, | ||
| 29 | MaxwellCommandProcessed, | ||
| 30 | IncomingPrimitiveBatch, | ||
| 31 | FinishedPrimitiveBatch, | ||
| 32 | |||
| 33 | NumEvents | ||
| 34 | }; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Inherit from this class to be notified of events registered to some debug context. | ||
| 38 | * Most importantly this is used for our debugger GUI. | ||
| 39 | * | ||
| 40 | * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods. | ||
| 41 | * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state | ||
| 42 | * access | ||
| 43 | * @todo Evaluate an alternative interface, in which there is only one managing observer and | ||
| 44 | * multiple child observers running (by design) on the same thread. | ||
| 45 | */ | ||
| 46 | class BreakPointObserver { | ||
| 47 | public: | ||
| 48 | /// Constructs the object such that it observes events of the given DebugContext. | ||
| 49 | BreakPointObserver(std::shared_ptr<DebugContext> debug_context) | ||
| 50 | : context_weak(debug_context) { | ||
| 51 | std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); | ||
| 52 | debug_context->breakpoint_observers.push_back(this); | ||
| 53 | } | ||
| 54 | |||
| 55 | virtual ~BreakPointObserver() { | ||
| 56 | auto context = context_weak.lock(); | ||
| 57 | if (context) { | ||
| 58 | std::unique_lock<std::mutex> lock(context->breakpoint_mutex); | ||
| 59 | context->breakpoint_observers.remove(this); | ||
| 60 | |||
| 61 | // If we are the last observer to be destroyed, tell the debugger context that | ||
| 62 | // it is free to continue. In particular, this is required for a proper yuzu | ||
| 63 | // shutdown, when the emulation thread is waiting at a breakpoint. | ||
| 64 | if (context->breakpoint_observers.empty()) | ||
| 65 | context->Resume(); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Action to perform when a breakpoint was reached. | ||
| 71 | * @param event Type of event which triggered the breakpoint | ||
| 72 | * @param data Optional data pointer (if unused, this is a nullptr) | ||
| 73 | * @note This function will perform nothing unless it is overridden in the child class. | ||
| 74 | */ | ||
| 75 | virtual void OnMaxwellBreakPointHit(Event event, void* data) {} | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Action to perform when emulation is resumed from a breakpoint. | ||
| 79 | * @note This function will perform nothing unless it is overridden in the child class. | ||
| 80 | */ | ||
| 81 | virtual void OnMaxwellResume() {} | ||
| 82 | |||
| 83 | protected: | ||
| 84 | /** | ||
| 85 | * Weak context pointer. This need not be valid, so when requesting a shared_ptr via | ||
| 86 | * context_weak.lock(), always compare the result against nullptr. | ||
| 87 | */ | ||
| 88 | std::weak_ptr<DebugContext> context_weak; | ||
| 89 | }; | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Simple structure defining a breakpoint state | ||
| 93 | */ | ||
| 94 | struct BreakPoint { | ||
| 95 | bool enabled = false; | ||
| 96 | }; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Static constructor used to create a shared_ptr of a DebugContext. | ||
| 100 | */ | ||
| 101 | static std::shared_ptr<DebugContext> Construct() { | ||
| 102 | return std::shared_ptr<DebugContext>(new DebugContext); | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Used by the emulation core when a given event has happened. If a breakpoint has been set | ||
| 107 | * for this event, OnEvent calls the event handlers of the registered breakpoint observers. | ||
| 108 | * The current thread then is halted until Resume() is called from another thread (or until | ||
| 109 | * emulation is stopped). | ||
| 110 | * @param event Event which has happened | ||
| 111 | * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until | ||
| 112 | * Resume() is called. | ||
| 113 | */ | ||
| 114 | void OnEvent(Event event, void* data) { | ||
| 115 | // This check is left in the header to allow the compiler to inline it. | ||
| 116 | if (!breakpoints[(int)event].enabled) | ||
| 117 | return; | ||
| 118 | // For the rest of event handling, call a separate function. | ||
| 119 | DoOnEvent(event, data); | ||
| 120 | } | ||
| 121 | |||
| 122 | void DoOnEvent(Event event, void* data); | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Resume from the current breakpoint. | ||
| 126 | * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. | ||
| 127 | * Calling from any other thread is safe. | ||
| 128 | */ | ||
| 129 | void Resume(); | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Delete all set breakpoints and resume emulation. | ||
| 133 | */ | ||
| 134 | void ClearBreakpoints() { | ||
| 135 | for (auto& bp : breakpoints) { | ||
| 136 | bp.enabled = false; | ||
| 137 | } | ||
| 138 | Resume(); | ||
| 139 | } | ||
| 140 | |||
| 141 | // TODO: Evaluate if access to these members should be hidden behind a public interface. | ||
| 142 | std::array<BreakPoint, (int)Event::NumEvents> breakpoints; | ||
| 143 | Event active_breakpoint; | ||
| 144 | bool at_breakpoint = false; | ||
| 145 | |||
| 146 | private: | ||
| 147 | /** | ||
| 148 | * Private default constructor to make sure people always construct this through Construct() | ||
| 149 | * instead. | ||
| 150 | */ | ||
| 151 | DebugContext() = default; | ||
| 152 | |||
| 153 | /// Mutex protecting current breakpoint state and the observer list. | ||
| 154 | std::mutex breakpoint_mutex; | ||
| 155 | |||
| 156 | /// Used by OnEvent to wait for resumption. | ||
| 157 | std::condition_variable resume_from_breakpoint; | ||
| 158 | |||
| 159 | /// List of registered observers | ||
| 160 | std::list<BreakPointObserver*> breakpoint_observers; | ||
| 161 | }; | ||
| 162 | |||
| 163 | extern std::shared_ptr<DebugContext> g_debug_context; | ||
| 164 | |||
| 165 | } // namespace Tegra | ||