diff options
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/clipper.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/command_processor.cpp | 30 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.cpp | 138 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.h | 148 | ||||
| -rw-r--r-- | src/video_core/gpu_debugger.h | 2 | ||||
| -rw-r--r-- | src/video_core/pica.h | 42 | ||||
| -rw-r--r-- | src/video_core/primitive_assembly.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/rasterizer.cpp | 12 | ||||
| -rw-r--r-- | src/video_core/renderer_base.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_util.cpp | 34 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 45 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 11 | ||||
| -rw-r--r-- | src/video_core/utils.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/utils.h | 20 | ||||
| -rw-r--r-- | src/video_core/vertex_shader.cpp | 22 | ||||
| -rw-r--r-- | src/video_core/vertex_shader.h | 2 | ||||
| -rw-r--r-- | src/video_core/video_core.cpp | 8 |
17 files changed, 417 insertions, 111 deletions
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index 96d3dabe2..632fb959a 100644 --- a/src/video_core/clipper.cpp +++ b/src/video_core/clipper.cpp @@ -157,12 +157,12 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { InitScreenCoordinates(vtx2); - DEBUG_LOG(GPU, + LOG_TRACE(Render_Software, "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " - "(%.3lu, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " + "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", i,output_list.size(), buffer_vertices.size(), - vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(), + vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 1ec727698..b74cd3261 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -8,6 +8,7 @@ #include "pica.h" #include "primitive_assembly.h" #include "vertex_shader.h" +#include "core/hle/service/gsp_gpu.h" #include "debug_utils/debug_utils.h" @@ -34,15 +35,26 @@ 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<void*>(&id)); + DebugUtils::OnPicaRegWrite(id, registers[id]); switch(id) { + // Trigger IRQ + case PICA_REG_INDEX(trigger_irq): + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); + return; + // It seems like these trigger vertex rendering case PICA_REG_INDEX(trigger_draw): case PICA_REG_INDEX(trigger_draw_indexed): { 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()); @@ -60,7 +72,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { const u8* load_address = base_address + loader_config.data_offset; // TODO: What happens if a loader overwrites a previous one's data? - for (int component = 0; component < loader_config.component_count; ++component) { + for (unsigned component = 0; component < loader_config.component_count; ++component) { u32 attribute_index = loader_config.GetComponent(component); vertex_attribute_sources[attribute_index] = load_address; vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); @@ -102,7 +114,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : *(float*)srcdata; input.attr[i][comp] = float24::FromFloat32(srcval); - DEBUG_LOG(GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", + LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", comp, i, vertex, index, attribute_config.GetBaseAddress(), vertex_attribute_sources[i] - base_address, @@ -132,6 +144,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; } @@ -160,7 +176,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index); if (uniform_setup.index > 95) { - ERROR_LOG(GPU, "Invalid VS uniform index %d", (int)uniform_setup.index); + LOG_ERROR(HW_GPU, "Invalid VS uniform index %d", (int)uniform_setup.index); break; } @@ -176,7 +192,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); } - DEBUG_LOG(GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, + LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(), uniform.w.ToFloat32()); @@ -229,6 +245,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<void*>(&id)); } static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { @@ -259,8 +278,9 @@ static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { void ProcessCommandList(const u32* list, u32 size) { u32* read_pointer = (u32*)list; + u32 list_length = size / sizeof(u32); - while (read_pointer < list + size) { + while (read_pointer < list + list_length) { read_pointer += ExecuteCommandBlock(read_pointer); } } diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 22b8e9950..1a20f19ec 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 <algorithm> +#include <condition_variable> +#include <list> #include <map> #include <fstream> #include <mutex> @@ -12,14 +14,56 @@ #include <png.h> #endif +#include "common/log.h" #include "common/file_util.h" +#include "video_core/math.h" #include "video_core/pica.h" #include "debug_utils.h" namespace Pica { +void DebugContext::OnEvent(Event event, void* data) { + if (!breakpoints[event].enabled) + return; + + { + std::unique_lock<std::mutex> 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<std::mutex> 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<DebugContext> g_debug_context; // TODO: Get rid of this global + namespace DebugUtils { void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { @@ -155,7 +199,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data // This is put into a try-catch block to make sure we notice unknown configurations. std::vector<OutputRegisterInfo> output_info_table; - for (int i = 0; i < 7; ++i) { + for (unsigned i = 0; i < 7; ++i) { using OutputAttributes = Pica::Regs::VSOutputAttributes; // TODO: It's still unclear how the attribute components map to the register! @@ -204,8 +248,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data it->component_mask = it->component_mask | component_mask; } } catch (const std::out_of_range& ) { - _dbg_assert_msg_(GPU, 0, "Unknown output attribute mapping"); - ERROR_LOG(GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x", + _dbg_assert_msg_(HW_GPU, 0, "Unknown output attribute mapping"); + LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x", (int)output_attributes[i].map_x.Value(), (int)output_attributes[i].map_y.Value(), (int)output_attributes[i].map_z.Value(), @@ -265,7 +309,7 @@ static int is_pica_tracing = false; void StartPicaTracing() { if (is_pica_tracing) { - ERROR_LOG(GPU, "StartPicaTracing called even though tracing already running!"); + LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!"); return; } @@ -298,7 +342,7 @@ void OnPicaRegWrite(u32 id, u32 value) std::unique_ptr<PicaTrace> FinishPicaTracing() { if (!is_pica_tracing) { - ERROR_LOG(GPU, "FinishPicaTracing called even though tracing already running!"); + LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!"); return {}; } @@ -312,15 +356,51 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() return std::move(ret); } +const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { + _dbg_assert_(Debug_GPU, 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 }; +} + +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. return; #ifndef HAVE_PNG - return; + return; #else - if (!data) + if (!data) return; // Write data to file @@ -341,7 +421,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { - ERROR_LOG(GPU, "Could not allocate write struct\n"); + LOG_ERROR(Debug_GPU, "Could not allocate write struct\n"); goto finalise; } @@ -349,13 +429,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { // Initialize info structure info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { - ERROR_LOG(GPU, "Could not allocate info struct\n"); + LOG_ERROR(Debug_GPU, "Could not allocate info struct\n"); goto finalise; } // Setup Exception handling if (setjmp(png_jmpbuf(png_ptr))) { - ERROR_LOG(GPU, "Error during png creation\n"); + LOG_ERROR(Debug_GPU, "Error during png creation\n"); goto finalise; } @@ -375,34 +455,22 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { png_write_info(png_ptr, info_ptr); buf = new u8[row_stride * texture_config.height]; - for (int y = 0; y < texture_config.height; ++y) { - for (int 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]; + for (unsigned y = 0; y < texture_config.height; ++y) { + for (unsigned x = 0; x < texture_config.width; ++x) { + TextureInfo info; + info.width = texture_config.width; + info.height = texture_config.height; + info.stride = row_stride; + info.format = registers.texture0_format; + Math::Vec4<u8> 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(); } } // Write image data - for (auto y = 0; y < texture_config.height; ++y) + for (unsigned y = 0; y < texture_config.height; ++y) { u8* row_ptr = (u8*)buf + y * row_stride; u8* ptr = row_ptr; @@ -514,7 +582,7 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n"; } - DEBUG_LOG(GPU, "%s", stage_info.c_str()); + LOG_TRACE(HW_GPU, "%s", stage_info.c_str()); } } // namespace diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 8b1499bf2..51f14f12f 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -5,13 +5,147 @@ #pragma once #include <array> +#include <condition_variable> +#include <list> +#include <map> #include <memory> +#include <mutex> #include <vector> +#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<DebugContext> debug_context) : context_weak(debug_context) { + std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); + debug_context->breakpoint_observers.push_back(this); + } + + virtual ~BreakPointObserver() { + auto context = context_weak.lock(); + if (context) { + std::unique_lock<std::mutex> 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<DebugContext> 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<DebugContext> Construct() { + return std::shared_ptr<DebugContext>(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<Event, BreakPoint> 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<BreakPointObserver*> breakpoint_observers; +}; + +extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global + namespace DebugUtils { // Simple utility class for dumping geometry data to an OBJ file @@ -41,7 +175,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data // Utility class to log Pica commands. struct PicaTrace { struct Write : public std::pair<u32,u32> { - Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {} + Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {} u32& Id() { return first; } const u32& Id() const { return first; } @@ -57,6 +191,18 @@ bool IsPicaTracing(); void OnPicaRegWrite(u32 id, u32 value); std::unique_ptr<PicaTrace> 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<u8> 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<Pica::Regs::TevStageConfig,6>& stages); diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 1242eb58f..16b1656bb 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -39,7 +39,7 @@ public: virtual void GXCommandProcessed(int total_command_count) { const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1); - ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value()); + LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value()); } protected: diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 5fe15a218..4c3791ad9 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -45,10 +45,16 @@ struct Regs { #define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) #define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; - INSERT_PADDING_WORDS(0x41); + INSERT_PADDING_WORDS(0x10); + + u32 trigger_irq; + + INSERT_PADDING_WORDS(0x30); BitField<0, 24, u32> viewport_size_x; + INSERT_PADDING_WORDS(0x1); + BitField<0, 24, u32> viewport_size_y; INSERT_PADDING_WORDS(0x9); @@ -109,8 +115,8 @@ struct Regs { u32 address; - u32 GetPhysicalAddress() { - return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; + u32 GetPhysicalAddress() const { + return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; } // texture1 and texture2 store the texture format directly after the address @@ -130,7 +136,26 @@ struct Regs { // Seems like they are luminance formats and compressed textures. }; - BitField<0, 1, u32> texturing_enable; + static unsigned BytesPerPixel(TextureFormat format) { + switch (format) { + case TextureFormat::RGBA8: + return 4; + + case TextureFormat::RGB8: + return 3; + + case TextureFormat::RGBA5551: + case TextureFormat::RGB565: + case TextureFormat::RGBA4: + return 2; + + default: + // placeholder for yet unknown formats + return 1; + } + } + + BitField< 0, 1, u32> texturing_enable; TextureConfig texture0; INSERT_PADDING_WORDS(0x8); BitField<0, 4, TextureFormat> texture0_format; @@ -287,7 +312,7 @@ struct Regs { inline u32 GetBaseAddress() const { // TODO: Ugly, should fix PhysicalToVirtualAddress instead - return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; + return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; } // Descriptor for internal vertex attributes @@ -517,10 +542,6 @@ struct Regs { static std::string GetCommandName(int index) { std::map<u32, std::string> 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 { \ @@ -529,6 +550,7 @@ struct Regs { map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ } while(false) + ADD_FIELD(trigger_irq); ADD_FIELD(viewport_size_x); ADD_FIELD(viewport_size_y); ADD_FIELD(viewport_depth_range); @@ -557,7 +579,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]; @@ -593,6 +614,7 @@ private: #ifndef _MSC_VER #define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position") +ASSERT_REG_POSITION(trigger_irq, 0x10); ASSERT_REG_POSITION(viewport_size_x, 0x41); ASSERT_REG_POSITION(viewport_size_y, 0x43); ASSERT_REG_POSITION(viewport_depth_range, 0x4d); diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index dabf2d1a3..102693ed9 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp @@ -43,7 +43,7 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl break; default: - ERROR_LOG(GPU, "Unknown triangle topology %x:", (int)topology); + LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology); break; } } diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index a35f0c0d8..b7e04a560 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -252,7 +252,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return combiner_output.rgb(); default: - ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source); + LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); return {}; } }; @@ -272,7 +272,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return combiner_output.a(); default: - ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source); + LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); return 0; } }; @@ -283,7 +283,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, case ColorModifier::SourceColor: return values; default: - ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); + LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); return {}; } }; @@ -293,7 +293,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, case AlphaModifier::SourceAlpha: return value; default: - ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); + LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); return 0; } }; @@ -307,7 +307,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return ((input[0] * input[1]) / 255).Cast<u8>(); default: - ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op); + LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); return {}; } }; @@ -321,7 +321,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return input[0] * input[1] / 255; default: - ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op); + LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); return 0; } }; diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index f1dbc9d17..bce402b88 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -25,7 +25,7 @@ public: /// Swap buffers (render frame) virtual void SwapBuffers() = 0; - /** + /** * Set the emulator window to use for renderer * @param window EmuWindow handle to emulator window to use for rendering */ diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index a0eb0418c..d0f82e6cd 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -20,9 +20,9 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { int info_log_length; // Compile Vertex Shader - DEBUG_LOG(GPU, "Compiling vertex shader."); + LOG_DEBUG(Render_OpenGL, "Compiling vertex shader..."); - glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL); + glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr); glCompileShader(vertex_shader_id); // Check Vertex Shader @@ -31,14 +31,18 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> vertex_shader_error(info_log_length); - glGetShaderInfoLog(vertex_shader_id, info_log_length, NULL, &vertex_shader_error[0]); - DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]); + glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]); + if (result) { + LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]); + } else { + LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]); + } } // Compile Fragment Shader - DEBUG_LOG(GPU, "Compiling fragment shader."); + LOG_DEBUG(Render_OpenGL, "Compiling fragment shader..."); - glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL); + glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr); glCompileShader(fragment_shader_id); // Check Fragment Shader @@ -47,12 +51,16 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> fragment_shader_error(info_log_length); - glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]); - DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]); + glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]); + if (result) { + LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]); + } else { + LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", &fragment_shader_error[0]); + } } // Link the program - DEBUG_LOG(GPU, "Linking program."); + LOG_DEBUG(Render_OpenGL, "Linking program..."); GLuint program_id = glCreateProgram(); glAttachShader(program_id, vertex_shader_id); @@ -65,8 +73,12 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { if (info_log_length > 1) { std::vector<char> program_error(info_log_length); - glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]); - DEBUG_LOG(GPU, "%s", &program_error[0]); + glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); + if (result) { + LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]); + } else { + LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); + } } glDeleteShader(vertex_shader_id); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8483f79be..e2caeeb8f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -61,7 +61,7 @@ void RendererOpenGL::SwapBuffers() { for(int i : {0, 1}) { const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; - if (textures[i].width != framebuffer.width || textures[i].height != framebuffer.height) { + if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a // performance problem. @@ -90,7 +90,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1); - DEBUG_LOG(GPU, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", + LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", framebuffer.stride * framebuffer.height, framebuffer_vaddr, (int)framebuffer.width, (int)framebuffer.height, (int)framebuffer.format); @@ -98,15 +98,15 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); // TODO: Handle other pixel formats - _dbg_assert_msg_(RENDER, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, + _dbg_assert_msg_(Render_OpenGL, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, "Unsupported 3DS pixel format."); size_t pixel_stride = framebuffer.stride / 3; // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately - _dbg_assert_(RENDER, pixel_stride * 3 == framebuffer.stride); + _dbg_assert_(Render_OpenGL, pixel_stride * 3 == framebuffer.stride); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default // only allows rows to have a memory alignement of 4. - _dbg_assert_(RENDER, pixel_stride % 4 == 0); + _dbg_assert_(Render_OpenGL, pixel_stride % 4 == 0); glBindTexture(GL_TEXTURE_2D, texture.handle); glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); @@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x * Draws the emulated screens to the emulator window. */ void RendererOpenGL::DrawScreens() { - glViewport(0, 0, resolution_width, resolution_height); + auto viewport_extent = GetViewportExtent(); + glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom? glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program_id); @@ -228,17 +229,45 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { render_window = window; } +MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { + unsigned framebuffer_width; + unsigned framebuffer_height; + std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize(); + + float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width; + float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width; + + MathUtil::Rectangle<unsigned> viewport_extent; + if (window_aspect_ratio > emulation_aspect_ratio) { + // Window is narrower than the emulation content => apply borders to the top and bottom + unsigned viewport_height = std::round(emulation_aspect_ratio * framebuffer_width); + viewport_extent.left = 0; + viewport_extent.top = (framebuffer_height - viewport_height) / 2; + viewport_extent.right = viewport_extent.left + framebuffer_width; + viewport_extent.bottom = viewport_extent.top + viewport_height; + } else { + // Otherwise, apply borders to the left and right sides of the window. + unsigned viewport_width = std::round(framebuffer_height / emulation_aspect_ratio); + viewport_extent.left = (framebuffer_width - viewport_width) / 2; + viewport_extent.top = 0; + viewport_extent.right = viewport_extent.left + viewport_width; + viewport_extent.bottom = viewport_extent.top + framebuffer_height; + } + + return viewport_extent; +} + /// Initialize the renderer void RendererOpenGL::Init() { render_window->MakeCurrent(); int err = ogl_LoadFunctions(); if (ogl_LOAD_SUCCEEDED != err) { - ERROR_LOG(RENDER, "Failed to initialize GL functions! Exiting..."); + LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting..."); exit(-1); } - NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION)); + LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION)); InitOpenGLObjects(); } diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index eed201a95..7fdcec731 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -4,13 +4,15 @@ #pragma once +#include <array> + #include "generated/gl_3_2_core.h" -#include "common/common.h" +#include "common/math_util.h" + #include "core/hw/gpu.h" -#include "video_core/renderer_base.h" -#include <array> +#include "video_core/renderer_base.h" class EmuWindow; @@ -52,6 +54,9 @@ private: static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, const TextureInfo& texture); + /// Computes the viewport rectangle + MathUtil::Rectangle<unsigned> GetViewportExtent(); + EmuWindow* render_window; ///< Handle to render window u32 last_mode; ///< Last render mode diff --git a/src/video_core/utils.cpp b/src/video_core/utils.cpp index c1848f923..f1156a493 100644 --- a/src/video_core/utils.cpp +++ b/src/video_core/utils.cpp @@ -20,7 +20,7 @@ namespace VideoCore { void DumpTGA(std::string filename, short width, short height, u8* raw_data) { TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0}; FILE* fout = fopen(filename.c_str(), "wb"); - + fwrite(&hdr, sizeof(TGAHeader), 1, fout); for (int y = 0; y < height; y++) { @@ -30,7 +30,7 @@ void DumpTGA(std::string filename, short width, short height, u8* raw_data) { putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r } } - + fclose(fout); } } // namespace diff --git a/src/video_core/utils.h b/src/video_core/utils.h index 9cb3d4d43..21380a908 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h @@ -12,24 +12,24 @@ namespace FormatPrecision { /// Adjust RGBA8 color with RGBA6 precision static inline u32 rgba8_with_rgba6(u32 src) { - u32 color = src; - color &= 0xFCFCFCFC; - color |= (color >> 6) & 0x03030303; - return color; + u32 color = src; + color &= 0xFCFCFCFC; + color |= (color >> 6) & 0x03030303; + return color; } /// Adjust RGBA8 color with RGB565 precision static inline u32 rgba8_with_rgb565(u32 src) { - u32 color = (src & 0xF8FCF8); - color |= (color >> 5) & 0x070007; - color |= (color >> 6) & 0x000300; - color |= 0xFF000000; - return color; + u32 color = (src & 0xF8FCF8); + color |= (color >> 5) & 0x070007; + color |= (color >> 6) & 0x000300; + color |= 0xFF000000; + return color; } /// Adjust Z24 depth value with Z16 precision static inline u32 z24_with_z16(u32 src) { - return (src & 0xFFFF00) | (src >> 16); + return (src & 0xFFFF00) | (src >> 16); } } // namespace diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 96625791c..477e78cfe 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp @@ -2,11 +2,16 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include <boost/range/algorithm.hpp> + +#include <common/file_util.h> + +#include <core/mem_map.h> + +#include "debug_utils/debug_utils.h" + #include "pica.h" #include "vertex_shader.h" -#include "debug_utils/debug_utils.h" -#include <core/mem_map.h> -#include <common/file_util.h> namespace Pica { @@ -201,7 +206,7 @@ static void ProcessShaderCode(VertexShaderState& state) { case Instruction::OpCode::CALL: increment_pc = false; - _dbg_assert_(GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); + _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); *++state.call_stack_pointer = state.program_counter - shader_memory; // TODO: Does this offset refer to the beginning of shader memory? @@ -213,7 +218,7 @@ static void ProcessShaderCode(VertexShaderState& state) { break; default: - ERROR_LOG(GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", + LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); break; } @@ -238,7 +243,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) // Setup input register table const auto& attribute_register_map = registers.vs_input_register_map; float24 dummy_register; - std::fill(&state.input_register_table[0], &state.input_register_table[16], &dummy_register); + boost::fill(state.input_register_table, &dummy_register); if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; @@ -272,8 +277,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) state.status_registers[0] = false; state.status_registers[1] = false; - std::fill(state.call_stack, state.call_stack + sizeof(state.call_stack) / sizeof(state.call_stack[0]), - VertexShaderState::INVALID_ADDRESS); + boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); state.call_stack_pointer = &state.call_stack[0]; ProcessShaderCode(state); @@ -281,7 +285,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) state.debug.max_opdesc_id, registers.vs_main_offset, registers.vs_output_attributes); - DEBUG_LOG(GPU, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", + LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32()); diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index 607a8e803..bfb6fb6e3 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h @@ -141,7 +141,7 @@ union Instruction { return BitFieldType::Value(); else if (GetRegisterType() == Temporary) return BitFieldType::Value() - 0x10; - else if (GetRegisterType() == FloatUniform) + else // if (GetRegisterType() == FloatUniform) return BitFieldType::Value() - 0x20; } diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index c779771c5..6791e4007 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -17,8 +17,8 @@ namespace VideoCore { -EmuWindow* g_emu_window = NULL; ///< Frontend emulator window -RendererBase* g_renderer = NULL; ///< Renderer plugin +EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window +RendererBase* g_renderer = nullptr; ///< Renderer plugin int g_current_frame = 0; /// Initialize the video core @@ -30,13 +30,13 @@ void Init(EmuWindow* emu_window) { g_current_frame = 0; - NOTICE_LOG(VIDEO, "initialized OK"); + LOG_DEBUG(Render, "initialized OK"); } /// Shutdown the video core void Shutdown() { delete g_renderer; - NOTICE_LOG(VIDEO, "shutdown OK"); + LOG_DEBUG(Render, "shutdown OK"); } } // namespace |
