// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include #include #include "video_core/math.h" #include "video_core/pica.h" namespace Pica { class DebugContext { public: enum class Event { FirstEvent = 0, CommandLoaded = FirstEvent, CommandProcessed, IncomingPrimitiveBatch, FinishedPrimitiveBatch, 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); } 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(); } } /** * 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() { } 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; }; /** * 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); /** * 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() { breakpoints.clear(); Resume(); } // TODO: Evaluate if access to these members should be hidden behind a public interface. std::map breakpoints; Event active_breakpoint; bool at_breakpoint = false; 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; /// Used by OnEvent to wait for resumption. std::condition_variable resume_from_breakpoint; /// List of registered observers std::list breakpoint_observers; }; extern std::shared_ptr g_debug_context; // TODO: Get rid of this global namespace DebugUtils { // Simple utility class for dumping geometry data to an OBJ file class GeometryDumper { public: struct Vertex { std::array pos; }; void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2); void Dump(); private: struct Face { int index[3]; }; std::vector vertices; std::vector faces; }; void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, u32 main_offset, const Regs::VSOutputAttributes* output_attributes); // Utility class to log Pica commands. struct PicaTrace { struct Write : public std::pair { Write(u32 id, u32 value) : std::pair(id, value) {} u32& Id() { return first; } const u32& Id() const { return first; } u32& Value() { return second; } const u32& Value() const { return second; } }; std::vector writes; }; void StartPicaTracing(); bool IsPicaTracing(); void OnPicaRegWrite(u32 id, u32 value); std::unique_ptr FinishPicaTracing(); struct TextureInfo { PAddr physical_address; int width; int height; int stride; Pica::Regs::TextureFormat format; static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config, const Pica::Regs::TextureFormat& format); }; const Math::Vec4 LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha = false); void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); void DumpTevStageConfig(const std::array& stages); } // namespace } // namespace