From 8db65723d233486ac98ab9112a81f80b0313c7f7 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Dec 2014 21:55:32 +0100 Subject: Build fix for something which shouldn't have compiled successfully to begin with. --- src/video_core/pica.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/video_core') diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 5fe15a218..10fa73355 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -109,7 +109,7 @@ struct Regs { u32 address; - u32 GetPhysicalAddress() { + u32 GetPhysicalAddress() const { return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; } -- cgit v1.2.3 From 2c71ec70527abd091d69f1fdd30aaf95d815214a Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 25 Oct 2014 18:02:26 +0200 Subject: Pica/DebugUtils: Add breakpoint functionality. --- src/video_core/command_processor.cpp | 13 +++ src/video_core/debug_utils/debug_utils.cpp | 43 ++++++++++ src/video_core/debug_utils/debug_utils.h | 133 +++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) (limited to 'src/video_core') diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 8a6ba2560..298b04c51 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -34,6 +34,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { u32 old_value = registers[id]; registers[id] = (old_value & ~mask) | (value & mask); + if (g_debug_context) + g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast(&id)); + DebugUtils::OnPicaRegWrite(id, registers[id]); switch(id) { @@ -43,6 +46,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { { DebugUtils::DumpTevStageConfig(registers.GetTevStages()); + if (g_debug_context) + g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); + const auto& attribute_config = registers.vertex_attributes; const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); @@ -132,6 +138,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); } geometry_dumper.Dump(); + + if (g_debug_context) + g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr); + break; } @@ -229,6 +239,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { default: break; } + + if (g_debug_context) + g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast(&id)); } static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 8a5f11424..11f87d988 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include +#include +#include #include #include #include @@ -12,6 +14,7 @@ #include #endif +#include "common/log.h" #include "common/file_util.h" #include "video_core/pica.h" @@ -20,6 +23,46 @@ namespace Pica { +void DebugContext::OnEvent(Event event, void* data) { + if (!breakpoints[event].enabled) + return; + + { + std::unique_lock lock(breakpoint_mutex); + + // TODO: Should stop the CPU thread here once we multithread emulation. + + active_breakpoint = event; + at_breakpoint = true; + + // Tell all observers that we hit a breakpoint + for (auto& breakpoint_observer : breakpoint_observers) { + breakpoint_observer->OnPicaBreakPointHit(event, data); + } + + // Wait until another thread tells us to Resume() + resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; }); + } +} + +void DebugContext::Resume() { + { + std::unique_lock lock(breakpoint_mutex); + + // Tell all observers that we are about to resume + for (auto& breakpoint_observer : breakpoint_observers) { + breakpoint_observer->OnPicaResume(); + } + + // Resume the waiting thread (i.e. OnEvent()) + at_breakpoint = false; + } + + resume_from_breakpoint.notify_one(); +} + +std::shared_ptr g_debug_context; // TODO: Get rid of this global + namespace DebugUtils { void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index b1558cfae..26b26e22f 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -5,13 +5,146 @@ #pragma once #include +#include +#include +#include #include +#include #include #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 -- cgit v1.2.3 From fd194d95b0f1522b970a2b77f9ea490fb8c3ef08 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 24 Aug 2014 14:39:52 +0200 Subject: citra-qt: Add texture viewer to Pica command list. The texture viewer is enabled when selecting a write command to one of the texture config registers. --- src/video_core/debug_utils/debug_utils.cpp | 57 +++++++++++++++++++----------- src/video_core/debug_utils/debug_utils.h | 9 +++++ 2 files changed, 45 insertions(+), 21 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 11f87d988..59909c827 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include + #include #include #include @@ -17,6 +19,7 @@ #include "common/log.h" #include "common/file_util.h" +#include "video_core/math.h" #include "video_core/pica.h" #include "debug_utils.h" @@ -355,6 +358,30 @@ std::unique_ptr FinishPicaTracing() return std::move(ret); } +const Math::Vec4 LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { + assert(info.format == Pica::Regs::TextureFormat::RGB8); + + // Cf. rasterizer code for an explanation of this algorithm. + int texel_index_within_tile = 0; + for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { + int sub_tile_width = 1 << block_size_index; + int sub_tile_height = 1 << block_size_index; + + int sub_tile_index = (x & sub_tile_width) << block_size_index; + sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); + texel_index_within_tile += sub_tile_index; + } + + const int block_width = 8; + const int block_height = 8; + + int coarse_x = (x / block_width) * block_width; + int coarse_y = (y / block_height) * block_height; + + const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; + return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; +} + void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { // NOTE: Permanently enabling this just trashes hard disks for no reason. // Hence, this is currently disabled. @@ -420,27 +447,15 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { buf = new u8[row_stride * texture_config.height]; for (unsigned y = 0; y < texture_config.height; ++y) { for (unsigned x = 0; x < texture_config.width; ++x) { - // Cf. rasterizer code for an explanation of this algorithm. - int texel_index_within_tile = 0; - for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { - int sub_tile_width = 1 << block_size_index; - int sub_tile_height = 1 << block_size_index; - - int sub_tile_index = (x & sub_tile_width) << block_size_index; - sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); - texel_index_within_tile += sub_tile_index; - } - - const int block_width = 8; - const int block_height = 8; - - int coarse_x = (x / block_width) * block_width; - int coarse_y = (y / block_height) * block_height; - - u8* source_ptr = (u8*)data + coarse_x * block_height * 3 + coarse_y * row_stride + texel_index_within_tile * 3; - buf[3 * x + y * row_stride ] = source_ptr[2]; - buf[3 * x + y * row_stride + 1] = source_ptr[1]; - buf[3 * x + y * row_stride + 2] = source_ptr[0]; + TextureInfo info; + info.width = texture_config.width; + info.height = texture_config.height; + info.stride = row_stride; + info.format = registers.texture0_format; + Math::Vec4 texture_color = LookupTexture(data, x, y, info); + buf[3 * x + y * row_stride ] = texture_color.r(); + buf[3 * x + y * row_stride + 1] = texture_color.g(); + buf[3 * x + y * row_stride + 2] = texture_color.b(); } } diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 26b26e22f..bad4c919a 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -12,6 +12,7 @@ #include #include +#include "video_core/math.h" #include "video_core/pica.h" namespace Pica { @@ -190,6 +191,14 @@ bool IsPicaTracing(); void OnPicaRegWrite(u32 id, u32 value); std::unique_ptr FinishPicaTracing(); +struct TextureInfo { + int width; + int height; + int stride; + Pica::Regs::TextureFormat format; +}; + +const Math::Vec4 LookupTexture(const u8* source, int x, int y, const TextureInfo& info); void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); void DumpTevStageConfig(const std::array& stages); -- cgit v1.2.3 From 2793619dcef9fb2f97db5f0258ca950e18fe7f13 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 24 Aug 2014 17:23:02 +0200 Subject: citra_qt: Add enhanced texture debugging widgets. Double-clicking a texture parameter command in the pica command lists will spawn these as a new tab in the pica command list dock area. --- src/video_core/debug_utils/debug_utils.cpp | 12 ++++++++++++ src/video_core/debug_utils/debug_utils.h | 4 ++++ src/video_core/pica.h | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'src/video_core') diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 59909c827..31ce09faf 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -382,6 +382,18 @@ const Math::Vec4 LookupTexture(const u8* source, int x, int y, const Texture return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; } +TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, + const Regs::TextureFormat& format) +{ + TextureInfo info; + info.address = config.GetPhysicalAddress(); + info.width = config.width; + info.height = config.height; + info.format = format; + info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width; + return info; +} + void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { // NOTE: Permanently enabling this just trashes hard disks for no reason. // Hence, this is currently disabled. diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index bad4c919a..51f14f12f 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -192,10 +192,14 @@ void OnPicaRegWrite(u32 id, u32 value); std::unique_ptr FinishPicaTracing(); struct TextureInfo { + unsigned int 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); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 10fa73355..c1f35a011 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -130,7 +130,20 @@ struct Regs { // Seems like they are luminance formats and compressed textures. }; - BitField<0, 1, u32> texturing_enable; + static unsigned BytesPerPixel(TextureFormat format) { + if (format == TextureFormat::RGBA8) + return 4; + else if (format == TextureFormat::RGB8) + return 3; + else if (format == TextureFormat::RGBA5551 || + format == TextureFormat::RGB565 || + format == TextureFormat::RGBA4) + return 2; + else // placeholder + return 1; + } + + BitField< 0, 1, u32> texturing_enable; TextureConfig texture0; INSERT_PADDING_WORDS(0x8); BitField<0, 4, TextureFormat> texture0_format; -- cgit v1.2.3 From 0cd27a511ecd170484b672263c09192b579e31ac Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Dec 2014 19:41:03 +0100 Subject: Some code cleanup. --- src/video_core/debug_utils/debug_utils.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 31ce09faf..71b03f31c 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 // Refer to the license.txt file included. -#include - #include #include #include @@ -359,7 +357,7 @@ std::unique_ptr FinishPicaTracing() } const Math::Vec4 LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { - assert(info.format == Pica::Regs::TextureFormat::RGB8); + _dbg_assert_(GPU, info.format == Pica::Regs::TextureFormat::RGB8); // Cf. rasterizer code for an explanation of this algorithm. int texel_index_within_tile = 0; -- cgit v1.2.3 From 79bb403089ff91c24b8356ad8d5bc5f7666a0d11 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Dec 2014 20:41:01 +0100 Subject: More coding style fixes. --- src/video_core/pica.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/pica.h b/src/video_core/pica.h index c1f35a011..b463a32ef 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -131,16 +131,22 @@ struct Regs { }; static unsigned BytesPerPixel(TextureFormat format) { - if (format == TextureFormat::RGBA8) + switch (format) { + case TextureFormat::RGBA8: return 4; - else if (format == TextureFormat::RGB8) + + case TextureFormat::RGB8: return 3; - else if (format == TextureFormat::RGBA5551 || - format == TextureFormat::RGB565 || - format == TextureFormat::RGBA4) + + case TextureFormat::RGBA5551: + case TextureFormat::RGB565: + case TextureFormat::RGBA4: return 2; - else // placeholder + + default: + // placeholder for yet unknown formats return 1; + } } BitField< 0, 1, u32> texturing_enable; -- cgit v1.2.3 From 0305435edd9d6de3a2b245d60bcbf339d6dda2ff Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Dec 2014 20:42:53 +0100 Subject: Pica: Re-enable command names on MSVC. The affected code is no longer limited by compiler support on that platform. --- src/video_core/pica.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/video_core') diff --git a/src/video_core/pica.h b/src/video_core/pica.h index b463a32ef..e7ca38978 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -536,10 +536,6 @@ struct Regs { static std::string GetCommandName(int index) { std::map map; - // TODO: MSVC does not support using offsetof() on non-static data members even though this - // is technically allowed since C++11. Hence, this functionality is disabled until - // MSVC properly supports it. - #ifndef _MSC_VER Regs regs; #define ADD_FIELD(name) \ do { \ @@ -576,7 +572,6 @@ struct Regs { ADD_FIELD(vs_swizzle_patterns); #undef ADD_FIELD - #endif // _MSC_VER // Return empty string if no match is found return map[index]; -- cgit v1.2.3