GPU: Rework pre-draw clipping

This commit is contained in:
Stenzek 2024-05-01 20:58:47 +10:00
parent 7e22fb08d2
commit d1483d8077
No known key found for this signature in database
3 changed files with 49 additions and 86 deletions

View file

@ -299,22 +299,6 @@ protected:
return (m_drawing_area.left <= m_drawing_area.right && m_drawing_area.top <= m_drawing_area.bottom); return (m_drawing_area.left <= m_drawing_area.right && m_drawing_area.top <= m_drawing_area.bottom);
} }
/// Clamps the specified coordinates to the drawing area.
ALWAYS_INLINE void ClampCoordinatesToDrawingArea(s32* x, s32* y)
{
const s32 x_value = *x;
if (x_value < static_cast<s32>(m_drawing_area.left))
*x = m_drawing_area.left;
else if (x_value >= static_cast<s32>(m_drawing_area.right))
*x = m_drawing_area.right - 1;
const s32 y_value = *y;
if (y_value < static_cast<s32>(m_drawing_area.top))
*y = m_drawing_area.top;
else if (y_value >= static_cast<s32>(m_drawing_area.bottom))
*y = m_drawing_area.bottom - 1;
}
void AddCommandTicks(TickCount ticks); void AddCommandTicks(TickCount ticks);
void WriteGP1(u32 value); void WriteGP1(u32 value);
@ -332,15 +316,21 @@ protected:
virtual void UpdateDisplay(); virtual void UpdateDisplay();
virtual void DrawRendererStats(); virtual void DrawRendererStats();
ALWAYS_INLINE void AddDrawTriangleTicks(s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, bool shaded, bool textured, ALWAYS_INLINE_RELEASE void AddDrawTriangleTicks(s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, bool shaded,
bool semitransparent) bool textured, bool semitransparent)
{ {
// This will not produce the correct results for triangles which are partially outside the clip area. // This will not produce the correct results for triangles which are partially outside the clip area.
// However, usually it'll undershoot not overshoot. If we wanted to make this more accurate, we'd need to intersect // However, usually it'll undershoot not overshoot. If we wanted to make this more accurate, we'd need to intersect
// the edges with the clip rectangle. // the edges with the clip rectangle.
ClampCoordinatesToDrawingArea(&x1, &y1); // TODO: Coordinates are exclusive, so off by one here...
ClampCoordinatesToDrawingArea(&x2, &y2); const s32 clip_right = static_cast<s32>(m_drawing_area.right) + 1;
ClampCoordinatesToDrawingArea(&x3, &y3); const s32 clip_bottom = static_cast<s32>(m_drawing_area.bottom) + 1;
x1 = std::clamp(x1, static_cast<s32>(m_drawing_area.left), clip_right);
x2 = std::clamp(x2, static_cast<s32>(m_drawing_area.left), clip_right);
x3 = std::clamp(x3, static_cast<s32>(m_drawing_area.left), clip_right);
y1 = std::clamp(y1, static_cast<s32>(m_drawing_area.top), clip_bottom);
y2 = std::clamp(y2, static_cast<s32>(m_drawing_area.top), clip_bottom);
y3 = std::clamp(y3, static_cast<s32>(m_drawing_area.top), clip_bottom);
TickCount pixels = std::abs((x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2) / 2); TickCount pixels = std::abs((x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2) / 2);
if (textured) if (textured)
@ -352,24 +342,44 @@ protected:
AddCommandTicks(pixels); AddCommandTicks(pixels);
} }
ALWAYS_INLINE void AddDrawRectangleTicks(u32 width, u32 height, bool textured, bool semitransparent) ALWAYS_INLINE_RELEASE void AddDrawRectangleTicks(s32 x, s32 y, u32 width, u32 height, bool textured,
bool semitransparent)
{ {
u32 ticks_per_row = width; // We do -1 on the inside of the clamp, in case the rectangle is entirely clipped.
u32 drawn_width = static_cast<u32>(
std::clamp<s32>(x + static_cast<s32>(width), static_cast<s32>(m_drawing_area.left),
static_cast<s32>(m_drawing_area.right) + 1) -
std::clamp<s32>(x, static_cast<s32>(m_drawing_area.left), static_cast<s32>(m_drawing_area.right) + 1));
u32 drawn_height = static_cast<u32>(
std::clamp<s32>(y + static_cast<s32>(height), static_cast<s32>(m_drawing_area.top),
static_cast<s32>(m_drawing_area.bottom) + 1) -
std::clamp<s32>(y, static_cast<s32>(m_drawing_area.top), static_cast<s32>(m_drawing_area.bottom) + 1));
u32 ticks_per_row = drawn_width;
if (textured) if (textured)
ticks_per_row += width; ticks_per_row += drawn_width;
if (semitransparent || m_GPUSTAT.check_mask_before_draw) if (semitransparent || m_GPUSTAT.check_mask_before_draw)
ticks_per_row += (width + 1u) / 2u; ticks_per_row += (drawn_width + 1u) / 2u;
if (m_GPUSTAT.SkipDrawingToActiveField()) if (m_GPUSTAT.SkipDrawingToActiveField())
height = std::max<u32>(height / 2, 1u); drawn_height = std::max<u32>(drawn_height / 2, 1u);
AddCommandTicks(ticks_per_row * height); AddCommandTicks(ticks_per_row * drawn_height);
} }
ALWAYS_INLINE void AddDrawLineTicks(u32 width, u32 height, bool shaded) ALWAYS_INLINE_RELEASE void AddDrawLineTicks(s32 min_x, s32 min_y, s32 max_x, s32 max_y, bool shaded)
{ {
if (m_GPUSTAT.SkipDrawingToActiveField()) // We do -1 on the inside of the clamp, in case the rectangle is entirely clipped.
height = std::max<u32>(height / 2, 1u); // Lines are inclusive?
u32 drawn_width = static_cast<u32>(
std::clamp<s32>(max_x + 1, static_cast<s32>(m_drawing_area.left), static_cast<s32>(m_drawing_area.right) + 1) -
std::clamp<s32>(min_x, static_cast<s32>(m_drawing_area.left), static_cast<s32>(m_drawing_area.right) + 1));
u32 drawn_height = static_cast<u32>(
std::clamp<s32>(max_y + 1, static_cast<s32>(m_drawing_area.top), static_cast<s32>(m_drawing_area.bottom) + 1) -
std::clamp<s32>(min_y, static_cast<s32>(m_drawing_area.top), static_cast<s32>(m_drawing_area.bottom) + 1));
AddCommandTicks(std::max(width, height)); if (m_GPUSTAT.SkipDrawingToActiveField())
drawn_height = std::max<u32>(drawn_height / 2, 1u);
AddCommandTicks(std::max(drawn_width, drawn_height));
} }
std::unique_ptr<TimingEvent> m_crtc_tick_event; std::unique_ptr<TimingEvent> m_crtc_tick_event;

View file

@ -2213,14 +2213,7 @@ void GPU_HW::LoadVertices()
} }
IncludeDrawnDirtyRectangle(pos_x, pos_y, pos_x + rectangle_width, pos_y + rectangle_height); IncludeDrawnDirtyRectangle(pos_x, pos_y, pos_x + rectangle_width, pos_y + rectangle_height);
AddDrawRectangleTicks(pos_x, pos_y, rectangle_width, rectangle_height, rc.texture_enable, rc.transparency_enable);
const u32 clip_left = static_cast<u32>(std::clamp<s32>(pos_x, m_drawing_area.left, m_drawing_area.right));
const u32 clip_right =
static_cast<u32>(std::clamp<s32>(pos_x + rectangle_width, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(pos_y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(pos_y + rectangle_height, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
AddDrawRectangleTicks(clip_right - clip_left, clip_bottom - clip_top, rc.texture_enable, rc.transparency_enable);
if (m_sw_renderer) if (m_sw_renderer)
{ {
@ -2276,15 +2269,8 @@ void GPU_HW::LoadVertices()
return; return;
} }
IncludeDrawnDirtyRectangle(min_x, min_y, max_x, max_y); IncludeDrawnDirtyRectangle(min_x, min_y, max_x + 1, max_y + 1);
AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
const u32 clip_left = static_cast<u32>(std::clamp<s32>(min_x, m_drawing_area.left, m_drawing_area.right));
const u32 clip_right = static_cast<u32>(std::clamp<s32>(max_x, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(min_y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(max_y, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
AddDrawLineTicks(clip_right - clip_left, clip_bottom - clip_top, rc.shading_enable);
// TODO: Should we do a PGXP lookup here? Most lines are 2D. // TODO: Should we do a PGXP lookup here? Most lines are 2D.
DrawLine(static_cast<float>(start_x), static_cast<float>(start_y), start_color, static_cast<float>(end_x), DrawLine(static_cast<float>(start_x), static_cast<float>(start_y), start_color, static_cast<float>(end_x),
@ -2343,16 +2329,8 @@ void GPU_HW::LoadVertices()
} }
else else
{ {
IncludeDrawnDirtyRectangle(min_x, min_y, max_x, max_y); IncludeDrawnDirtyRectangle(min_x, min_y, max_x + 1, max_y + 1);
AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
const u32 clip_left = static_cast<u32>(std::clamp<s32>(min_x, m_drawing_area.left, m_drawing_area.right));
const u32 clip_right =
static_cast<u32>(std::clamp<s32>(max_x, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(min_y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(max_y, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
AddDrawLineTicks(clip_right - clip_left, clip_bottom - clip_top, rc.shading_enable);
// TODO: Should we do a PGXP lookup here? Most lines are 2D. // TODO: Should we do a PGXP lookup here? Most lines are 2D.
DrawLine(static_cast<float>(start_x), static_cast<float>(start_y), start_color, static_cast<float>(end_x), DrawLine(static_cast<float>(start_x), static_cast<float>(start_y), start_color, static_cast<float>(end_x),

View file

@ -677,15 +677,7 @@ void GPU_SW::DispatchRenderCommand()
if (!IsDrawingAreaIsValid()) if (!IsDrawingAreaIsValid())
return; return;
const u32 clip_left = static_cast<u32>(std::clamp<s32>(cmd->x, m_drawing_area.left, m_drawing_area.right)); AddDrawRectangleTicks(cmd->x, cmd->y, cmd->width, cmd->height, rc.texture_enable, rc.transparency_enable);
const u32 clip_right =
static_cast<u32>(std::clamp<s32>(cmd->x + cmd->width, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(cmd->y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(cmd->y + cmd->height, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
// cmd->bounds.Set(Truncate16(clip_left), Truncate16(clip_top), Truncate16(clip_right), Truncate16(clip_bottom));
AddDrawRectangleTicks(clip_right - clip_left, clip_bottom - clip_top, rc.texture_enable, rc.transparency_enable);
m_backend.PushCommand(cmd); m_backend.PushCommand(cmd);
} }
@ -737,14 +729,7 @@ void GPU_SW::DispatchRenderCommand()
return; return;
} }
const u32 clip_left = static_cast<u32>(std::clamp<s32>(min_x, m_drawing_area.left, m_drawing_area.right)); AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
const u32 clip_right = static_cast<u32>(std::clamp<s32>(max_x, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(min_y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(max_y, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
// cmd->bounds.Set(Truncate16(clip_left), Truncate16(clip_top), Truncate16(clip_right),
// Truncate16(clip_bottom));
AddDrawLineTicks(clip_right - clip_left, clip_bottom - clip_top, rc.shading_enable);
m_backend.PushCommand(cmd); m_backend.PushCommand(cmd);
} }
@ -760,7 +745,6 @@ void GPU_SW::DispatchRenderCommand()
cmd->vertices[0].x = start_vp.x + m_drawing_offset.x; cmd->vertices[0].x = start_vp.x + m_drawing_offset.x;
cmd->vertices[0].y = start_vp.y + m_drawing_offset.y; cmd->vertices[0].y = start_vp.y + m_drawing_offset.y;
cmd->vertices[0].color = m_render_command.color_for_first_vertex; cmd->vertices[0].color = m_render_command.color_for_first_vertex;
// cmd->bounds.SetInvalid();
const bool shaded = m_render_command.shading_enable; const bool shaded = m_render_command.shading_enable;
for (u32 i = 1; i < num_vertices; i++) for (u32 i = 1; i < num_vertices; i++)
@ -780,16 +764,7 @@ void GPU_SW::DispatchRenderCommand()
} }
else else
{ {
const u32 clip_left = static_cast<u32>(std::clamp<s32>(min_x, m_drawing_area.left, m_drawing_area.right)); AddDrawLineTicks(min_x, min_y, max_x, max_y, rc.shading_enable);
const u32 clip_right =
static_cast<u32>(std::clamp<s32>(max_x, m_drawing_area.left, m_drawing_area.right)) + 1u;
const u32 clip_top = static_cast<u32>(std::clamp<s32>(min_y, m_drawing_area.top, m_drawing_area.bottom));
const u32 clip_bottom =
static_cast<u32>(std::clamp<s32>(max_y, m_drawing_area.top, m_drawing_area.bottom)) + 1u;
// cmd->bounds.Include(Truncate16(clip_left), Truncate16(clip_right), Truncate16(clip_top),
// Truncate16(clip_bottom));
AddDrawLineTicks(clip_right - clip_left, clip_bottom - clip_top, m_render_command.shading_enable);
} }
} }