Sfoglia il codice sorgente

LibGL: Implement `glScissor()`

Jelle Raaijmakers 3 anni fa
parent
commit
bb58f6ccab

+ 8 - 0
Userland/Libraries/LibGL/DepthBuffer.cpp

@@ -33,4 +33,12 @@ void DepthBuffer::clear(float depth)
     }
 }
 
+void DepthBuffer::clear(Gfx::IntRect bounds, float depth)
+{
+    bounds.intersect({ 0, 0, m_size.width(), m_size.height() });
+    for (int y = bounds.top(); y <= bounds.bottom(); ++y)
+        for (int x = bounds.left(); x <= bounds.right(); ++x)
+            m_data[y * m_size.width() + x] = depth;
+}
+
 }

+ 2 - 0
Userland/Libraries/LibGL/DepthBuffer.h

@@ -6,6 +6,7 @@
 
 #pragma once
 
+#include <LibGfx/Rect.h>
 #include <LibGfx/Size.h>
 
 namespace GL {
@@ -18,6 +19,7 @@ public:
     float* scanline(int y);
 
     void clear(float depth);
+    void clear(Gfx::IntRect bounds, float depth);
 
 private:
     Gfx::IntSize m_size;

+ 5 - 0
Userland/Libraries/LibGL/GL/gl.h

@@ -290,6 +290,10 @@ extern "C" {
 #define GL_FOG_COLOR 0x0B66
 #define GL_FOG_DENSITY 0x0B62
 
+// Scissor enums
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_SCISSOR_TEST 0x0C11
+
 // OpenGL State & GLGet
 #define GL_MODELVIEW_MATRIX 0x0BA6
 
@@ -416,6 +420,7 @@ GLAPI void glFogfv(GLenum mode, GLfloat* params);
 GLAPI void glFogf(GLenum pname, GLfloat param);
 GLAPI void glFogi(GLenum pname, GLint param);
 GLAPI void glPixelStorei(GLenum pname, GLint param);
+GLAPI void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
 
 #ifdef __cplusplus
 }

+ 1 - 0
Userland/Libraries/LibGL/GLContext.h

@@ -86,6 +86,7 @@ public:
     virtual void gl_fogf(GLenum pname, GLfloat params) = 0;
     virtual void gl_fogi(GLenum pname, GLint param) = 0;
     virtual void gl_pixel_store(GLenum pname, GLfloat param) = 0;
+    virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height) = 0;
 
     virtual void present() = 0;
 };

+ 5 - 0
Userland/Libraries/LibGL/GLUtils.cpp

@@ -154,3 +154,8 @@ void glPixelStorei(GLenum pname, GLint param)
 {
     g_gl_context->gl_pixel_store(pname, param);
 }
+
+void glScissor(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+    g_gl_context->gl_scissor(x, y, width, height);
+}

+ 28 - 0
Userland/Libraries/LibGL/SoftwareGLContext.cpp

@@ -549,6 +549,10 @@ void SoftwareGLContext::gl_enable(GLenum capability)
         rasterizer_options.fog_enabled = true;
         update_rasterizer_options = true;
         break;
+    case GL_SCISSOR_TEST:
+        rasterizer_options.scissor_enabled = true;
+        update_rasterizer_options = true;
+        break;
     default:
         RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
     }
@@ -589,6 +593,10 @@ void SoftwareGLContext::gl_disable(GLenum capability)
         rasterizer_options.fog_enabled = false;
         update_rasterizer_options = true;
         break;
+    case GL_SCISSOR_TEST:
+        rasterizer_options.scissor_enabled = false;
+        update_rasterizer_options = true;
+        break;
     default:
         RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
     }
@@ -614,6 +622,8 @@ GLboolean SoftwareGLContext::gl_is_enabled(GLenum capability)
         return m_alpha_test_enabled;
     case GL_FOG:
         return rasterizer_options.fog_enabled;
+    case GL_SCISSOR_TEST:
+        return rasterizer_options.scissor_enabled;
     }
 
     RETURN_VALUE_WITH_ERROR_IF(true, GL_INVALID_ENUM, 0);
@@ -1472,6 +1482,14 @@ void SoftwareGLContext::gl_get_integerv(GLenum pname, GLint* data)
     case GL_MAX_TEXTURE_SIZE:
         *data = 4096;
         break;
+    case GL_SCISSOR_BOX: {
+        auto scissor_box = m_rasterizer.options().scissor_box;
+        *(data + 0) = scissor_box.x();
+        *(data + 1) = scissor_box.y();
+        *(data + 2) = scissor_box.width();
+        *(data + 3) = scissor_box.height();
+        break;
+    }
     default:
         // According to the Khronos docs, we always return GL_INVALID_ENUM if we encounter a non-accepted value
         // for `pname`
@@ -1966,6 +1984,16 @@ void SoftwareGLContext::gl_pixel_store(GLenum pname, GLfloat param)
     }
 }
 
+void SoftwareGLContext::gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+    APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_scissor, x, y, width, height);
+    RETURN_WITH_ERROR_IF(width < 0 || height < 0, GL_INVALID_VALUE);
+
+    auto options = m_rasterizer.options();
+    options.scissor_box = { x, y, width, height };
+    m_rasterizer.set_options(options);
+}
+
 void SoftwareGLContext::present()
 {
     m_rasterizer.blit_to(*m_frontbuffer);

+ 4 - 1
Userland/Libraries/LibGL/SoftwareGLContext.h

@@ -20,6 +20,7 @@
 #include <AK/Vector.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Matrix4x4.h>
+#include <LibGfx/Rect.h>
 #include <LibGfx/Vector3.h>
 
 namespace GL {
@@ -96,6 +97,7 @@ public:
     virtual void gl_fogf(GLenum pname, GLfloat param) override;
     virtual void gl_fogi(GLenum pname, GLint param) override;
     virtual void gl_pixel_store(GLenum pname, GLfloat param) override;
+    virtual void gl_scissor(GLint x, GLint y, GLsizei width, GLsizei height);
     virtual void present() override;
 
 private:
@@ -231,7 +233,8 @@ private:
             decltype(&SoftwareGLContext::gl_draw_arrays),
             decltype(&SoftwareGLContext::gl_draw_elements),
             decltype(&SoftwareGLContext::gl_depth_range),
-            decltype(&SoftwareGLContext::gl_polygon_offset)>;
+            decltype(&SoftwareGLContext::gl_polygon_offset),
+            decltype(&SoftwareGLContext::gl_scissor)>;
 
         using ExtraSavedArguments = Variant<
             FloatMatrix4x4>;

+ 27 - 9
Userland/Libraries/LibGL/SoftwareRasterizer.cpp

@@ -183,11 +183,15 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
     };
 
     // Calculate block-based bounds
+    auto render_bounds = render_target.rect();
+    if (options.scissor_enabled)
+        render_bounds.intersect(options.scissor_box);
+    int const block_padding = RASTERIZER_BLOCK_SIZE - 1;
     // clang-format off
-    const int bx0 = max(0,                      min(min(v0.x(), v1.x()), v2.x())                            ) / RASTERIZER_BLOCK_SIZE;
-    const int bx1 = min(render_target.width(),  max(max(v0.x(), v1.x()), v2.x()) + RASTERIZER_BLOCK_SIZE - 1) / RASTERIZER_BLOCK_SIZE;
-    const int by0 = max(0,                      min(min(v0.y(), v1.y()), v2.y())                            ) / RASTERIZER_BLOCK_SIZE;
-    const int by1 = min(render_target.height(), max(max(v0.y(), v1.y()), v2.y()) + RASTERIZER_BLOCK_SIZE - 1) / RASTERIZER_BLOCK_SIZE;
+    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;
     // clang-format on
 
     static_assert(RASTERIZER_BLOCK_SIZE < sizeof(int) * 8, "RASTERIZER_BLOCK_SIZE must be smaller than the pixel_mask's width in bits");
@@ -229,11 +233,10 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
             int y0 = by * RASTERIZER_BLOCK_SIZE;
 
             // Generate the coverage mask
-            if (test_point(b0) && test_point(b1) && test_point(b2) && test_point(b3)) {
+            if (!options.scissor_enabled && test_point(b0) && test_point(b1) && test_point(b2) && test_point(b3)) {
                 // The block is fully contained within the triangle. Fill the mask with all 1s
-                for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++) {
+                for (int y = 0; y < RASTERIZER_BLOCK_SIZE; y++)
                     pixel_mask[y] = -1;
-                }
             } else {
                 // The block overlaps at least one triangle edge.
                 // We need to test coverage of every pixel within the block.
@@ -242,7 +245,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
                     pixel_mask[y] = 0;
 
                     for (int x = 0; x < RASTERIZER_BLOCK_SIZE; x++, coords += dbdx) {
-                        if (test_point(coords))
+                        if (test_point(coords) && (!options.scissor_enabled || render_bounds.contains(x0 + x, y0 + y)))
                             pixel_mask[y] |= 1 << x;
                     }
                 }
@@ -481,6 +484,7 @@ SoftwareRasterizer::SoftwareRasterizer(const Gfx::IntSize& min_size)
     : m_render_target { Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, closest_multiple(min_size, RASTERIZER_BLOCK_SIZE)).release_value_but_fixme_should_propagate_errors() }
     , m_depth_buffer { adopt_own(*new DepthBuffer(closest_multiple(min_size, RASTERIZER_BLOCK_SIZE))) }
 {
+    m_options.scissor_box = m_render_target->rect();
 }
 
 void SoftwareRasterizer::submit_triangle(const GLTriangle& triangle, const Array<TextureUnit, 32>& texture_units)
@@ -559,14 +563,28 @@ void SoftwareRasterizer::clear_color(const FloatVector4& color)
     uint8_t g = static_cast<uint8_t>(clamp(color.y(), 0.0f, 1.0f) * 255);
     uint8_t b = static_cast<uint8_t>(clamp(color.z(), 0.0f, 1.0f) * 255);
     uint8_t a = static_cast<uint8_t>(clamp(color.w(), 0.0f, 1.0f) * 255);
+    auto const fill_color = Gfx::Color(r, g, b, a);
+
+    if (m_options.scissor_enabled) {
+        auto fill_rect = m_render_target->rect();
+        fill_rect.intersect(m_options.scissor_box);
+        Gfx::Painter painter { *m_render_target };
+        painter.fill_rect(fill_rect, fill_color);
+        return;
+    }
 
-    m_render_target->fill(Gfx::Color(r, g, b, a));
+    m_render_target->fill(fill_color);
 }
 
 void SoftwareRasterizer::clear_depth(float depth)
 {
     wait_for_all_threads();
 
+    if (m_options.scissor_enabled) {
+        m_depth_buffer->clear(m_options.scissor_box, depth);
+        return;
+    }
+
     m_depth_buffer->clear(depth);
 }
 

+ 3 - 0
Userland/Libraries/LibGL/SoftwareRasterizer.h

@@ -14,6 +14,7 @@
 #include <AK/Array.h>
 #include <AK/OwnPtr.h>
 #include <LibGfx/Bitmap.h>
+#include <LibGfx/Rect.h>
 #include <LibGfx/Vector4.h>
 
 namespace GL {
@@ -44,6 +45,8 @@ struct RasterizerOptions {
     GLboolean fog_enabled { false };
     GLfloat fog_start { 0.0f };
     GLfloat fog_end { 1.0f };
+    bool scissor_enabled { false };
+    Gfx::IntRect scissor_box {};
     GLenum draw_buffer { GL_BACK };
     GLfloat depth_offset_factor { 0 };
     GLfloat depth_offset_constant { 0 };