Selaa lähdekoodia

LibGL+LibGPU+LibSoftGPU: Implement point and line drawing

Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.

In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:

* `set_coverage_mask`: the primitive needs to provide initial coverage
   mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
  need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
  shading is going to take place. All attributes like color, tex coords
  and fog depth need to be set so alpha testing and eventually,
  fragment rasterization can take place.

As of this commit, there are four instantiations of this function:

* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased

In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
Jelle Raaijmakers 3 vuotta sitten
vanhempi
commit
a20bf80b05

+ 52 - 0
Tests/LibGL/TestRender.cpp

@@ -117,3 +117,55 @@ TEST_CASE(0003_rect_w_coordinate_regression)
     context->present();
     expect_bitmap_equals_reference(context->frontbuffer(), "0003_rect_w_coordinate_regression");
 }
+
+TEST_CASE(0004_points)
+{
+    auto context = create_testing_context(64, 64);
+
+    // Aliased points
+    for (size_t i = 0; i < 3; ++i) {
+        glPointSize(1.f + i);
+        glBegin(GL_POINTS);
+        glVertex2f(-.5f + i * .5f, .5f);
+        glEnd();
+    }
+
+    // Anti-aliased points
+    glEnable(GL_POINT_SMOOTH);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    for (size_t i = 0; i < 3; ++i) {
+        glPointSize(3.f - i);
+        glBegin(GL_POINTS);
+        glVertex2f(-.5f + i * .5f, -.5f);
+        glEnd();
+    }
+
+    EXPECT_EQ(glGetError(), 0u);
+
+    context->present();
+    expect_bitmap_equals_reference(context->frontbuffer(), "0004_points");
+}
+
+TEST_CASE(0005_lines_antialiased)
+{
+    auto context = create_testing_context(64, 64);
+
+    // Draw anti-aliased lines
+    glEnable(GL_LINE_SMOOTH);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    glBegin(GL_LINES);
+    for (size_t i = 0; i < 6; ++i) {
+        glVertex2f(-.9f, .25f - i * .1f);
+        glVertex2f(.9f, .9f - i * .36f);
+    }
+    glEnd();
+
+    EXPECT_EQ(glGetError(), 0u);
+
+    context->present();
+    expect_bitmap_equals_reference(context->frontbuffer(), "0005_lines");
+}

BIN
Tests/LibGL/reference-images/0004_points.qoi


BIN
Tests/LibGL/reference-images/0005_lines.qoi


+ 12 - 15
Userland/Libraries/LibGL/GLContext.cpp

@@ -133,22 +133,8 @@ void GLContext::gl_end()
 
     // Make sure we had a `glBegin` before this call...
     RETURN_WITH_ERROR_IF(!m_in_draw_state, GL_INVALID_OPERATION);
-
     m_in_draw_state = false;
 
-    // FIXME: Add support for the remaining primitive types.
-    if (m_current_draw_mode != GL_TRIANGLES
-        && m_current_draw_mode != GL_TRIANGLE_FAN
-        && m_current_draw_mode != GL_TRIANGLE_STRIP
-        && m_current_draw_mode != GL_QUADS
-        && m_current_draw_mode != GL_QUAD_STRIP
-        && m_current_draw_mode != GL_POLYGON) {
-
-        m_vertex_list.clear_with_capacity();
-        dbgln_if(GL_DEBUG, "gl_end(): draw mode {:#x} unsupported", m_current_draw_mode);
-        RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
-    }
-
     Vector<size_t, 32> enabled_texture_units;
     for (size_t i = 0; i < m_texture_units.size(); ++i) {
         if (m_texture_units[i].texture_2d_enabled())
@@ -159,6 +145,18 @@ void GLContext::gl_end()
 
     GPU::PrimitiveType primitive_type;
     switch (m_current_draw_mode) {
+    case GL_LINE_LOOP:
+        primitive_type = GPU::PrimitiveType::LineLoop;
+        break;
+    case GL_LINE_STRIP:
+        primitive_type = GPU::PrimitiveType::LineStrip;
+        break;
+    case GL_LINES:
+        primitive_type = GPU::PrimitiveType::Lines;
+        break;
+    case GL_POINTS:
+        primitive_type = GPU::PrimitiveType::Points;
+        break;
     case GL_TRIANGLES:
         primitive_type = GPU::PrimitiveType::Triangles;
         break;
@@ -178,7 +176,6 @@ void GLContext::gl_end()
     }
 
     m_rasterizer->draw_primitives(primitive_type, m_model_view_matrix, m_projection_matrix, m_texture_matrix, m_vertex_list, enabled_texture_units);
-
     m_vertex_list.clear_with_capacity();
 }
 

+ 1 - 1
Userland/Libraries/LibGPU/Device.h

@@ -40,7 +40,7 @@ public:
 
     virtual DeviceInfo info() const = 0;
 
-    virtual void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units) = 0;
+    virtual void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex>& vertices, Vector<size_t> const& enabled_texture_units) = 0;
     virtual void resize(Gfx::IntSize const& min_size) = 0;
     virtual void clear_color(FloatVector4 const&) = 0;
     virtual void clear_depth(DepthType) = 0;

+ 5 - 1
Userland/Libraries/LibGPU/Enums.h

@@ -88,9 +88,13 @@ enum class WindingOrder {
 };
 
 enum class PrimitiveType {
+    Lines,
+    LineLoop,
+    LineStrip,
+    Points,
+    TriangleFan,
     Triangles,
     TriangleStrip,
-    TriangleFan,
     Quads,
 };
 

+ 40 - 0
Userland/Libraries/LibSoftGPU/Clipper.cpp

@@ -91,6 +91,46 @@ FLATTEN static constexpr void clip_plane(Vector<GPU::Vertex>& input_list, Vector
     }
 }
 
+void Clipper::clip_points_against_frustum(Vector<GPU::Vertex>& vertices)
+{
+    m_vertex_buffer.clear_with_capacity();
+
+    for (auto& vertex : vertices) {
+        auto const coords = vertex.clip_coordinates;
+        if (point_within_clip_plane<ClipPlane::LEFT>(coords) && point_within_clip_plane<ClipPlane::RIGHT>(coords)
+            && point_within_clip_plane<ClipPlane::TOP>(coords) && point_within_clip_plane<ClipPlane::BOTTOM>(coords)
+            && point_within_clip_plane<ClipPlane::NEAR>(coords) && point_within_clip_plane<ClipPlane::FAR>(coords))
+            m_vertex_buffer.append(vertex);
+    }
+
+    vertices.clear_with_capacity();
+    vertices.extend(m_vertex_buffer);
+}
+
+template<Clipper::ClipPlane plane>
+static constexpr bool constrain_line_within_plane(GPU::Vertex& from, GPU::Vertex& to)
+{
+    auto from_within_plane = point_within_clip_plane<plane>(from.clip_coordinates);
+    auto to_within_plane = point_within_clip_plane<plane>(to.clip_coordinates);
+    if (!from_within_plane && !to_within_plane)
+        return false;
+    if (!from_within_plane)
+        from = clip_intersection_point<plane>(from, to);
+    else if (!to_within_plane)
+        to = clip_intersection_point<plane>(from, to);
+    return true;
+}
+
+bool Clipper::clip_line_against_frustum(GPU::Vertex& from, GPU::Vertex& to)
+{
+    return constrain_line_within_plane<ClipPlane::LEFT>(from, to)
+        && constrain_line_within_plane<ClipPlane::RIGHT>(from, to)
+        && constrain_line_within_plane<ClipPlane::TOP>(from, to)
+        && constrain_line_within_plane<ClipPlane::BOTTOM>(from, to)
+        && constrain_line_within_plane<ClipPlane::NEAR>(from, to)
+        && constrain_line_within_plane<ClipPlane::FAR>(from, to);
+}
+
 void Clipper::clip_triangle_against_frustum(Vector<GPU::Vertex>& input_verts)
 {
     // FIXME C++23. Static reflection will provide looping over all enum values.

+ 2 - 0
Userland/Libraries/LibSoftGPU/Clipper.h

@@ -26,6 +26,8 @@ public:
 
     Clipper() = default;
 
+    void clip_points_against_frustum(Vector<GPU::Vertex>& vertices);
+    bool clip_line_against_frustum(GPU::Vertex& from, GPU::Vertex& to);
     void clip_triangle_against_frustum(Vector<GPU::Vertex>& input_vecs);
 
 private:

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 555 - 361
Userland/Libraries/LibSoftGPU/Device.cpp


+ 14 - 2
Userland/Libraries/LibSoftGPU/Device.h

@@ -47,7 +47,7 @@ public:
 
     virtual GPU::DeviceInfo info() const override;
 
-    virtual void draw_primitives(GPU::PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<GPU::Vertex> const& vertices, Vector<size_t> const& enabled_texture_units) override;
+    virtual void draw_primitives(GPU::PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<GPU::Vertex>& vertices, Vector<size_t> const& enabled_texture_units) override;
     virtual void resize(Gfx::IntSize const& min_size) override;
     virtual void clear_color(FloatVector4 const&) override;
     virtual void clear_depth(GPU::DepthType) override;
@@ -74,10 +74,22 @@ public:
     virtual void set_raster_position(FloatVector4 const& position, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform) override;
 
 private:
+    void calculate_vertex_lighting(GPU::Vertex& vertex) const;
     void draw_statistics_overlay(Gfx::Bitmap&);
     Gfx::IntRect get_rasterization_rect_of_size(Gfx::IntSize size) const;
 
-    void rasterize_triangle(Triangle const&);
+    template<typename CB1, typename CB2, typename CB3>
+    void rasterize(Gfx::IntRect& render_bounds, CB1 set_coverage_mask, CB2 set_quad_depth, CB3 set_quad_attributes);
+
+    void rasterize_line_aliased(GPU::Vertex&, GPU::Vertex&);
+    void rasterize_line_antialiased(GPU::Vertex&, GPU::Vertex&);
+    void rasterize_line(GPU::Vertex&, GPU::Vertex&);
+
+    void rasterize_point_aliased(GPU::Vertex&);
+    void rasterize_point_antialiased(GPU::Vertex&);
+    void rasterize_point(GPU::Vertex&);
+
+    void rasterize_triangle(Triangle&);
     void setup_blend_factors();
     void shade_fragments(PixelQuad&);
     bool test_alpha(PixelQuad&);

+ 15 - 8
Userland/Libraries/LibSoftGPU/PixelQuad.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -7,6 +8,7 @@
 #pragma once
 
 #include <AK/SIMD.h>
+#include <AK/SIMDExtras.h>
 #include <LibGfx/Vector2.h>
 #include <LibGfx/Vector3.h>
 #include <LibGfx/Vector4.h>
@@ -14,15 +16,20 @@
 
 namespace SoftGPU {
 
+using AK::SIMD::expand4;
+using AK::SIMD::f32x4;
+using AK::SIMD::i32x4;
+
 struct PixelQuad final {
-    Vector2<AK::SIMD::i32x4> screen_coordinates;
-    Vector3<AK::SIMD::f32x4> barycentrics;
-    AK::SIMD::f32x4 depth;
-    Vector4<AK::SIMD::f32x4> vertex_color;
-    Array<Vector4<AK::SIMD::f32x4>, GPU::NUM_SAMPLERS> texture_coordinates;
-    Vector4<AK::SIMD::f32x4> out_color;
-    AK::SIMD::f32x4 fog_depth;
-    AK::SIMD::i32x4 mask;
+    Vector2<i32x4> screen_coordinates;
+    Vector3<f32x4> barycentrics;
+    f32x4 depth;
+    Vector4<f32x4> vertex_color;
+    Array<Vector4<f32x4>, GPU::NUM_SAMPLERS> texture_coordinates;
+    Vector4<f32x4> out_color;
+    f32x4 fog_depth;
+    i32x4 mask;
+    f32x4 coverage { expand4(1.f) };
 };
 
 }

+ 13 - 0
Userland/Libraries/LibSoftGPU/SIMD.h

@@ -103,6 +103,11 @@ ALWAYS_INLINE static Vector2<AK::SIMD::f32x4> ddy(Vector2<AK::SIMD::f32x4> const
     };
 }
 
+ALWAYS_INLINE static AK::SIMD::f32x4 length(Vector2<AK::SIMD::f32x4> const& v)
+{
+    return AK::SIMD::sqrt(v.dot(v));
+}
+
 // Calculates a quadratic approximation of log2, exploiting the fact that IEEE754 floats are represented as mantissa * 2^exponent.
 // See https://stackoverflow.com/questions/9411823/fast-log2float-x-implementation-c
 ALWAYS_INLINE static AK::SIMD::f32x4 log2_approximate(AK::SIMD::f32x4 v)
@@ -124,4 +129,12 @@ ALWAYS_INLINE static AK::SIMD::f32x4 log2_approximate(AK::SIMD::f32x4 v)
     return log;
 }
 
+ALWAYS_INLINE static Vector2<AK::SIMD::f32x4> to_vec2_f32x4(Vector2<AK::SIMD::i32x4> const& v)
+{
+    return {
+        AK::SIMD::to_f32x4(v.x()),
+        AK::SIMD::to_f32x4(v.y()),
+    };
+}
+
 }

+ 0 - 2
Userland/Libraries/LibSoftGPU/Triangle.h

@@ -15,8 +15,6 @@ namespace SoftGPU {
 
 struct Triangle {
     GPU::Vertex vertices[3];
-    IntVector2 subpixel_coordinates[3];
-    i32 area;
 };
 
 }

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä