diff --git a/Userland/Libraries/LibGL/SoftwareRasterizer.cpp b/Userland/Libraries/LibGL/SoftwareRasterizer.cpp index 25d8fb21ba..aea228365b 100644 --- a/Userland/Libraries/LibGL/SoftwareRasterizer.cpp +++ b/Userland/Libraries/LibGL/SoftwareRasterizer.cpp @@ -38,6 +38,66 @@ static Gfx::RGBA32 to_rgba32(const FloatVector4& v) return a << 24 | b << 16 | g << 8 | r; } +static FloatVector4 to_vec4(Gfx::RGBA32 rgba) +{ + return { + (rgba & 0xff) / 255.0f, + ((rgba >> 8) & 0xff) / 255.0f, + ((rgba >> 16) & 0xff) / 255.0f, + ((rgba >> 24) & 0xff) / 255.0f + }; +} + +static constexpr void setup_blend_factors(GLenum mode, FloatVector4& constant, float& src_alpha, float& dst_alpha, float& src_color, float& dst_color) +{ + constant = { 0.0f, 0.0f, 0.0f, 0.0f }; + src_alpha = 0; + dst_alpha = 0; + src_color = 0; + dst_color = 0; + + switch (mode) { + case GL_ZERO: + break; + case GL_ONE: + constant = { 1.0f, 1.0f, 1.0f, 1.0f }; + break; + case GL_SRC_COLOR: + src_color = 1; + break; + case GL_ONE_MINUS_SRC_COLOR: + constant = { 1.0f, 1.0f, 1.0f, 1.0f }; + src_color = -1; + break; + case GL_SRC_ALPHA: + src_alpha = 1; + break; + case GL_ONE_MINUS_SRC_ALPHA: + constant = { 1.0f, 1.0f, 1.0f, 1.0f }; + src_alpha = -1; + break; + case GL_DST_ALPHA: + dst_alpha = -1; + break; + case GL_ONE_MINUS_DST_ALPHA: + constant = { 1.0f, 1.0f, 1.0f, 1.0f }; + dst_alpha = -1; + break; + case GL_DST_COLOR: + dst_color = 1; + break; + case GL_ONE_MINUS_DST_COLOR: + constant = { 1.0f, 1.0f, 1.0f, 1.0f }; + dst_color = -1; + break; + case GL_SRC_ALPHA_SATURATE: + // FIXME: How do we implement this? + break; + default: + VERIFY_NOT_REACHED(); + } +} + template static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& render_target, DepthBuffer& depth_buffer, const GLTriangle& triangle, PS pixel_shader) { @@ -57,6 +117,36 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re float one_over_area = 1.0f / area; + FloatVector4 src_constant {}; + float src_factor_src_alpha = 0; + float src_factor_dst_alpha = 0; + float src_factor_src_color = 0; + float src_factor_dst_color = 0; + + FloatVector4 dst_constant {}; + float dst_factor_src_alpha = 0; + float dst_factor_dst_alpha = 0; + float dst_factor_src_color = 0; + float dst_factor_dst_color = 0; + + if (options.enable_blending) { + setup_blend_factors( + options.blend_source_factor, + src_constant, + src_factor_src_alpha, + src_factor_dst_alpha, + src_factor_src_color, + src_factor_dst_color); + + setup_blend_factors( + options.blend_source_factor, + dst_constant, + dst_factor_src_alpha, + dst_factor_dst_alpha, + dst_factor_src_color, + dst_factor_dst_color); + } + // Obey top-left rule: // This sets up "zero" for later pixel coverage tests. // Depending on where on the triangle the edge is located @@ -97,6 +187,8 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re static_assert(RASTERIZER_BLOCK_SIZE < sizeof(int) * 8, "RASTERIZER_BLOCK_SIZE must be smaller than the pixel_mask's width in bits"); int pixel_mask[RASTERIZER_BLOCK_SIZE]; + FloatVector4 pixel_buffer[RASTERIZER_BLOCK_SIZE][RASTERIZER_BLOCK_SIZE]; + // Iterate over all blocks within the bounds of the triangle for (int by = by0; by < by1; by++) { for (int bx = bx0; bx < bx1; bx++) { @@ -191,7 +283,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re continue; } - auto* pixel = &render_target.scanline(y0 + y)[x0]; + auto* pixel = pixel_buffer[y]; for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, coords += dbdx, pixel++) { if (~pixel_mask[y] & (1 << x)) continue; @@ -215,7 +307,47 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re FloatVector2(triangle.vertices[2].u, triangle.vertices[2].v), barycentric); - *pixel = to_rgba32(pixel_shader(uv, rgba)); + *pixel = pixel_shader(uv, rgba); + } + } + + if (options.enable_blending) { + // Blend color values from pixel_buffer into render_target + for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++) { + auto src = pixel_buffer[y]; + auto dst = &render_target.scanline(y + y0)[x0]; + for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, src++, dst++) { + if (~pixel_mask[y] & (1 << x)) + continue; + + auto float_dst = to_vec4(*dst); + + auto src_factor = src_constant + + *src * src_factor_src_color + + FloatVector4(src->w(), src->w(), src->w(), src->w()) * src_factor_src_alpha + + float_dst * src_factor_dst_color + + FloatVector4(float_dst.w(), float_dst.w(), float_dst.w(), float_dst.w()) * src_factor_dst_alpha; + + auto dst_factor = dst_constant + + *src * dst_factor_src_color + + FloatVector4(src->w(), src->w(), src->w(), src->w()) * dst_factor_src_alpha + + float_dst * dst_factor_dst_color + + FloatVector4(float_dst.w(), float_dst.w(), float_dst.w(), float_dst.w()) * dst_factor_dst_alpha; + + *dst = to_rgba32(*src * src_factor + float_dst * dst_factor); + } + } + } else { + // Copy color values from pixel_buffer into render_target + for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++) { + auto src = pixel_buffer[y]; + auto dst = &render_target.scanline(y + y0)[x0]; + for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, src++, dst++) { + if (~pixel_mask[y] & (1 << x)) + continue; + + *dst = to_rgba32(*src); + } } } }