Browse Source

LibGL: Implement color blending

This implements different blend modes in the SoftwareRasterizer by
first setting up the blend factors then rendering the pixels into a
temporary buffer and finally mixing the contents of the temporary buffer
with the contents of the backbuffer based on the blend factors.
Stephan Unverwerth 4 years ago
parent
commit
f70a6ff712
1 changed files with 134 additions and 2 deletions
  1. 134 2
      Userland/Libraries/LibGL/SoftwareRasterizer.cpp

+ 134 - 2
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<typename PS>
 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);
+                    }
                 }
             }
         }