From 8aa28825db955232e562af6ad706c76c625138ba Mon Sep 17 00:00:00 2001 From: GASTER Date: Sat, 8 Oct 2016 21:33:21 -0500 Subject: [PATCH] Search bar for GPU Commands --- src/citra_qt/debugger/graphics_cmdlists.cpp | 26 +- src/citra_qt/debugger/graphics_cmdlists.h | 8 +- src/video_core/debug_utils/debug_utils.h | 438 +++++++++++--------- 3 files changed, 263 insertions(+), 209 deletions(-) diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 8a784d108..942d639e8 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "citra_qt/debugger/graphics_cmdlists.h" #include "citra_qt/util/spinbox.h" #include "citra_qt/util/util.h" @@ -20,6 +21,8 @@ #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica.h" #include "video_core/pica_state.h" +#include +using namespace std; QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); @@ -51,7 +54,7 @@ public: GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {} int GPUCommandListModel::rowCount(const QModelIndex& parent) const { - return static_cast(pica_trace.writes.size()); + return static_cast((pica_trace.isSearchCorrected ? pica_trace.search_corrected_writes : pica_trace.writes).size()); } int GPUCommandListModel::columnCount(const QModelIndex& parent) const { @@ -62,7 +65,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); - const auto& write = pica_trace.writes[index.row()]; + const auto& write = (pica_trace.isSearchCorrected ? pica_trace.search_corrected_writes[index.row()] : pica_trace.writes[index.row()]); if (role == Qt::DisplayRole) { QString content; @@ -112,6 +115,16 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& endResetModel(); } +void GPUCommandListModel::UpdatePicaTrace(const QString& str) { + + pica_trace.CorrectSearchWrites(str.toStdString()); + cout << "Updated writes"; + + beginResetModel(); + + endResetModel(); +} + #define COMMAND_IN_RANGE(cmd_id, reg_name) \ (cmd_id >= PICA_REG_INDEX(reg_name) && \ cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::g_state.regs.reg_name)) / 4) @@ -218,7 +231,14 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) sub_layout->addWidget(copy_all); main_layout->addLayout(sub_layout); } - main_widget->setLayout(main_layout); + + search_bar = new QLineEdit(); + search_bar->setPlaceholderText("Search Bar"); + main_layout->addWidget(search_bar); + + connect(search_bar, SIGNAL(textChanged(const QString &)), model, SLOT(UpdatePicaTrace(const QString &))); + + main_widget->setLayout(main_layout); setWidget(main_widget); } diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h index fa2b9122b..5139d6a81 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics_cmdlists.h @@ -27,9 +27,10 @@ public: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - + public slots: void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); + void UpdatePicaTrace(const QString &); private: Pica::DebugUtils::PicaTrace pica_trace; @@ -41,13 +42,14 @@ class GPUCommandListWidget : public QDockWidget { public: GPUCommandListWidget(QWidget* parent = nullptr); + QLineEdit* search_bar; + public slots: void OnToggleTracing(); void OnCommandDoubleClicked(const QModelIndex&); - void SetCommandInfo(const QModelIndex&); - void CopyAllToClipboard(); + void CopyAllToClipboard(); signals: void TracingFinished(const Pica::DebugUtils::PicaTrace&); diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 189c93abb..cc33ec8df 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -20,251 +20,283 @@ #include "video_core/pica.h" namespace CiTrace { -class Recorder; + class Recorder; } namespace Pica { -namespace Shader { -struct ShaderSetup; -} + namespace Shader { + struct ShaderSetup; + } -class DebugContext { -public: - enum class Event { - FirstEvent = 0, + class DebugContext { + public: + enum class Event { + FirstEvent = 0, - PicaCommandLoaded = FirstEvent, - PicaCommandProcessed, - IncomingPrimitiveBatch, - FinishedPrimitiveBatch, - VertexShaderInvocation, - IncomingDisplayTransfer, - GSPCommandProcessed, - BufferSwapped, + PicaCommandLoaded = FirstEvent, + PicaCommandProcessed, + IncomingPrimitiveBatch, + FinishedPrimitiveBatch, + VertexShaderInvocation, + IncomingDisplayTransfer, + GSPCommandProcessed, + BufferSwapped, - NumEvents - }; + NumEvents + }; - /** - * Inherit from this class to be notified of events registered to some debug context. - * Most importantly this is used for our debugger GUI. - * - * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods. - * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state - * access - * @todo Evaluate an alternative interface, in which there is only one managing observer and - * multiple child observers running (by design) on the same thread. - */ - class BreakPointObserver { - public: - /// Constructs the object such that it observes events of the given DebugContext. - BreakPointObserver(std::shared_ptr debug_context) - : context_weak(debug_context) { - std::unique_lock lock(debug_context->breakpoint_mutex); - debug_context->breakpoint_observers.push_back(this); - } + /** + * Inherit from this class to be notified of events registered to some debug context. + * Most importantly this is used for our debugger GUI. + * + * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods. + * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state + * access + * @todo Evaluate an alternative interface, in which there is only one managing observer and + * multiple child observers running (by design) on the same thread. + */ + class BreakPointObserver { + public: + /// Constructs the object such that it observes events of the given DebugContext. + BreakPointObserver(std::shared_ptr debug_context) + : context_weak(debug_context) { + std::unique_lock lock(debug_context->breakpoint_mutex); + debug_context->breakpoint_observers.push_back(this); + } - virtual ~BreakPointObserver() { - auto context = context_weak.lock(); - if (context) { - std::unique_lock lock(context->breakpoint_mutex); - context->breakpoint_observers.remove(this); + virtual ~BreakPointObserver() { + auto context = context_weak.lock(); + if (context) { + std::unique_lock lock(context->breakpoint_mutex); + context->breakpoint_observers.remove(this); - // If we are the last observer to be destroyed, tell the debugger context that - // it is free to continue. In particular, this is required for a proper Citra - // shutdown, when the emulation thread is waiting at a breakpoint. - if (context->breakpoint_observers.empty()) - context->Resume(); - } - } + // If we are the last observer to be destroyed, tell the debugger context that + // it is free to continue. In particular, this is required for a proper Citra + // shutdown, when the emulation thread is waiting at a breakpoint. + if (context->breakpoint_observers.empty()) + context->Resume(); + } + } - /** - * Action to perform when a breakpoint was reached. - * @param event Type of event which triggered the breakpoint - * @param data Optional data pointer (if unused, this is a nullptr) - * @note This function will perform nothing unless it is overridden in the child class. - */ - virtual void OnPicaBreakPointHit(Event, void*) {} + /** + * Action to perform when a breakpoint was reached. + * @param event Type of event which triggered the breakpoint + * @param data Optional data pointer (if unused, this is a nullptr) + * @note This function will perform nothing unless it is overridden in the child class. + */ + virtual void OnPicaBreakPointHit(Event, void*) {} - /** - * Action to perform when emulation is resumed from a breakpoint. - * @note This function will perform nothing unless it is overridden in the child class. - */ - virtual void OnPicaResume() {} + /** + * Action to perform when emulation is resumed from a breakpoint. + * @note This function will perform nothing unless it is overridden in the child class. + */ + virtual void OnPicaResume() {} - protected: - /** - * Weak context pointer. This need not be valid, so when requesting a shared_ptr via - * context_weak.lock(), always compare the result against nullptr. - */ - std::weak_ptr context_weak; - }; + protected: + /** + * Weak context pointer. This need not be valid, so when requesting a shared_ptr via + * context_weak.lock(), always compare the result against nullptr. + */ + std::weak_ptr context_weak; + }; - /** - * Simple structure defining a breakpoint state - */ - struct BreakPoint { - bool enabled = false; - }; + /** + * Simple structure defining a breakpoint state + */ + struct BreakPoint { + bool enabled = false; + }; - /** - * Static constructor used to create a shared_ptr of a DebugContext. - */ - static std::shared_ptr Construct() { - return std::shared_ptr(new DebugContext); - } + /** + * Static constructor used to create a shared_ptr of a DebugContext. + */ + static std::shared_ptr Construct() { + return std::shared_ptr(new DebugContext); + } - /** - * Used by the emulation core when a given event has happened. If a breakpoint has been set - * for this event, OnEvent calls the event handlers of the registered breakpoint observers. - * The current thread then is halted until Resume() is called from another thread (or until - * emulation is stopped). - * @param event Event which has happened - * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until - * Resume() is called. - */ - void OnEvent(Event event, void* data) { - // This check is left in the header to allow the compiler to inline it. - if (!breakpoints[(int)event].enabled) - return; - // For the rest of event handling, call a separate function. - DoOnEvent(event, data); - } + /** + * Used by the emulation core when a given event has happened. If a breakpoint has been set + * for this event, OnEvent calls the event handlers of the registered breakpoint observers. + * The current thread then is halted until Resume() is called from another thread (or until + * emulation is stopped). + * @param event Event which has happened + * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until + * Resume() is called. + */ + void OnEvent(Event event, void* data) { + // This check is left in the header to allow the compiler to inline it. + if (!breakpoints[(int)event].enabled) + return; + // For the rest of event handling, call a separate function. + DoOnEvent(event, data); + } - void DoOnEvent(Event event, void* data); + void DoOnEvent(Event event, void* data); - /** - * Resume from the current breakpoint. - * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. - * Calling from any other thread is safe. - */ - void Resume(); + /** + * Resume from the current breakpoint. + * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. + * Calling from any other thread is safe. + */ + void Resume(); - /** - * Delete all set breakpoints and resume emulation. - */ - void ClearBreakpoints() { - for (auto& bp : breakpoints) { - bp.enabled = false; - } - Resume(); - } + /** + * Delete all set breakpoints and resume emulation. + */ + void ClearBreakpoints() { + for (auto& bp : breakpoints) { + bp.enabled = false; + } + Resume(); + } - // TODO: Evaluate if access to these members should be hidden behind a public interface. - std::array breakpoints; - Event active_breakpoint; - bool at_breakpoint = false; + // TODO: Evaluate if access to these members should be hidden behind a public interface. + std::array breakpoints; + Event active_breakpoint; + bool at_breakpoint = false; - std::shared_ptr recorder = nullptr; + std::shared_ptr recorder = nullptr; -private: - /** - * Private default constructor to make sure people always construct this through Construct() - * instead. - */ - DebugContext() = default; + private: + /** + * Private default constructor to make sure people always construct this through Construct() + * instead. + */ + DebugContext() = default; - /// Mutex protecting current breakpoint state and the observer list. - std::mutex breakpoint_mutex; + /// Mutex protecting current breakpoint state and the observer list. + std::mutex breakpoint_mutex; - /// Used by OnEvent to wait for resumption. - std::condition_variable resume_from_breakpoint; + /// Used by OnEvent to wait for resumption. + std::condition_variable resume_from_breakpoint; - /// List of registered observers - std::list breakpoint_observers; -}; + /// List of registered observers + std::list breakpoint_observers; + }; -extern std::shared_ptr g_debug_context; // TODO: Get rid of this global + extern std::shared_ptr g_debug_context; // TODO: Get rid of this global -namespace DebugUtils { + namespace DebugUtils { #define PICA_DUMP_TEXTURES 0 #define PICA_LOG_TEV 0 -void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, - const Shader::ShaderSetup& setup, - const Regs::VSOutputAttributes* output_attributes); + void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, + const Shader::ShaderSetup& setup, + const Regs::VSOutputAttributes* output_attributes); -// Utility class to log Pica commands. -struct PicaTrace { - struct Write { - u16 cmd_id; - u16 mask; - u32 value; - }; - std::vector writes; -}; + // Utility class to log Pica commands. + struct PicaTrace { + struct Write { + u16 cmd_id; + u16 mask; + u32 value; + }; + std::vector writes; + std::vector search_corrected_writes; + bool isSearchCorrected = false; + void CorrectSearchWrites(std::string search_string) { -void StartPicaTracing(); -bool IsPicaTracing(); -void OnPicaRegWrite(PicaTrace::Write write); -std::unique_ptr FinishPicaTracing(); + search_corrected_writes.clear(); -struct TextureInfo { - PAddr physical_address; - int width; - int height; - int stride; - Pica::Regs::TextureFormat format; + if (search_string.size() == 0) + isSearchCorrected = false; + else isSearchCorrected = true; - static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, - const Pica::Regs::TextureFormat& format); -}; + for (int i = 0; i < writes.size(); i++) { + bool isValid = true; -/** - * Lookup texel located at the given coordinates and return an RGBA vector of its color. - * @param source Source pointer to read data from - * @param s,t Texture coordinates to read from - * @param info TextureInfo object describing the texture setup - * @param disable_alpha This is used for debug widgets which use this method to display textures - * without providing a good way to visualize alpha by themselves. If true, this will return 255 for - * the alpha component, and either drop the information entirely or store it in an "unused" color - * channel. - * @todo Eventually we should get rid of the disable_alpha parameter. - */ -const Math::Vec4 LookupTexture(const u8* source, int s, int t, const TextureInfo& info, - bool disable_alpha = false); + std::string cmdName = Pica::Regs::GetCommandName(writes[i].cmd_id); -void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); + if (cmdName.size() < search_string.size()) + continue; -std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage); -std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage); + for (int j = 0; j < search_string.size(); j++) { + if (cmdName.at(j) != search_string.at(j)) { + isValid = false; + break; + } + } -/// Dumps the Tev stage config to log at trace level -void DumpTevStageConfig(const std::array& stages); + if (!isValid) + continue; -/** - * Used in the vertex loader to merge access records. TODO: Investigate if actually useful. - */ -class MemoryAccessTracker { - /// Combine overlapping and close ranges - void SimplifyRanges() { - for (auto it = ranges.begin(); it != ranges.end(); ++it) { - // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, - // too - auto it2 = std::next(it); - while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) { - it->second = std::max(it->second, it2->first + it2->second - it->first); - it2 = ranges.erase(it2); - } - } - } + search_corrected_writes.push_back(writes[i]); + } -public: - /// Record a particular memory access in the list - void AddAccess(u32 paddr, u32 size) { - // Create new range or extend existing one - ranges[paddr] = std::max(ranges[paddr], size); + } + }; - // Simplify ranges... - SimplifyRanges(); - } + void StartPicaTracing(); + bool IsPicaTracing(); + void OnPicaRegWrite(PicaTrace::Write write); + std::unique_ptr FinishPicaTracing(); - /// Map of accessed ranges (mapping start address to range size) - std::map ranges; -}; + struct TextureInfo { + PAddr physical_address; + int width; + int height; + int stride; + Pica::Regs::TextureFormat format; -} // namespace + static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, + const Pica::Regs::TextureFormat& format); + }; -} // namespace + /** + * Lookup texel located at the given coordinates and return an RGBA vector of its color. + * @param source Source pointer to read data from + * @param s,t Texture coordinates to read from + * @param info TextureInfo object describing the texture setup + * @param disable_alpha This is used for debug widgets which use this method to display textures + * without providing a good way to visualize alpha by themselves. If true, this will return 255 for + * the alpha component, and either drop the information entirely or store it in an "unused" color + * channel. + * @todo Eventually we should get rid of the disable_alpha parameter. + */ + const Math::Vec4 LookupTexture(const u8* source, int s, int t, const TextureInfo& info, + bool disable_alpha = false); + + void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); + + std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage); + std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage); + + /// Dumps the Tev stage config to log at trace level + void DumpTevStageConfig(const std::array& stages); + + /** + * Used in the vertex loader to merge access records. TODO: Investigate if actually useful. + */ + class MemoryAccessTracker { + /// Combine overlapping and close ranges + void SimplifyRanges() { + for (auto it = ranges.begin(); it != ranges.end(); ++it) { + // NOTE: We add 32 to the range end address to make sure "close" ranges are combined, + // too + auto it2 = std::next(it); + while (it2 != ranges.end() && it->first + it->second + 32 >= it2->first) { + it->second = std::max(it->second, it2->first + it2->second - it->first); + it2 = ranges.erase(it2); + } + } + } + + public: + /// Record a particular memory access in the list + void AddAccess(u32 paddr, u32 size) { + // Create new range or extend existing one + ranges[paddr] = std::max(ranges[paddr], size); + + // Simplify ranges... + SimplifyRanges(); + } + + /// Map of accessed ranges (mapping start address to range size) + std::map ranges; + }; + + } // namespace + +} // namespace \ No newline at end of file