GPU: Base work for hardware renderer

This commit is contained in:
Connor McLaughlin
2019-09-12 12:53:04 +10:00
parent c0853de6a6
commit 4706a906d5
10 changed files with 627 additions and 131 deletions

View File

@ -4,14 +4,6 @@
#include "dma.h"
Log_SetChannel(GPU);
static constexpr s32 S11ToS32(u32 value)
{
if (value & (UINT16_C(1) << 10))
return static_cast<s32>(UINT32_C(0xFFFFF800) | value);
else
return value;
}
GPU::GPU() = default;
GPU::~GPU() = default;
@ -109,96 +101,92 @@ u32 GPU::ReadGPUREAD()
void GPU::WriteGP0(u32 value)
{
Assert(m_GP0_command_length < MAX_GP0_COMMAND_LENGTH);
m_GP0_command[m_GP0_command_length++] = value;
m_GP0_command.push_back(value);
Assert(m_GP0_command.size() <= 128);
const u8 command = Truncate8(m_GP0_command[0] >> 24);
const u32 param = m_GP0_command[0] & UINT32_C(0x00FFFFFF);
switch (command)
if (command >= 0x20 && command <= 0x7F)
{
case 0x00: // NOP
// Draw polygon
if (!HandleRenderCommand())
return;
}
else
{
switch (command)
{
case 0x00: // NOP
break;
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_texture_config.x_flip = (param & (UINT32_C(1) << 12)) != 0;
m_texture_config.y_flip = (param & (UINT32_C(1) << 13)) != 0;
Log_DebugPrintf("Set draw mode %08X", param);
}
break;
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_texture_config.x_flip = (param & (UINT32_C(1) << 12)) != 0;
m_texture_config.y_flip = (param & (UINT32_C(1) << 13)) != 0;
Log_DebugPrintf("Set draw mode %08X", param);
}
break;
case 0xE2: // set texture window
{
m_texture_config.window_mask_x = param & UINT32_C(0x1F);
m_texture_config.window_mask_y = (param >> 5) & UINT32_C(0x1F);
m_texture_config.window_offset_x = (param >> 10) & UINT32_C(0x1F);
m_texture_config.window_offset_y = (param >> 15) & UINT32_C(0x1F);
Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_texture_config.window_mask_x,
m_texture_config.window_mask_y, m_texture_config.window_offset_x,
m_texture_config.window_offset_y);
}
break;
case 0xE3: // Set drawing area top left
{
m_drawing_area.top_left_x = param & UINT32_C(0x3FF);
m_drawing_area.top_left_y = (param >> 10) & UINT32_C(0x1FF);
Log_DebugPrintf("Set drawing area top-left: (%u, %u)", m_drawing_area.top_left_x, m_drawing_area.top_left_y);
}
break;
case 0xE4: // Set drawing area bottom right
{
m_drawing_area.bottom_right_x = param & UINT32_C(0x3FF);
m_drawing_area.bottom_right_y = (param >> 10) & UINT32_C(0x1FF);
Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.bottom_right_x,
m_drawing_area.bottom_right_y);
}
break;
case 0xE5: // Set drawing offset
{
m_drawing_offset.x = S11ToS32(param & UINT32_C(0x7FF));
m_drawing_offset.y = S11ToS32((param >> 11) & UINT32_C(0x7FF));
Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y);
}
break;
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));
}
break;
default:
{
if (command < 0x20)
case 0xE2: // set texture window
{
m_texture_config.window_mask_x = param & UINT32_C(0x1F);
m_texture_config.window_mask_y = (param >> 5) & UINT32_C(0x1F);
m_texture_config.window_offset_x = (param >> 10) & UINT32_C(0x1F);
m_texture_config.window_offset_y = (param >> 15) & UINT32_C(0x1F);
Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_texture_config.window_mask_x,
m_texture_config.window_mask_y, m_texture_config.window_offset_x,
m_texture_config.window_offset_y);
}
else if (command < 0x40)
break;
case 0xE3: // Set drawing area top left
{
// Draw polygon
if (!HandleRenderPolygonCommand())
return;
break;
m_drawing_area.top_left_x = param & UINT32_C(0x3FF);
m_drawing_area.top_left_y = (param >> 10) & UINT32_C(0x1FF);
Log_DebugPrintf("Set drawing area top-left: (%u, %u)", m_drawing_area.top_left_x, m_drawing_area.top_left_y);
}
break;
Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command);
case 0xE4: // Set drawing area bottom right
{
m_drawing_area.bottom_right_x = param & UINT32_C(0x3FF);
m_drawing_area.bottom_right_y = (param >> 10) & UINT32_C(0x1FF);
Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.bottom_right_x,
m_drawing_area.bottom_right_y);
}
break;
case 0xE5: // Set drawing offset
{
m_drawing_offset.x = S11ToS32(param & UINT32_C(0x7FF));
m_drawing_offset.y = S11ToS32((param >> 11) & UINT32_C(0x7FF));
Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y);
}
break;
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));
}
break;
default:
{
Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command);
}
break;
}
break;
}
m_GP0_command.fill(UINT32_C(0));
m_GP0_command_length = 0;
m_GP0_command.clear();
}
void GPU::WriteGP1(u32 value)
@ -221,25 +209,84 @@ void GPU::WriteGP1(u32 value)
}
}
bool GPU::HandleRenderPolygonCommand()
bool GPU::HandleRenderCommand()
{
const u8 command = Truncate8(m_GP0_command[0] >> 24);
const bool semi_transparent = !!(command & 0x02);
const bool textured = !!(command & 0x04);
const bool four_points = !!(command & 0x08);
const bool shaded = !!(command & 0x10);
// shaded vertices use the colour from the first word for the first vertex
const u8 words_per_vertex = 1 + BoolToUInt8(textured) + BoolToUInt8(shaded);
const u8 num_vertices = four_points ? 4 : 3;
const u8 total_words = words_per_vertex * num_vertices + BoolToUInt8(!shaded);
if (m_GP0_command_length < total_words)
const RenderCommand rc{m_GP0_command[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 (size_t pos = 0; pos < m_GP0_command.size(); pos += words_per_vertex)
{
if (m_GP0_command[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 =
1 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.rectangle_size == DrawRectangleSize::Variable);
num_vertices = 1;
total_words = words_per_vertex;
}
break;
default:
UnreachableCode();
return true;
}
if (m_GP0_command.size() < total_words)
return false;
Log_DebugPrintf("Render %s %s %s %s polygon (%u verts, %u words per vert)",
four_points ? "four-point" : "three-point", semi_transparent ? "semi-transparent" : "opaque",
textured ? "textured" : "non-textured", shaded ? "shaded" : "monochrome", ZeroExtend32(num_vertices),
static constexpr std::array<const char*, 4> 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<u8>(rc.primitive.GetValue())], ZeroExtend32(num_vertices),
ZeroExtend32(words_per_vertex));
DispatchRenderCommand(rc, num_vertices);
return true;
}
void GPU::DispatchRenderCommand(RenderCommand rc, u32 num_vertices) {}
void GPU::FlushRender() {}