mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-19 08:15:46 -04:00
GPU: Implement FIFO and timings
This will cause a slight performance loss. I've left some knobs in which can be tweaked to mitigate this, but the goal is to be compatible with all games which require them.
This commit is contained in:
@ -7,10 +7,9 @@
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
#define CHECK_COMMAND_SIZE(num_words) \
|
||||
if (command_size < num_words) \
|
||||
if (m_fifo.GetSize() < num_words) \
|
||||
{ \
|
||||
m_command_total_words = num_words; \
|
||||
m_state = State::WaitingForParameters; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
@ -24,33 +23,95 @@ static constexpr u32 ReplaceZero(u32 value, u32 value_for_zero)
|
||||
|
||||
void GPU::ExecuteCommands()
|
||||
{
|
||||
Assert(m_GP0_buffer.size() < 1048576);
|
||||
m_syncing = true;
|
||||
|
||||
const u32* command_ptr = m_GP0_buffer.data();
|
||||
u32 command_size = static_cast<u32>(m_GP0_buffer.size());
|
||||
while (m_state != State::ReadingVRAM && command_size > 0 && command_size >= m_command_total_words)
|
||||
for (;;)
|
||||
{
|
||||
const u32 command = command_ptr[0] >> 24;
|
||||
const u32* old_command_ptr = command_ptr;
|
||||
if (!(this->*s_GP0_command_handler_table[command])(command_ptr, command_size))
|
||||
break;
|
||||
if (m_command_ticks <= m_max_run_ahead && !m_fifo.IsEmpty())
|
||||
{
|
||||
switch (m_blitter_state)
|
||||
{
|
||||
case BlitterState::Idle:
|
||||
{
|
||||
const u32 command = m_fifo.Peek(0) >> 24;
|
||||
if ((this->*s_GP0_command_handler_table[command])())
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 words_used = static_cast<u32>(command_ptr - old_command_ptr);
|
||||
DebugAssert(words_used <= command_size);
|
||||
command_size -= words_used;
|
||||
case BlitterState::WritingVRAM:
|
||||
{
|
||||
DebugAssert(m_blit_remaining_words > 0);
|
||||
const u32 words_to_copy = std::min(m_blit_remaining_words, m_fifo.GetSize());
|
||||
const size_t old_size = m_blit_buffer.size();
|
||||
m_blit_buffer.resize(m_blit_buffer.size() + words_to_copy);
|
||||
m_fifo.PopRange(&m_blit_buffer[old_size], words_to_copy);
|
||||
m_blit_remaining_words -= words_to_copy;
|
||||
AddCommandTicks(words_to_copy);
|
||||
|
||||
Log_DebugPrintf("VRAM write burst of %u words, %u words remaining", words_to_copy, m_blit_remaining_words);
|
||||
if (m_blit_remaining_words == 0)
|
||||
FinishVRAMWrite();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
case BlitterState::ReadingVRAM:
|
||||
{
|
||||
Panic("shouldn't be here");
|
||||
}
|
||||
break;
|
||||
|
||||
case BlitterState::DrawingPolyLine:
|
||||
{
|
||||
const u32 words_per_vertex = m_render_command.shading_enable ? 2 : 1;
|
||||
u32 terminator_index =
|
||||
m_render_command.shading_enable ? ((static_cast<u32>(m_blit_buffer.size()) & 1u) ^ 1u) : 0u;
|
||||
for (; terminator_index < m_fifo.GetSize(); terminator_index += words_per_vertex)
|
||||
{
|
||||
// polyline must have at least two vertices, and the terminator is (word & 0xf000f000) == 0x50005000.
|
||||
// terminator is on the first word for the vertex
|
||||
if ((m_fifo.Peek(terminator_index) & UINT32_C(0xF000F000)) == UINT32_C(0x50005000))
|
||||
break;
|
||||
}
|
||||
|
||||
const bool found_terminator = (terminator_index < m_fifo.GetSize());
|
||||
const u32 words_to_copy = std::min(terminator_index, m_fifo.GetSize());
|
||||
if (words_to_copy > 0)
|
||||
{
|
||||
const size_t old_size = m_blit_buffer.size();
|
||||
m_blit_buffer.resize(m_blit_buffer.size() + words_to_copy);
|
||||
m_fifo.PopRange(&m_blit_buffer[old_size], words_to_copy);
|
||||
}
|
||||
|
||||
Log_DebugPrintf("Added %u words to polyline", words_to_copy);
|
||||
if (found_terminator)
|
||||
{
|
||||
// drop terminator
|
||||
m_fifo.RemoveOne();
|
||||
Log_DebugPrintf("Drawing poly-line with %u vertices", GetPolyLineVertexCount());
|
||||
DispatchRenderCommand();
|
||||
m_blit_buffer.clear();
|
||||
EndCommand();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_fifo_pushed = false;
|
||||
UpdateDMARequest();
|
||||
if (!m_fifo_pushed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (command_size == 0)
|
||||
m_GP0_buffer.clear();
|
||||
else if (command_ptr > m_GP0_buffer.data())
|
||||
m_GP0_buffer.erase(m_GP0_buffer.begin(), m_GP0_buffer.begin() + (command_ptr - m_GP0_buffer.data()));
|
||||
|
||||
UpdateDMARequest();
|
||||
m_syncing = false;
|
||||
}
|
||||
|
||||
void GPU::EndCommand()
|
||||
{
|
||||
m_state = State::Idle;
|
||||
m_blitter_state = BlitterState::Idle;
|
||||
m_command_total_words = 0;
|
||||
}
|
||||
|
||||
@ -67,7 +128,24 @@ GPU::GP0CommandHandlerTable GPU::GenerateGP0CommandHandlerTable()
|
||||
table[i] = &GPU::HandleNOPCommand;
|
||||
table[0x1F] = &GPU::HandleInterruptRequestCommand;
|
||||
for (u32 i = 0x20; i <= 0x7F; i++)
|
||||
table[i] = &GPU::HandleRenderCommand;
|
||||
{
|
||||
const RenderCommand rc{i << 24};
|
||||
switch (rc.primitive)
|
||||
{
|
||||
case Primitive::Polygon:
|
||||
table[i] = &GPU::HandleRenderPolygonCommand;
|
||||
break;
|
||||
case Primitive::Line:
|
||||
table[i] = rc.polyline ? &GPU::HandleRenderPolyLineCommand : &GPU::HandleRenderLineCommand;
|
||||
break;
|
||||
case Primitive::Rectangle:
|
||||
table[i] = &GPU::HandleRenderRectangleCommand;
|
||||
break;
|
||||
default:
|
||||
table[i] = &GPU::HandleUnknownGP0Command;
|
||||
break;
|
||||
}
|
||||
}
|
||||
table[0xE0] = &GPU::HandleNOPCommand;
|
||||
table[0xE1] = &GPU::HandleSetDrawModeCommand;
|
||||
table[0xE2] = &GPU::HandleSetTextureWindowCommand;
|
||||
@ -87,30 +165,31 @@ GPU::GP0CommandHandlerTable GPU::GenerateGP0CommandHandlerTable()
|
||||
return table;
|
||||
}
|
||||
|
||||
bool GPU::HandleUnknownGP0Command(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleUnknownGP0Command()
|
||||
{
|
||||
const u32 command = *(command_ptr++) >> 24;
|
||||
const u32 command = m_fifo.Pop() >> 24;
|
||||
Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleNOPCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleNOPCommand()
|
||||
{
|
||||
command_ptr++;
|
||||
m_fifo.RemoveOne();
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleClearCacheCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleClearCacheCommand()
|
||||
{
|
||||
Log_DebugPrintf("GP0 clear cache");
|
||||
command_ptr++;
|
||||
m_fifo.RemoveOne();
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleInterruptRequestCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleInterruptRequestCommand()
|
||||
{
|
||||
Log_WarningPrintf("GP0 interrupt request");
|
||||
if (!m_GPUSTAT.interrupt_request)
|
||||
@ -119,35 +198,38 @@ bool GPU::HandleInterruptRequestCommand(const u32*& command_ptr, u32 command_siz
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::GPU);
|
||||
}
|
||||
|
||||
command_ptr++;
|
||||
m_fifo.RemoveOne();
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetDrawModeCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetDrawModeCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
Log_DebugPrintf("Set draw mode %08X", param);
|
||||
SetDrawMode(Truncate16(param));
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetTextureWindowCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetTextureWindowCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
SetTextureWindow(param);
|
||||
Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_draw_mode.texture_window_mask_x,
|
||||
m_draw_mode.texture_window_mask_y, m_draw_mode.texture_window_offset_x,
|
||||
m_draw_mode.texture_window_offset_y);
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetDrawingAreaTopLeftCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
const u32 left = param & 0x3FF;
|
||||
const u32 top = (param >> 10) & 0x1FF;
|
||||
Log_DebugPrintf("Set drawing area top-left: (%u, %u)", left, top);
|
||||
@ -160,16 +242,17 @@ bool GPU::HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 comman
|
||||
m_drawing_area_changed = true;
|
||||
}
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetDrawingAreaBottomRightCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
|
||||
const u32 right = param & 0x3FF;
|
||||
const u32 bottom = (param >> 10) & 0x1FF;
|
||||
const u32 right = param & 0x3FFu;
|
||||
const u32 bottom = (param >> 10) & 0x1FFu;
|
||||
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)
|
||||
{
|
||||
@ -180,15 +263,16 @@ bool GPU::HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 co
|
||||
m_drawing_area_changed = true;
|
||||
}
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetDrawingOffsetCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const s32 x = SignExtendN<11, s32>(param & 0x7FF);
|
||||
const s32 y = SignExtendN<11, s32>((param >> 11) & 0x7FF);
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
const s32 x = SignExtendN<11, s32>(param & 0x7FFu);
|
||||
const s32 y = SignExtendN<11, s32>((param >> 11) & 0x7FFu);
|
||||
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)
|
||||
{
|
||||
@ -198,13 +282,14 @@ bool GPU::HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_siz
|
||||
m_drawing_offset.y = y;
|
||||
}
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleSetMaskBitCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleSetMaskBitCommand()
|
||||
{
|
||||
const u32 param = *(command_ptr++) & 0x00FFFFFF;
|
||||
const u32 param = m_fifo.Pop() & 0x00FFFFFFu;
|
||||
|
||||
constexpr u32 gpustat_mask = (1 << 11) | (1 << 12);
|
||||
const u32 gpustat_bits = (param & 0x03) << 11;
|
||||
@ -216,172 +301,198 @@ bool GPU::HandleSetMaskBitCommand(const u32*& command_ptr, u32 command_size)
|
||||
Log_DebugPrintf("Set mask bit %u %u", BoolToUInt32(m_GPUSTAT.set_mask_while_drawing),
|
||||
BoolToUInt32(m_GPUSTAT.check_mask_before_draw));
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleRenderCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleRenderPolygonCommand()
|
||||
{
|
||||
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);
|
||||
CHECK_COMMAND_SIZE(total_words);
|
||||
|
||||
// set draw state up
|
||||
if (rc.texture_enable)
|
||||
{
|
||||
const u16 texpage_attribute = Truncate16((rc.shading_enable ? command_ptr[5] : command_ptr[4]) >> 16);
|
||||
SetDrawMode((texpage_attribute & DrawMode::Reg::POLYGON_TEXPAGE_MASK) |
|
||||
(m_draw_mode.mode_reg.bits & ~DrawMode::Reg::POLYGON_TEXPAGE_MASK));
|
||||
SetTexturePalette(Truncate16(command_ptr[2] >> 16));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Primitive::Line:
|
||||
{
|
||||
words_per_vertex = 1 + BoolToUInt8(rc.shading_enable);
|
||||
if (rc.polyline)
|
||||
{
|
||||
// polyline must have at least two vertices, and the terminator is (word & 0xf000f000) == 0x50005000. terminator
|
||||
// is on the first word for the vertex
|
||||
num_vertices = 2;
|
||||
bool found_terminator = false;
|
||||
for (u32 pos = rc.shading_enable ? 4 : 3; pos < command_size; pos += words_per_vertex)
|
||||
{
|
||||
if ((command_ptr[pos] & UINT32_C(0xF000F000)) == UINT32_C(0x50005000))
|
||||
{
|
||||
found_terminator = true;
|
||||
break;
|
||||
}
|
||||
|
||||
num_vertices++;
|
||||
}
|
||||
if (!found_terminator)
|
||||
return false;
|
||||
|
||||
total_words = words_per_vertex * num_vertices + BoolToUInt32(!rc.shading_enable) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
num_vertices = 2;
|
||||
total_words = words_per_vertex * num_vertices + BoolToUInt32(!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;
|
||||
|
||||
if (rc.texture_enable)
|
||||
SetTexturePalette(Truncate16(command_ptr[2] >> 16));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
UnreachableCode();
|
||||
return true;
|
||||
}
|
||||
const RenderCommand rc{m_fifo.Peek(0)};
|
||||
|
||||
// shaded vertices use the colour from the first word for the first vertex
|
||||
const u32 words_per_vertex = 1 + BoolToUInt32(rc.texture_enable) + BoolToUInt32(rc.shading_enable);
|
||||
const u32 num_vertices = rc.quad_polygon ? 4 : 3;
|
||||
const u32 total_words = words_per_vertex * num_vertices + BoolToUInt32(!rc.shading_enable);
|
||||
CHECK_COMMAND_SIZE(total_words);
|
||||
|
||||
static constexpr std::array<const char*, 4> primitive_names = {{"", "polygon", "line", "rectangle"}};
|
||||
|
||||
Log_TracePrintf("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<u8>(rc.primitive.GetValue())], ZeroExtend32(num_vertices),
|
||||
ZeroExtend32(words_per_vertex));
|
||||
|
||||
if (IsInterlacedRenderingEnabled() && IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
|
||||
DispatchRenderCommand(rc, num_vertices, command_ptr);
|
||||
command_ptr += total_words;
|
||||
Log_TracePrintf(
|
||||
"Render %s %s %s %s polygon (%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", ZeroExtend32(num_vertices), ZeroExtend32(words_per_vertex));
|
||||
|
||||
// set draw state up
|
||||
if (rc.texture_enable)
|
||||
{
|
||||
const u16 texpage_attribute = Truncate16((rc.shading_enable ? m_fifo.Peek(5) : m_fifo.Peek(4)) >> 16);
|
||||
SetDrawMode((texpage_attribute & DrawMode::Reg::POLYGON_TEXPAGE_MASK) |
|
||||
(m_draw_mode.mode_reg.bits & ~DrawMode::Reg::POLYGON_TEXPAGE_MASK));
|
||||
SetTexturePalette(Truncate16(m_fifo.Peek(2) >> 16));
|
||||
}
|
||||
|
||||
m_stats.num_vertices += num_vertices;
|
||||
m_stats.num_polygons++;
|
||||
m_render_command.bits = rc.bits;
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
DispatchRenderCommand();
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleRenderRectangleCommand()
|
||||
{
|
||||
const RenderCommand rc{m_fifo.Peek(0)};
|
||||
const u32 total_words =
|
||||
2 + BoolToUInt32(rc.texture_enable) + BoolToUInt32(rc.rectangle_size == DrawRectangleSize::Variable);
|
||||
|
||||
CHECK_COMMAND_SIZE(total_words);
|
||||
|
||||
if (IsInterlacedRenderingEnabled() && IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
|
||||
if (rc.texture_enable)
|
||||
SetTexturePalette(Truncate16(m_fifo.Peek(2) >> 16));
|
||||
|
||||
Log_TracePrintf("Render %s %s %s rectangle (%u words)", rc.transparency_enable ? "semi-transparent" : "opaque",
|
||||
rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome",
|
||||
total_words);
|
||||
|
||||
m_stats.num_vertices++;
|
||||
m_stats.num_polygons++;
|
||||
m_render_command.bits = rc.bits;
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
DispatchRenderCommand();
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleRenderLineCommand()
|
||||
{
|
||||
const RenderCommand rc{m_fifo.Peek(0)};
|
||||
const u32 total_words = rc.shading_enable ? 4 : 3;
|
||||
CHECK_COMMAND_SIZE(total_words);
|
||||
|
||||
if (IsInterlacedRenderingEnabled() && IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
|
||||
Log_TracePrintf("Render %s %s line (%u total words)", rc.transparency_enable ? "semi-transparent" : "opaque",
|
||||
rc.shading_enable ? "shaded" : "monochrome", total_words);
|
||||
|
||||
m_stats.num_vertices += 2;
|
||||
m_stats.num_polygons++;
|
||||
m_render_command.bits = rc.bits;
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
DispatchRenderCommand();
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleRenderPolyLineCommand()
|
||||
{
|
||||
// always read the first two vertices, we test for the terminator after that
|
||||
const RenderCommand rc{m_fifo.Peek(0)};
|
||||
const u32 min_words = rc.shading_enable ? 3 : 4;
|
||||
CHECK_COMMAND_SIZE(min_words);
|
||||
|
||||
if (IsInterlacedRenderingEnabled() && IsRasterScanlinePending())
|
||||
Synchronize();
|
||||
|
||||
Log_TracePrintf("Render %s %s poly-line", rc.transparency_enable ? "semi-transparent" : "opaque",
|
||||
rc.shading_enable ? "shaded" : "monochrome");
|
||||
|
||||
m_render_command.bits = rc.bits;
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
const u32 words_to_pop = min_words - 1;
|
||||
m_blit_buffer.resize(words_to_pop);
|
||||
m_fifo.PopRange(m_blit_buffer.data(), words_to_pop);
|
||||
|
||||
// polyline goes via a different path through the blit buffer
|
||||
m_blitter_state = BlitterState::DrawingPolyLine;
|
||||
m_command_total_words = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleFillRectangleCommand()
|
||||
{
|
||||
CHECK_COMMAND_SIZE(3);
|
||||
|
||||
FlushRender();
|
||||
|
||||
const u32 color = command_ptr[0] & 0x00FFFFFF;
|
||||
const u32 dst_x = command_ptr[1] & 0x3F0;
|
||||
const u32 dst_y = (command_ptr[1] >> 16) & 0x3FF;
|
||||
const u32 width = ((command_ptr[2] & 0x3FF) + 0xF) & ~0xF;
|
||||
const u32 height = (command_ptr[2] >> 16) & 0x1FF;
|
||||
command_ptr += 3;
|
||||
const u32 color = m_fifo.Pop() & 0x00FFFFFF;
|
||||
const u32 dst_x = m_fifo.Peek() & 0x3F0;
|
||||
const u32 dst_y = (m_fifo.Pop() >> 16) & 0x3FF;
|
||||
const u32 width = ((m_fifo.Peek() & 0x3FF) + 0xF) & ~0xF;
|
||||
const u32 height = (m_fifo.Pop() >> 16) & 0x1FF;
|
||||
|
||||
Log_DebugPrintf("Fill VRAM rectangle offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, width, height);
|
||||
|
||||
FillVRAM(dst_x, dst_y, width, height, color);
|
||||
m_stats.num_vram_fills++;
|
||||
AddCommandTicks(46 + ((width / 8) + 9) * height);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleCopyRectangleCPUToVRAMCommand()
|
||||
{
|
||||
CHECK_COMMAND_SIZE(3);
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
const u32 copy_width = ReplaceZero(command_ptr[2] & 0x3FF, 0x400);
|
||||
const u32 copy_height = ReplaceZero((command_ptr[2] >> 16) & 0x1FF, 0x200);
|
||||
const u32 dst_x = m_fifo.Peek() & 0x3FF;
|
||||
const u32 dst_y = (m_fifo.Pop() >> 16) & 0x3FF;
|
||||
const u32 copy_width = ReplaceZero(m_fifo.Peek() & 0x3FF, 0x400);
|
||||
const u32 copy_height = ReplaceZero((m_fifo.Pop() >> 16) & 0x1FF, 0x200);
|
||||
const u32 num_pixels = copy_width * copy_height;
|
||||
const u32 num_words = 3 + ((num_pixels + 1) / 2);
|
||||
if (command_size < num_words)
|
||||
{
|
||||
m_command_total_words = num_words;
|
||||
m_state = State::WritingVRAM;
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 dst_x = command_ptr[1] & 0x3FF;
|
||||
const u32 dst_y = (command_ptr[1] >> 16) & 0x3FF;
|
||||
const u32 num_words = ((num_pixels + 1) / 2);
|
||||
|
||||
Log_DebugPrintf("Copy rectangle from CPU to VRAM offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, copy_width,
|
||||
copy_height);
|
||||
|
||||
if (m_system->GetSettings().debugging.dump_cpu_to_vram_copies)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
|
||||
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;
|
||||
m_stats.num_vram_writes++;
|
||||
EndCommand();
|
||||
|
||||
m_blitter_state = BlitterState::WritingVRAM;
|
||||
m_blit_buffer.reserve(num_words);
|
||||
m_blit_remaining_words = num_words;
|
||||
m_vram_transfer.x = Truncate16(dst_x);
|
||||
m_vram_transfer.y = Truncate16(dst_y);
|
||||
m_vram_transfer.width = Truncate16(copy_width);
|
||||
m_vram_transfer.height = Truncate16(copy_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size)
|
||||
void GPU::FinishVRAMWrite()
|
||||
{
|
||||
if (m_system->GetSettings().debugging.dump_cpu_to_vram_copies)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
|
||||
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * m_vram_transfer.width,
|
||||
m_blit_buffer.data(), true);
|
||||
}
|
||||
|
||||
FlushRender();
|
||||
UpdateVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height, m_blit_buffer.data());
|
||||
m_blit_buffer.clear();
|
||||
m_vram_transfer = {};
|
||||
m_blitter_state = BlitterState::Idle;
|
||||
m_stats.num_vram_writes++;
|
||||
}
|
||||
|
||||
bool GPU::HandleCopyRectangleVRAMToCPUCommand()
|
||||
{
|
||||
CHECK_COMMAND_SIZE(3);
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
m_vram_transfer.width = ((Truncate16(command_ptr[2]) - 1) & 0x3FF) + 1;
|
||||
m_vram_transfer.height = ((Truncate16(command_ptr[2] >> 16) - 1) & 0x1FF) + 1;
|
||||
m_vram_transfer.x = Truncate16(command_ptr[1] & 0x3FF);
|
||||
m_vram_transfer.y = Truncate16((command_ptr[1] >> 16) & 0x3FF);
|
||||
command_ptr += 3;
|
||||
m_vram_transfer.x = Truncate16(m_fifo.Peek() & 0x3FF);
|
||||
m_vram_transfer.y = Truncate16((m_fifo.Pop() >> 16) & 0x3FF);
|
||||
m_vram_transfer.width = ((Truncate16(m_fifo.Peek()) - 1) & 0x3FF) + 1;
|
||||
m_vram_transfer.height = ((Truncate16(m_fifo.Pop() >> 16) - 1) & 0x1FF) + 1;
|
||||
|
||||
Log_DebugPrintf("Copy rectangle from VRAM to CPU offset=(%u,%u), size=(%u,%u)", m_vram_transfer.x, m_vram_transfer.y,
|
||||
m_vram_transfer.width, m_vram_transfer.height);
|
||||
@ -402,22 +513,22 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 comma
|
||||
|
||||
// switch to pixel-by-pixel read state
|
||||
m_stats.num_vram_reads++;
|
||||
m_state = State::ReadingVRAM;
|
||||
m_blitter_state = BlitterState::ReadingVRAM;
|
||||
m_command_total_words = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size)
|
||||
bool GPU::HandleCopyRectangleVRAMToVRAMCommand()
|
||||
{
|
||||
CHECK_COMMAND_SIZE(4);
|
||||
m_fifo.RemoveOne();
|
||||
|
||||
const u32 src_x = command_ptr[1] & 0x3FF;
|
||||
const u32 src_y = (command_ptr[1] >> 16) & 0x3FF;
|
||||
const u32 dst_x = command_ptr[2] & 0x3FF;
|
||||
const u32 dst_y = (command_ptr[2] >> 16) & 0x3FF;
|
||||
const u32 width = ReplaceZero(command_ptr[3] & 0x3FF, 0x400);
|
||||
const u32 height = ReplaceZero((command_ptr[3] >> 16) & 0x1FF, 0x200);
|
||||
command_ptr += 4;
|
||||
const u32 src_x = m_fifo.Peek() & 0x3FF;
|
||||
const u32 src_y = (m_fifo.Pop() >> 16) & 0x3FF;
|
||||
const u32 dst_x = m_fifo.Peek() & 0x3FF;
|
||||
const u32 dst_y = (m_fifo.Pop() >> 16) & 0x3FF;
|
||||
const u32 width = ReplaceZero(m_fifo.Peek() & 0x3FF, 0x400);
|
||||
const u32 height = ReplaceZero((m_fifo.Pop() >> 16) & 0x1FF, 0x200);
|
||||
|
||||
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);
|
||||
@ -425,6 +536,7 @@ bool GPU::HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 comm
|
||||
FlushRender();
|
||||
CopyVRAM(src_x, src_y, dst_x, dst_y, width, height);
|
||||
m_stats.num_vram_copies++;
|
||||
AddCommandTicks(width * height * 2);
|
||||
EndCommand();
|
||||
return true;
|
||||
}
|
||||
|
Reference in New Issue
Block a user