From 8ae3eb6c33cfd506905c452f8295b5bc87b65c98 Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Thu, 30 Dec 2021 18:40:20 +0100 Subject: [PATCH] LibSoftGPU: Implement 5 bits of subpixel precision This snaps vertices to 1/32 of a pixel before rasterization resulting in smoother movement and less floaty appearance of moving triangles. This also reduces the severity of the artifacts in the glquake port. 5 bits should allow up to 1024x1024 render targets. Anything larger needs a different implementation. --- Userland/Libraries/LibSoftGPU/Config.h | 1 + Userland/Libraries/LibSoftGPU/Device.cpp | 26 +++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Userland/Libraries/LibSoftGPU/Config.h b/Userland/Libraries/LibSoftGPU/Config.h index 0292a153cd..52bda0c8cf 100644 --- a/Userland/Libraries/LibSoftGPU/Config.h +++ b/Userland/Libraries/LibSoftGPU/Config.h @@ -17,6 +17,7 @@ namespace SoftGPU { static constexpr bool ENABLE_STATISTICS_OVERLAY = false; static constexpr int RASTERIZER_BLOCK_SIZE = 8; static constexpr int NUM_SAMPLERS = 32; +static constexpr int SUBPIXEL_BITS = 5; // See: https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_edge_color_problem // FIXME: make this dynamically configurable through ConfigServer diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index d7a9285a61..8ed8c3c0e5 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/Userland/Libraries/LibSoftGPU/Device.cpp @@ -130,10 +130,12 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re Vertex const vertex1 = triangle.vertices[1]; Vertex const vertex2 = triangle.vertices[2]; + constexpr int subpixel_factor = 1 << SUBPIXEL_BITS; + // Calculate area of the triangle for later tests - IntVector2 const v0 { static_cast(vertex0.window_coordinates.x()), static_cast(vertex0.window_coordinates.y()) }; - IntVector2 const v1 { static_cast(vertex1.window_coordinates.x()), static_cast(vertex1.window_coordinates.y()) }; - IntVector2 const v2 { static_cast(vertex2.window_coordinates.x()), static_cast(vertex2.window_coordinates.y()) }; + IntVector2 const v0 { static_cast(vertex0.window_coordinates.x() * subpixel_factor), static_cast(vertex0.window_coordinates.y() * subpixel_factor) }; + IntVector2 const v1 { static_cast(vertex1.window_coordinates.x() * subpixel_factor), static_cast(vertex1.window_coordinates.y() * subpixel_factor) }; + IntVector2 const v2 { static_cast(vertex2.window_coordinates.x() * subpixel_factor), static_cast(vertex2.window_coordinates.y() * subpixel_factor) }; int area = edge_function(v0, v1, v2); if (area == 0) @@ -204,12 +206,12 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re auto render_bounds = render_target.rect(); if (options.scissor_enabled) render_bounds.intersect(scissor_box_to_window_coordinates(options.scissor_box, render_target.rect())); - int const block_padding = RASTERIZER_BLOCK_SIZE - 1; + // clang-format off - int const bx0 = max(render_bounds.left(), min(min(v0.x(), v1.x()), v2.x())) / RASTERIZER_BLOCK_SIZE; - int const bx1 = (min(render_bounds.right(), max(max(v0.x(), v1.x()), v2.x())) + block_padding) / RASTERIZER_BLOCK_SIZE; - int const by0 = max(render_bounds.top(), min(min(v0.y(), v1.y()), v2.y())) / RASTERIZER_BLOCK_SIZE; - int const by1 = (min(render_bounds.bottom(), max(max(v0.y(), v1.y()), v2.y())) + block_padding) / RASTERIZER_BLOCK_SIZE; + int const bx0 = max(render_bounds.left(), min(min(v0.x(), v1.x()), v2.x()) / subpixel_factor) / RASTERIZER_BLOCK_SIZE; + int const bx1 = (min(render_bounds.right(), max(max(v0.x(), v1.x()), v2.x()) / subpixel_factor)) / RASTERIZER_BLOCK_SIZE + 1; + int const by0 = max(render_bounds.top(), min(min(v0.y(), v1.y()), v2.y()) / subpixel_factor) / RASTERIZER_BLOCK_SIZE; + int const by1 = (min(render_bounds.bottom(), max(max(v0.y(), v1.y()), v2.y()) / subpixel_factor)) / RASTERIZER_BLOCK_SIZE + 1; // clang-format on u8 pixel_mask[RASTERIZER_BLOCK_SIZE]; @@ -231,10 +233,10 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re // Edge values of the 4 block corners // clang-format off - auto b0 = calculate_edge_values({ bx * RASTERIZER_BLOCK_SIZE, by * RASTERIZER_BLOCK_SIZE }); - auto b1 = calculate_edge_values({ bx * RASTERIZER_BLOCK_SIZE + RASTERIZER_BLOCK_SIZE, by * RASTERIZER_BLOCK_SIZE }); - auto b2 = calculate_edge_values({ bx * RASTERIZER_BLOCK_SIZE, by * RASTERIZER_BLOCK_SIZE + RASTERIZER_BLOCK_SIZE }); - auto b3 = calculate_edge_values({ bx * RASTERIZER_BLOCK_SIZE + RASTERIZER_BLOCK_SIZE, by * RASTERIZER_BLOCK_SIZE + RASTERIZER_BLOCK_SIZE }); + auto b0 = calculate_edge_values(IntVector2{ bx, by } * RASTERIZER_BLOCK_SIZE * subpixel_factor); + auto b1 = calculate_edge_values(IntVector2{ bx + 1, by } * RASTERIZER_BLOCK_SIZE * subpixel_factor); + auto b2 = calculate_edge_values(IntVector2{ bx, by + 1 } * RASTERIZER_BLOCK_SIZE * subpixel_factor); + auto b3 = calculate_edge_values(IntVector2{ bx + 1, by + 1 } * RASTERIZER_BLOCK_SIZE * subpixel_factor); // clang-format on // If the whole block is outside any of the triangle edges we can discard it completely