diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index cb0e1ac12..663be4416 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -40,6 +40,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 4ac36b39f..c10fb3071 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -21,6 +21,7 @@ + diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 05d264b0c..5fd7f92c6 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -9,9 +9,6 @@ #include Log_SetChannel(GPU); -static u32 s_cpu_to_vram_dump_id = 1; -static u32 s_vram_to_cpu_dump_id = 1; - GPU::GPU() = default; GPU::~GPU() = default; @@ -250,6 +247,7 @@ void GPU::DMAWrite(const u32* words, u32 word_count) { case DMADirection::CPUtoGP0: { +#if 0 // partial command buffered? have to go through the slow path if (!m_GP0_buffer.empty()) { @@ -292,6 +290,10 @@ void GPU::DMAWrite(const u32* words, u32 word_count) } UpdateGPUSTAT(); +#else + for (u32 i = 0; i < word_count; i++) + WriteGP0(words[i]); +#endif } break; @@ -433,7 +435,8 @@ void GPU::WriteGP0(u32 value) Assert(m_GP0_buffer.size() <= 1048576); const u32* command_ptr = m_GP0_buffer.data(); - if (HandleGP0Command(command_ptr, static_cast(m_GP0_buffer.size()))) + const u32 command = m_GP0_buffer[0] >> 24; + if ((this->*s_GP0_command_handler_table[command])(command_ptr, static_cast(m_GP0_buffer.size()))) { DebugAssert((command_ptr - m_GP0_buffer.data()) == m_GP0_buffer.size()); m_GP0_buffer.clear(); @@ -442,130 +445,6 @@ void GPU::WriteGP0(u32 value) UpdateGPUSTAT(); } -bool GPU::HandleGP0Command(const u32*& command_ptr, u32 command_size) -{ - const u8 command = Truncate8(command_ptr[0] >> 24); - - if (command >= 0x20 && command <= 0x7F) - { - // Draw polygon - return HandleRenderCommand(command_ptr, command_size); - } - else - { - const u32 param = command_ptr[0] & UINT32_C(0x00FFFFFF); - - switch (command) - { - case 0x00: // NOP - case 0x01: // Clear cache - command_ptr++; - return true; - - case 0x02: // Fill Rectangle - return HandleFillRectangleCommand(command_ptr, command_size); - - case 0xA0: // Copy Rectangle CPU->VRAM - return HandleCopyRectangleCPUToVRAMCommand(command_ptr, command_size); - - case 0xC0: // Copy Rectangle VRAM->CPU - return HandleCopyRectangleVRAMToCPUCommand(command_ptr, command_size); - - case 0x80: // Copy Rectangle VRAM->VRAM - return HandleCopyRectangleVRAMToVRAMCommand(command_ptr, command_size); - - case 0xE1: // Set draw mode - { - // 0..10 bits match GPUSTAT - const u32 MASK = ((UINT32_C(1) << 11) - 1); - m_GPUSTAT.bits = (m_GPUSTAT.bits & ~MASK) | param & MASK; - m_GPUSTAT.texture_disable = (param & (UINT32_C(1) << 11)) != 0; - m_render_state.texture_x_flip = (param & (UINT32_C(1) << 12)) != 0; - m_render_state.texture_y_flip = (param & (UINT32_C(1) << 13)) != 0; - Log_DebugPrintf("Set draw mode %08X", param); - command_ptr++; - return true; - } - - case 0xE2: // set texture window - { - m_render_state.SetTextureWindow(param); - Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_render_state.texture_window_mask_x, - m_render_state.texture_window_mask_y, m_render_state.texture_window_offset_x, - m_render_state.texture_window_offset_y); - command_ptr++; - return true; - } - - case 0xE3: // Set drawing area top left - { - const u32 left = param & UINT32_C(0x3FF); - const u32 top = (param >> 10) & UINT32_C(0x1FF); - Log_DebugPrintf("Set drawing area top-left: (%u, %u)", left, top); - if (m_drawing_area.left != left || m_drawing_area.top != top) - { - FlushRender(); - - m_drawing_area.left = left; - m_drawing_area.top = top; - UpdateDrawingArea(); - } - command_ptr++; - return true; - } - - case 0xE4: // Set drawing area bottom right - { - const u32 right = param & UINT32_C(0x3FF); - const u32 bottom = (param >> 10) & UINT32_C(0x1FF); - Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.right, m_drawing_area.bottom); - if (m_drawing_area.right != right || m_drawing_area.bottom != bottom) - { - FlushRender(); - - m_drawing_area.right = right; - m_drawing_area.bottom = bottom; - UpdateDrawingArea(); - } - command_ptr++; - return true; - } - - case 0xE5: // Set drawing offset - { - const s32 x = SignExtendN<11, s32>(param & UINT32_C(0x7FF)); - const s32 y = SignExtendN<11, s32>((param >> 11) & UINT32_C(0x7FF)); - Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y); - if (m_drawing_offset.x != x || m_drawing_offset.y != y) - { - FlushRender(); - - m_drawing_offset.x = x; - m_drawing_offset.y = y; - } - command_ptr++; - return true; - } - - case 0xE6: // Mask bit setting - { - m_GPUSTAT.draw_set_mask_bit = (param & UINT32_C(0x01)) != 0; - m_GPUSTAT.draw_to_masked_pixels = (param & UINT32_C(0x01)) != 0; - Log_DebugPrintf("Set mask bit %u %u", BoolToUInt32(m_GPUSTAT.draw_set_mask_bit), - BoolToUInt32(m_GPUSTAT.draw_to_masked_pixels)); - command_ptr++; - return true; - } - - default: - { - Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command); - command_ptr++; - return true; - } - } - } -} void GPU::WriteGP1(u32 value) { @@ -732,211 +611,6 @@ void GPU::HandleGetGPUInfoCommand(u32 value) } } -bool GPU::HandleRenderCommand(const u32*& command_ptr, u32 command_size) -{ - const RenderCommand rc{command_ptr[0]}; - u8 words_per_vertex; - u32 num_vertices; - u32 total_words; - switch (rc.primitive) - { - case Primitive::Polygon: - { - // shaded vertices use the colour from the first word for the first vertex - words_per_vertex = 1 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.shading_enable); - num_vertices = rc.quad_polygon ? 4 : 3; - total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); - } - break; - - case Primitive::Line: - { - words_per_vertex = 1 + BoolToUInt8(rc.shading_enable); - if (rc.polyline) - { - // polyline goes until we hit the termination code - num_vertices = 0; - bool found_terminator = false; - for (u32 pos = 1 + BoolToUInt32(!rc.shading_enable); pos < command_size; pos += words_per_vertex) - { - if (command_ptr[pos] == 0x55555555) - { - found_terminator = true; - break; - } - - num_vertices++; - } - if (!found_terminator) - return false; - } - else - { - num_vertices = 2; - } - - total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); - } - break; - - case Primitive::Rectangle: - { - words_per_vertex = - 2 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.rectangle_size == DrawRectangleSize::Variable); - num_vertices = 1; - total_words = words_per_vertex; - } - break; - - default: - UnreachableCode(); - return true; - } - - if (command_size < total_words) - return false; - - static constexpr std::array primitive_names = {{"", "polygon", "line", "rectangle"}}; - - Log_DebugPrintf("Render %s %s %s %s %s (%u verts, %u words per vert)", rc.quad_polygon ? "four-point" : "three-point", - rc.transparency_enable ? "semi-transparent" : "opaque", - rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome", - primitive_names[static_cast(rc.primitive.GetValue())], ZeroExtend32(num_vertices), - ZeroExtend32(words_per_vertex)); - - DispatchRenderCommand(rc, num_vertices, command_ptr); - command_ptr += total_words; - return true; -} - -bool GPU::HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size) -{ - if (command_size < 3) - return false; - - FlushRender(); - - const u32 color = command_ptr[0] & UINT32_C(0x00FFFFFF); - const u32 dst_x = command_ptr[1] & UINT32_C(0xFFFF); - const u32 dst_y = command_ptr[1] >> 16; - const u32 width = command_ptr[2] & UINT32_C(0xFFFF); - const u32 height = command_ptr[2] >> 16; - command_ptr += 3; - - Log_DebugPrintf("Fill VRAM rectangle offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, width, height); - - // Drop higher precision when filling. Bit15 is set to 0. - // TODO: Force 8-bit color option. - const u16 color16 = RGBA8888ToRGBA5551(color); - - FillVRAM(dst_x, dst_y, width, height, color16); - return true; -} - -bool GPU::HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size) -{ - if (command_size < 3) - return false; - - const u32 copy_width = command_ptr[2] & UINT32_C(0xFFFF); - const u32 copy_height = command_ptr[2] >> 16; - const u32 num_pixels = copy_width * copy_height; - const u32 num_words = 3 + ((num_pixels + 1) / 2); - if (command_size < num_words) - return false; - - const u32 dst_x = command_ptr[1] & UINT32_C(0xFFFF); - const u32 dst_y = command_ptr[1] >> 16; - - Log_DebugPrintf("Copy rectangle from CPU to VRAM offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, copy_width, - copy_height); - - if ((dst_x + copy_width) > VRAM_WIDTH || (dst_y + copy_height) > VRAM_HEIGHT) - { - Panic("Out of bounds VRAM copy"); - return true; - } - - if (m_debug_options.dump_cpu_to_vram_copies) - { - DumpVRAMToFile(SmallString::FromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++), copy_width, copy_height, - sizeof(u16) * copy_width, &command_ptr[3], true); - } - - FlushRender(); - UpdateVRAM(dst_x, dst_y, copy_width, copy_height, &command_ptr[3]); - command_ptr += num_words; - return true; -} - -bool GPU::HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size) -{ - if (command_size < 3) - return false; - - const u32 width = command_ptr[2] & UINT32_C(0xFFFF); - const u32 height = command_ptr[2] >> 16; - const u32 num_pixels = width * height; - const u32 num_words = ((num_pixels + 1) / 2); - const u32 src_x = command_ptr[1] & UINT32_C(0xFFFF); - const u32 src_y = command_ptr[1] >> 16; - command_ptr += 3; - - Log_DebugPrintf("Copy rectangle from VRAM to CPU offset=(%u,%u), size=(%u,%u)", src_x, src_y, width, height); - - if ((src_x + width) > VRAM_WIDTH || (src_y + height) > VRAM_HEIGHT) - { - Panic("Out of bounds VRAM copy"); - return true; - } - - // all rendering should be done first... - FlushRender(); - - // TODO: A better way of doing this.. - std::vector temp(num_words); - ReadVRAM(src_x, src_y, width, height, temp.data()); - for (const u32 bits : temp) - m_GPUREAD_buffer.push_back(bits); - - if (m_debug_options.dump_vram_to_cpu_copies) - { - DumpVRAMToFile(SmallString::FromFormat("vram_to_cpu_copy_%u.png", s_cpu_to_vram_dump_id++), width, height, - sizeof(u16) * width, temp.data(), true); - } - - // Is this correct? - return true; -} - -bool GPU::HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size) -{ - if (command_size < 4) - return false; - - const u32 src_x = command_ptr[1] & UINT32_C(0xFFFF); - const u32 src_y = command_ptr[1] >> 16; - const u32 dst_x = command_ptr[2] & UINT32_C(0xFFFF); - const u32 dst_y = command_ptr[2] >> 16; - const u32 width = command_ptr[3] & UINT32_C(0xFFFF); - const u32 height = command_ptr[3] >> 16; - command_ptr += 4; - - Log_DebugPrintf("Copy rectangle from VRAM to VRAM src=(%u,%u), dst=(%u,%u), size=(%u,%u)", src_x, src_y, dst_x, dst_y, - width, height); - - if ((src_x + width) > VRAM_WIDTH || (src_y + height) > VRAM_HEIGHT || (dst_x + width) > VRAM_WIDTH || - (dst_y + height) > VRAM_HEIGHT) - { - Panic("Out of bounds VRAM copy"); - return true; - } - - FlushRender(); - CopyVRAM(src_x, src_y, dst_x, dst_y, width, height); - return true; -} - void GPU::UpdateDisplay() {} void GPU::UpdateDrawingArea() {} diff --git a/src/core/gpu.h b/src/core/gpu.h index 121b4ebb1..7ab4a26e7 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -184,14 +184,6 @@ protected: void WriteGP1(u32 value); void HandleGetGPUInfoCommand(u32 value); - // Rendering commands, returns false if not enough data is provided - bool HandleGP0Command(const u32*& command_ptr, u32 command_size); - bool HandleRenderCommand(const u32*& command_ptr, u32 command_size); - bool HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size); - bool HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size); - bool HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size); - bool HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size); - // Rendering in the backend virtual void UpdateDisplay(); virtual void UpdateDrawingArea(); @@ -357,4 +349,28 @@ protected: std::deque m_GPUREAD_buffer; DebugOptions m_debug_options; + +private: + using GP0CommandHandler = bool (GPU::*)(const u32*&, u32); + using GP0CommandHandlerTable = std::array; + static constexpr GP0CommandHandlerTable GenerateGP0CommandHandlerTable(); + + // Rendering commands, returns false if not enough data is provided + bool HandleUnknownGP0Command(const u32*& command_ptr, u32 command_size); + bool HandleNOPCommand(const u32*& command_ptr, u32 command_size); + bool HandleClearCacheCommand(const u32*& command_ptr, u32 command_size); + bool HandleInterruptRequestCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetDrawModeCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetTextureWindowCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_size); + bool HandleSetMaskBitCommand(const u32*& command_ptr, u32 command_size); + bool HandleRenderCommand(const u32*& command_ptr, u32 command_size); + bool HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size); + bool HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size); + bool HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size); + bool HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size); + + static const GP0CommandHandlerTable s_GP0_command_handler_table; }; diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp new file mode 100644 index 000000000..5fe1b2535 --- /dev/null +++ b/src/core/gpu_commands.cpp @@ -0,0 +1,368 @@ +#include "YBaseLib/Log.h" +#include "YBaseLib/String.h" +#include "gpu.h" +#include "interrupt_controller.h" +Log_SetChannel(GPU); + +static u32 s_cpu_to_vram_dump_id = 1; +static u32 s_vram_to_cpu_dump_id = 1; + +constexpr GPU::GP0CommandHandlerTable GPU::GenerateGP0CommandHandlerTable() +{ + GP0CommandHandlerTable table = {}; + for (u32 i = 0; i < static_cast(table.size()); i++) + table[i] = &GPU::HandleUnknownGP0Command; + table[0x00] = &GPU::HandleNOPCommand; + table[0x01] = &GPU::HandleClearCacheCommand; + table[0x02] = &GPU::HandleFillRectangleCommand; + table[0x03] = &GPU::HandleNOPCommand; + for (u32 i = 0x04; i <= 0x1E; i++) + table[i] = &GPU::HandleNOPCommand; + table[0x1F] = &GPU::HandleInterruptRequestCommand; + for (u32 i = 0x20; i <= 0x7F; i++) + table[i] = &GPU::HandleRenderCommand; + table[0xE0] = &GPU::HandleNOPCommand; + table[0xE1] = &GPU::HandleSetDrawModeCommand; + table[0xE2] = &GPU::HandleSetTextureWindowCommand; + table[0xE3] = &GPU::HandleSetDrawingAreaTopLeftCommand; + table[0xE4] = &GPU::HandleSetDrawingAreaBottomRightCommand; + table[0xE5] = &GPU::HandleSetDrawingOffsetCommand; + table[0xE6] = &GPU::HandleSetMaskBitCommand; + for (u32 i = 0xE7; i <= 0xEF; i++) + table[i] = &GPU::HandleNOPCommand; + for (u32 i = 0x80; i <= 0x9F; i++) + table[i] = &GPU::HandleCopyRectangleVRAMToVRAMCommand; + for (u32 i = 0xA0; i <= 0xBF; i++) + table[i] = &GPU::HandleCopyRectangleCPUToVRAMCommand; + for (u32 i = 0xC0; i <= 0xDF; i++) + table[i] = &GPU::HandleCopyRectangleVRAMToCPUCommand; + + return table; +} + +constexpr GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable(); + +bool GPU::HandleUnknownGP0Command(const u32*& command_ptr, u32 command_size) +{ + const u32 command = *(command_ptr++) >> 24; + Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command); + return true; +} + +bool GPU::HandleNOPCommand(const u32*& command_ptr, u32 command_size) +{ + command_ptr++; + return true; +} + +bool GPU::HandleClearCacheCommand(const u32*& command_ptr, u32 command_size) +{ + Log_DebugPrintf("GP0 clear cache"); + command_ptr++; + return true; +} + +bool GPU::HandleInterruptRequestCommand(const u32*& command_ptr, u32 command_size) +{ + Log_WarningPrintf("GP0 interrupt request"); + if (!m_GPUSTAT.interrupt_request) + { + m_GPUSTAT.interrupt_request = true; + m_interrupt_controller->InterruptRequest(InterruptController::IRQ::GPU); + } + + return true; +} + +bool GPU::HandleSetDrawModeCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + + // 0..10 bits match GPUSTAT + const u32 MASK = ((UINT32_C(1) << 11) - 1); + m_GPUSTAT.bits = (m_GPUSTAT.bits & ~MASK) | param & MASK; + m_GPUSTAT.texture_disable = (param & (UINT32_C(1) << 11)) != 0; + m_render_state.texture_x_flip = (param & (UINT32_C(1) << 12)) != 0; + m_render_state.texture_y_flip = (param & (UINT32_C(1) << 13)) != 0; + Log_DebugPrintf("Set draw mode %08X", param); + return true; +} + +bool GPU::HandleSetTextureWindowCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + m_render_state.SetTextureWindow(param); + Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_render_state.texture_window_mask_x, + m_render_state.texture_window_mask_y, m_render_state.texture_window_offset_x, + m_render_state.texture_window_offset_y); + return true; +} + +bool GPU::HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + const u32 left = param & UINT32_C(0x3FF); + const u32 top = (param >> 10) & UINT32_C(0x1FF); + Log_DebugPrintf("Set drawing area top-left: (%u, %u)", left, top); + if (m_drawing_area.left != left || m_drawing_area.top != top) + { + FlushRender(); + + m_drawing_area.left = left; + m_drawing_area.top = top; + UpdateDrawingArea(); + } + + return true; +} + +bool GPU::HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + + const u32 right = param & UINT32_C(0x3FF); + const u32 bottom = (param >> 10) & UINT32_C(0x1FF); + Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.right, m_drawing_area.bottom); + if (m_drawing_area.right != right || m_drawing_area.bottom != bottom) + { + FlushRender(); + + m_drawing_area.right = right; + m_drawing_area.bottom = bottom; + UpdateDrawingArea(); + } + + return true; +} + +bool GPU::HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + const s32 x = SignExtendN<11, s32>(param & UINT32_C(0x7FF)); + const s32 y = SignExtendN<11, s32>((param >> 11) & UINT32_C(0x7FF)); + Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y); + if (m_drawing_offset.x != x || m_drawing_offset.y != y) + { + FlushRender(); + + m_drawing_offset.x = x; + m_drawing_offset.y = y; + } + return true; +} + +bool GPU::HandleSetMaskBitCommand(const u32*& command_ptr, u32 command_size) +{ + const u32 param = *(command_ptr++) & UINT32_C(0x00FFFFFF); + + m_GPUSTAT.draw_set_mask_bit = (param & UINT32_C(0x01)) != 0; + m_GPUSTAT.draw_to_masked_pixels = (param & UINT32_C(0x01)) != 0; + Log_DebugPrintf("Set mask bit %u %u", BoolToUInt32(m_GPUSTAT.draw_set_mask_bit), + BoolToUInt32(m_GPUSTAT.draw_to_masked_pixels)); + return true; +} + +bool GPU::HandleRenderCommand(const u32*& command_ptr, u32 command_size) +{ + const RenderCommand rc{command_ptr[0]}; + u8 words_per_vertex; + u32 num_vertices; + u32 total_words; + switch (rc.primitive) + { + case Primitive::Polygon: + { + // shaded vertices use the colour from the first word for the first vertex + words_per_vertex = 1 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.shading_enable); + num_vertices = rc.quad_polygon ? 4 : 3; + total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); + } + break; + + case Primitive::Line: + { + words_per_vertex = 1 + BoolToUInt8(rc.shading_enable); + if (rc.polyline) + { + // polyline goes until we hit the termination code + num_vertices = 0; + bool found_terminator = false; + for (u32 pos = 1 + BoolToUInt32(!rc.shading_enable); pos < command_size; pos += words_per_vertex) + { + if (command_ptr[pos] == 0x55555555) + { + found_terminator = true; + break; + } + + num_vertices++; + } + if (!found_terminator) + return false; + } + else + { + num_vertices = 2; + } + + total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); + } + break; + + case Primitive::Rectangle: + { + words_per_vertex = + 2 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.rectangle_size == DrawRectangleSize::Variable); + num_vertices = 1; + total_words = words_per_vertex; + } + break; + + default: + UnreachableCode(); + return true; + } + + if (command_size < total_words) + return false; + + static constexpr std::array primitive_names = {{"", "polygon", "line", "rectangle"}}; + + Log_DebugPrintf("Render %s %s %s %s %s (%u verts, %u words per vert)", rc.quad_polygon ? "four-point" : "three-point", + rc.transparency_enable ? "semi-transparent" : "opaque", + rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome", + primitive_names[static_cast(rc.primitive.GetValue())], ZeroExtend32(num_vertices), + ZeroExtend32(words_per_vertex)); + + DispatchRenderCommand(rc, num_vertices, command_ptr); + command_ptr += total_words; + return true; +} + +bool GPU::HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size) +{ + if (command_size < 3) + return false; + + FlushRender(); + + const u32 color = command_ptr[0] & UINT32_C(0x00FFFFFF); + const u32 dst_x = command_ptr[1] & UINT32_C(0xFFFF); + const u32 dst_y = command_ptr[1] >> 16; + const u32 width = command_ptr[2] & UINT32_C(0xFFFF); + const u32 height = command_ptr[2] >> 16; + command_ptr += 3; + + Log_DebugPrintf("Fill VRAM rectangle offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, width, height); + + // Drop higher precision when filling. Bit15 is set to 0. + // TODO: Force 8-bit color option. + const u16 color16 = RGBA8888ToRGBA5551(color); + + FillVRAM(dst_x, dst_y, width, height, color16); + return true; +} + +bool GPU::HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size) +{ + if (command_size < 3) + return false; + + const u32 copy_width = command_ptr[2] & UINT32_C(0xFFFF); + const u32 copy_height = command_ptr[2] >> 16; + const u32 num_pixels = copy_width * copy_height; + const u32 num_words = 3 + ((num_pixels + 1) / 2); + if (command_size < num_words) + return false; + + const u32 dst_x = command_ptr[1] & UINT32_C(0xFFFF); + const u32 dst_y = command_ptr[1] >> 16; + + Log_DebugPrintf("Copy rectangle from CPU to VRAM offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, copy_width, + copy_height); + + if ((dst_x + copy_width) > VRAM_WIDTH || (dst_y + copy_height) > VRAM_HEIGHT) + { + Panic("Out of bounds VRAM copy"); + return true; + } + + if (m_debug_options.dump_cpu_to_vram_copies) + { + DumpVRAMToFile(SmallString::FromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++), copy_width, copy_height, + sizeof(u16) * copy_width, &command_ptr[3], true); + } + + FlushRender(); + UpdateVRAM(dst_x, dst_y, copy_width, copy_height, &command_ptr[3]); + command_ptr += num_words; + return true; +} + +bool GPU::HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size) +{ + if (command_size < 3) + return false; + + const u32 width = command_ptr[2] & UINT32_C(0xFFFF); + const u32 height = command_ptr[2] >> 16; + const u32 num_pixels = width * height; + const u32 num_words = ((num_pixels + 1) / 2); + const u32 src_x = command_ptr[1] & UINT32_C(0xFFFF); + const u32 src_y = command_ptr[1] >> 16; + command_ptr += 3; + + Log_DebugPrintf("Copy rectangle from VRAM to CPU offset=(%u,%u), size=(%u,%u)", src_x, src_y, width, height); + + if ((src_x + width) > VRAM_WIDTH || (src_y + height) > VRAM_HEIGHT) + { + Panic("Out of bounds VRAM copy"); + return true; + } + + // all rendering should be done first... + FlushRender(); + + // TODO: A better way of doing this.. + std::vector temp(num_words); + ReadVRAM(src_x, src_y, width, height, temp.data()); + for (const u32 bits : temp) + m_GPUREAD_buffer.push_back(bits); + + if (m_debug_options.dump_vram_to_cpu_copies) + { + DumpVRAMToFile(SmallString::FromFormat("vram_to_cpu_copy_%u.png", s_cpu_to_vram_dump_id++), width, height, + sizeof(u16) * width, temp.data(), true); + } + + // Is this correct? + return true; +} + +bool GPU::HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size) +{ + if (command_size < 4) + return false; + + const u32 src_x = command_ptr[1] & UINT32_C(0xFFFF); + const u32 src_y = command_ptr[1] >> 16; + const u32 dst_x = command_ptr[2] & UINT32_C(0xFFFF); + const u32 dst_y = command_ptr[2] >> 16; + const u32 width = command_ptr[3] & UINT32_C(0xFFFF); + const u32 height = command_ptr[3] >> 16; + command_ptr += 4; + + Log_DebugPrintf("Copy rectangle from VRAM to VRAM src=(%u,%u), dst=(%u,%u), size=(%u,%u)", src_x, src_y, dst_x, dst_y, + width, height); + + if ((src_x + width) > VRAM_WIDTH || (src_y + height) > VRAM_HEIGHT || (dst_x + width) > VRAM_WIDTH || + (dst_y + height) > VRAM_HEIGHT) + { + Panic("Out of bounds VRAM copy"); + return true; + } + + FlushRender(); + CopyVRAM(src_x, src_y, dst_x, dst_y, width, height); + return true; +} \ No newline at end of file