diff options
author | Jelle Raaijmakers <jelle@gmta.nl> | 2022-01-23 22:15:28 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-02-22 23:48:59 +0000 |
commit | db0616c67a1a046b460e73b8e3b6861d982c5f46 (patch) | |
tree | f2cd91b677003d69d9487a4c7a1543860efe6b31 | |
parent | 72ec2c21f41576162117425ccf1b2f707f1ecac7 (diff) | |
download | serenity-db0616c67a1a046b460e73b8e3b6861d982c5f46.zip |
LibSoftGPU: Generalize pixel buffers and standardize on BGRA8888
Between the OpenGL client and server, a lot of data type and color
conversion needs to happen. We are performing these conversions both in
`LibSoftGPU` and `LibGL`, which is not ideal. Additionally, some
concepts like the color, depth and stencil buffers should share their
logic but have separate implementations.
This is the first step towards generalizing our `LibSoftGPU` frame
buffer: a generalized `Typed3DBuffer` is introduced for arbitrary 3D
value storage and retrieval, and `Typed2DBuffer` wraps around it to
provide in an easy-to-use 2D pixel buffer. The color, depth and stencil
buffers are replaced by `Typed2DBuffer` and are now managed by the new
`FrameBuffer` class.
The `Image` class now uses multiple `Typed3DBuffer`s for layers and
mipmap levels. Additionally, the textures are now always stored as
BGRA8888, only converting between formats when reading or writing
pixels.
Ideally this refactor should have no functional changes, but some
graphical glitches in Grim Fandango seem to be fixed and most OpenGL
ports get an FPS boost on my machine. :^)
-rw-r--r-- | Userland/Libraries/LibGL/SoftwareGLContext.cpp | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h | 57 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h | 73 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h | 72 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/DepthBuffer.cpp | 44 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/DepthBuffer.h | 29 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Device.cpp | 154 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Device.h | 34 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Enums.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Image.cpp | 30 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Image.h | 46 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/StencilBuffer.cpp | 41 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/StencilBuffer.h | 33 |
14 files changed, 336 insertions, 313 deletions
diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index 5b2b0b1bea..89c3de6de1 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/Userland/Libraries/LibGL/SoftwareGLContext.cpp @@ -19,6 +19,7 @@ #include <LibGfx/Vector4.h> #include <LibSoftGPU/Device.h> #include <LibSoftGPU/Enums.h> +#include <LibSoftGPU/ImageFormat.h> namespace GL { @@ -982,28 +983,7 @@ void SoftwareGLContext::gl_tex_image_2d(GLenum target, GLint level, GLint intern // that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized. // To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time. // All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete. - SoftGPU::ImageFormat device_format; - switch (internal_format) { - case GL_RGB: - device_format = SoftGPU::ImageFormat::RGB888; - break; - - case GL_RGBA: - device_format = SoftGPU::ImageFormat::RGBA8888; - break; - - case GL_LUMINANCE8: - device_format = SoftGPU::ImageFormat::L8; - break; - - case GL_LUMINANCE8_ALPHA8: - device_format = SoftGPU::ImageFormat::L8A8; - break; - - default: - VERIFY_NOT_REACHED(); - } - m_active_texture_unit->bound_texture_2d()->set_device_image(m_rasterizer.create_image(device_format, width, height, 1, 999, 1)); + m_active_texture_unit->bound_texture_2d()->set_device_image(m_rasterizer.create_image(SoftGPU::ImageFormat::BGRA8888, width, height, 1, 999, 1)); m_sampler_config_is_dirty = true; } @@ -1730,7 +1710,7 @@ void SoftwareGLContext::gl_read_pixels(GLint x, GLint y, GLsizei width, GLsizei else color = m_frontbuffer->scanline(y + i)[x + j]; } else { - color = m_rasterizer.get_backbuffer_pixel(x + j, y + i); + color = m_rasterizer.get_color_buffer_pixel(x + j, y + i); } float red = ((color >> 24) & 0xff) / 255.0f; @@ -2979,7 +2959,7 @@ void SoftwareGLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat co void SoftwareGLContext::present() { - m_rasterizer.blit_to(*m_frontbuffer); + m_rasterizer.blit_color_buffer_to(*m_frontbuffer); } void SoftwareGLContext::sync_device_config() diff --git a/Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h b/Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h new file mode 100644 index 0000000000..7945339fe9 --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Error.h> +#include <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <LibGfx/Rect.h> +#include <LibGfx/Size.h> +#include <LibSoftGPU/Buffer/Typed2DBuffer.h> + +namespace SoftGPU { + +/* + * The frame buffer is a 2D buffer that consists of: + * - color buffer(s); (FIXME: implement multiple color buffers) + * - depth buffer; + * - stencil buffer; + * - accumulation buffer. (FIXME: implement accumulation buffer) + */ +template<typename C, typename D, typename S> +class FrameBuffer final : public RefCounted<FrameBuffer<C, D, S>> { +public: + static ErrorOr<NonnullRefPtr<FrameBuffer<C, D, S>>> try_create(Gfx::IntSize const& size) + { + Gfx::IntRect rect = { 0, 0, size.width(), size.height() }; + auto color_buffer = TRY(Typed2DBuffer<C>::try_create(size)); + auto depth_buffer = TRY(Typed2DBuffer<D>::try_create(size)); + auto stencil_buffer = TRY(Typed2DBuffer<S>::try_create(size)); + return adopt_ref(*new FrameBuffer(rect, color_buffer, depth_buffer, stencil_buffer)); + } + + NonnullRefPtr<Typed2DBuffer<C>> color_buffer() { return m_color_buffer; } + NonnullRefPtr<Typed2DBuffer<D>> depth_buffer() { return m_depth_buffer; } + NonnullRefPtr<Typed2DBuffer<S>> stencil_buffer() { return m_stencil_buffer; } + Gfx::IntRect rect() const { return m_rect; } + +private: + FrameBuffer(Gfx::IntRect rect, NonnullRefPtr<Typed2DBuffer<C>> color_buffer, NonnullRefPtr<Typed2DBuffer<D>> depth_buffer, NonnullRefPtr<Typed2DBuffer<S>> stencil_buffer) + : m_color_buffer(color_buffer) + , m_depth_buffer(depth_buffer) + , m_stencil_buffer(stencil_buffer) + , m_rect(rect) + { + } + + NonnullRefPtr<Typed2DBuffer<C>> m_color_buffer; + NonnullRefPtr<Typed2DBuffer<D>> m_depth_buffer; + NonnullRefPtr<Typed2DBuffer<S>> m_stencil_buffer; + Gfx::IntRect m_rect; +}; + +} diff --git a/Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h b/Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h new file mode 100644 index 0000000000..b056f4257e --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Error.h> +#include <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <AK/Try.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/Rect.h> +#include <LibGfx/Size.h> +#include <LibSoftGPU/Buffer/Typed3DBuffer.h> + +namespace SoftGPU { + +/** + * Typed2DBuffer<T> wraps TypedBuffer<T> and only interacts on the 2D plane with z = 0. + */ +template<typename T> +class Typed2DBuffer final : public RefCounted<Typed2DBuffer<T>> { +public: + static ErrorOr<NonnullRefPtr<Typed2DBuffer>> try_create(Gfx::IntSize const& size) + { + auto buffer = TRY(Typed3DBuffer<T>::try_create(size.width(), size.height(), 1)); + return adopt_ref(*new Typed2DBuffer(buffer)); + } + + void fill(T value, Gfx::IntRect const& rect) { m_buffer->fill(value, rect.left(), rect.right(), rect.top(), rect.bottom(), 0, 0); } + ALWAYS_INLINE T* scanline(int y) { return m_buffer->buffer_pointer(0, y, 0); } + ALWAYS_INLINE T const* scanline(int y) const { return m_buffer->buffer_pointer(0, y, 0); } + + void blit_from_bitmap(Gfx::Bitmap const& bitmap, Gfx::IntRect const& target) requires IsSame<T, u32> + { + VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888 || bitmap.format() == Gfx::BitmapFormat::BGRx8888); + int source_y = 0; + for (int y = target.top(); y <= target.bottom(); ++y) { + auto* buffer_scanline = scanline(y); + auto const* bitmap_scanline = bitmap.scanline(source_y++); + + int source_x = 0; + for (int x = target.left(); x <= target.right(); ++x) + buffer_scanline[x] = bitmap_scanline[source_x++]; + } + } + + void blit_to_bitmap(Gfx::Bitmap& bitmap, Gfx::IntRect const& target) const requires IsSame<T, u32> + { + VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888 || bitmap.format() == Gfx::BitmapFormat::BGRx8888); + int source_y = 0; + for (int y = target.top(); y <= target.bottom(); ++y) { + auto const* buffer_scanline = scanline(source_y++); + auto* bitmap_scanline = bitmap.scanline(y); + + int source_x = 0; + for (int x = target.left(); x <= target.right(); ++x) + bitmap_scanline[x] = buffer_scanline[source_x++]; + } + } + +private: + Typed2DBuffer(NonnullRefPtr<Typed3DBuffer<T>> buffer) + : m_buffer(buffer) + { + } + + NonnullRefPtr<Typed3DBuffer<T>> m_buffer; +}; + +} diff --git a/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h b/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h new file mode 100644 index 0000000000..567ffdd6ad --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Error.h> +#include <AK/FixedArray.h> +#include <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <AK/Try.h> + +namespace SoftGPU { + +/** + * TypedBuffer<T> is a generic 3D buffer that can be used to store + * values of a specific type at X, Y and Z coordinates. It is used as + * storage for images, and frame, depth and stencil buffers. + */ +template<typename T> +class Typed3DBuffer final : public RefCounted<Typed3DBuffer<T>> { +public: + static ErrorOr<NonnullRefPtr<Typed3DBuffer<T>>> try_create(int width, int height, int depth) + { + VERIFY(width > 0 && height > 0 && depth > 0); + auto data = TRY(FixedArray<T>::try_create(width * height * depth)); + return adopt_ref(*new Typed3DBuffer(width, height, depth, move(data))); + } + + ALWAYS_INLINE T* buffer_pointer(int x, int y, int z) + { + return &m_data[z * m_width * m_height + y * m_width + x]; + } + + ALWAYS_INLINE T const* buffer_pointer(int x, int y, int z) const + { + return &m_data[z * m_width * m_height + y * m_width + x]; + } + + void fill(T value, int x1, int x2, int y1, int y2, int z1, int z2) + { + for (auto z = z1; z <= z2; ++z) { + for (auto y = y1; y <= y2; ++y) { + auto* xline = buffer_pointer(0, y, z); + for (auto x = x1; x <= x2; ++x) + xline[x] = value; + } + } + } + + int depth() const { return m_depth; } + int height() const { return m_height; } + int width() const { return m_width; } + +private: + Typed3DBuffer(int width, int height, int depth, FixedArray<T> data) + : m_data(move(data)) + , m_depth(depth) + , m_height(height) + , m_width(width) + { + } + + FixedArray<T> m_data; + int m_depth; + int m_height; + int m_width; +}; + +} diff --git a/Userland/Libraries/LibSoftGPU/CMakeLists.txt b/Userland/Libraries/LibSoftGPU/CMakeLists.txt index 03ee632187..bc99282ec6 100644 --- a/Userland/Libraries/LibSoftGPU/CMakeLists.txt +++ b/Userland/Libraries/LibSoftGPU/CMakeLists.txt @@ -1,10 +1,8 @@ set(SOURCES Clipper.cpp - DepthBuffer.cpp Device.cpp Image.cpp Sampler.cpp - StencilBuffer.cpp ) add_compile_options(-Wno-psabi) diff --git a/Userland/Libraries/LibSoftGPU/DepthBuffer.cpp b/Userland/Libraries/LibSoftGPU/DepthBuffer.cpp deleted file mode 100644 index e2507545a9..0000000000 --- a/Userland/Libraries/LibSoftGPU/DepthBuffer.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include <LibSoftGPU/DepthBuffer.h> - -namespace SoftGPU { - -DepthBuffer::DepthBuffer(Gfx::IntSize const& size) - : m_size(size) - , m_data(new float[size.width() * size.height()]) -{ -} - -DepthBuffer::~DepthBuffer() -{ - delete[] m_data; -} - -float* DepthBuffer::scanline(int y) -{ - VERIFY(y >= 0 && y < m_size.height()); - return &m_data[y * m_size.width()]; -} - -void DepthBuffer::clear(float depth) -{ - int num_entries = m_size.width() * m_size.height(); - for (int i = 0; i < num_entries; ++i) { - m_data[i] = 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; -} - -} diff --git a/Userland/Libraries/LibSoftGPU/DepthBuffer.h b/Userland/Libraries/LibSoftGPU/DepthBuffer.h deleted file mode 100644 index 4fb6f078b5..0000000000 --- a/Userland/Libraries/LibSoftGPU/DepthBuffer.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <LibGfx/Rect.h> -#include <LibGfx/Size.h> - -namespace SoftGPU { - -class DepthBuffer final { -public: - DepthBuffer(Gfx::IntSize const&); - ~DepthBuffer(); - - float* scanline(int y); - - void clear(float depth); - void clear(Gfx::IntRect bounds, float depth); - -private: - Gfx::IntSize m_size; - float* m_data { nullptr }; -}; - -} diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index ffd2d4abf8..b66f2f11c9 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/Userland/Libraries/LibSoftGPU/Device.cpp @@ -62,7 +62,17 @@ constexpr static auto interpolate(const T& v0, const T& v1, const T& v2, const V return v0 * barycentric_coords.x() + v1 * barycentric_coords.y() + v2 * barycentric_coords.z(); } -ALWAYS_INLINE static u32x4 to_rgba32(const Vector4<f32x4>& v) +static ColorType to_bgra32(FloatVector4 const& color) +{ + auto clamped = color.clamped(0.0f, 1.0f); + auto r = static_cast<u8>(clamped.x() * 255); + auto g = static_cast<u8>(clamped.y() * 255); + auto b = static_cast<u8>(clamped.z() * 255); + auto a = static_cast<u8>(clamped.w() * 255); + return a << 24 | r << 16 | g << 8 | b; +} + +ALWAYS_INLINE static u32x4 to_bgra32(Vector4<f32x4> const& v) { auto clamped = v.clamped(expand4(0.0f), expand4(1.0f)); auto r = to_u32x4(clamped.x() * 255); @@ -88,7 +98,7 @@ Gfx::IntRect Device::window_coordinates_to_target_coordinates(Gfx::IntRect const { return { window_rect.x(), - m_render_target->rect().height() - window_rect.height() - window_rect.y(), + m_frame_buffer->rect().height() - window_rect.height() - window_rect.y(), window_rect.width(), window_rect.height(), }; @@ -203,7 +213,7 @@ void Device::rasterize_triangle(const Triangle& triangle) auto const one_over_area = 1.0f / area; - auto render_bounds = m_render_target->rect(); + auto render_bounds = m_frame_buffer->rect(); if (m_options.scissor_enabled) render_bounds.intersect(window_coordinates_to_target_coordinates(m_options.scissor_box)); @@ -259,11 +269,15 @@ void Device::rasterize_triangle(const Triangle& triangle) expand4(subpixel_factor / 2), }; + auto color_buffer = m_frame_buffer->color_buffer(); + auto depth_buffer = m_frame_buffer->depth_buffer(); + auto stencil_buffer = m_frame_buffer->stencil_buffer(); + // Stencil configuration and writing auto const stencil_configuration = m_stencil_configuration[Face::Front]; auto const stencil_reference_value = stencil_configuration.reference_value & stencil_configuration.test_mask; - auto write_to_stencil = [](u8* stencil_ptrs[4], i32x4 stencil_value, StencilOperation op, u8 reference_value, u8 write_mask, i32x4 pixel_mask) { + auto write_to_stencil = [](StencilType* stencil_ptrs[4], i32x4 stencil_value, StencilOperation op, StencilType reference_value, StencilType write_mask, i32x4 pixel_mask) { if (write_mask == 0 || op == StencilOperation::Keep) return; @@ -334,13 +348,13 @@ void Device::rasterize_triangle(const Triangle& triangle) int coverage_bits = maskbits(quad.mask); // Stencil testing - u8* stencil_ptrs[4]; + StencilType* stencil_ptrs[4]; i32x4 stencil_value; if (m_options.enable_stencil_test) { - stencil_ptrs[0] = coverage_bits & 1 ? &m_stencil_buffer->scanline(by)[bx] : nullptr; - stencil_ptrs[1] = coverage_bits & 2 ? &m_stencil_buffer->scanline(by)[bx + 1] : nullptr; - stencil_ptrs[2] = coverage_bits & 4 ? &m_stencil_buffer->scanline(by + 1)[bx] : nullptr; - stencil_ptrs[3] = coverage_bits & 8 ? &m_stencil_buffer->scanline(by + 1)[bx + 1] : nullptr; + stencil_ptrs[0] = coverage_bits & 1 ? &stencil_buffer->scanline(by)[bx] : nullptr; + stencil_ptrs[1] = coverage_bits & 2 ? &stencil_buffer->scanline(by)[bx + 1] : nullptr; + stencil_ptrs[2] = coverage_bits & 4 ? &stencil_buffer->scanline(by + 1)[bx] : nullptr; + stencil_ptrs[3] = coverage_bits & 8 ? &stencil_buffer->scanline(by + 1)[bx + 1] : nullptr; stencil_value = load4_masked(stencil_ptrs[0], stencil_ptrs[1], stencil_ptrs[2], stencil_ptrs[3], quad.mask); stencil_value &= stencil_configuration.test_mask; @@ -391,11 +405,11 @@ void Device::rasterize_triangle(const Triangle& triangle) } // Depth testing - float* depth_ptrs[4] = { - coverage_bits & 1 ? &m_depth_buffer->scanline(by)[bx] : nullptr, - coverage_bits & 2 ? &m_depth_buffer->scanline(by)[bx + 1] : nullptr, - coverage_bits & 4 ? &m_depth_buffer->scanline(by + 1)[bx] : nullptr, - coverage_bits & 8 ? &m_depth_buffer->scanline(by + 1)[bx + 1] : nullptr, + DepthType* depth_ptrs[4] = { + coverage_bits & 1 ? &depth_buffer->scanline(by)[bx] : nullptr, + coverage_bits & 2 ? &depth_buffer->scanline(by)[bx + 1] : nullptr, + coverage_bits & 4 ? &depth_buffer->scanline(by + 1)[bx] : nullptr, + coverage_bits & 8 ? &depth_buffer->scanline(by + 1)[bx + 1] : nullptr, }; if (m_options.enable_depth_test) { auto depth = load4_masked(depth_ptrs[0], depth_ptrs[1], depth_ptrs[2], depth_ptrs[3], quad.mask); @@ -438,7 +452,7 @@ void Device::rasterize_triangle(const Triangle& triangle) // // This is an interesting quirk that occurs due to us using the x87 FPU when Serenity is // compiled for the i386 target. When we calculate our depth value to be stored in the buffer, - // it is an 80-bit x87 floating point number, however, when stored into the DepthBuffer, this is + // it is an 80-bit x87 floating point number, however, when stored into the depth buffer, this is // truncated to 32 bits. This 38 bit loss of precision means that when x87 `FCOMP` is eventually // used here the comparison fails. // This could be solved by using a `long double` for the depth buffer, however this would take @@ -538,11 +552,11 @@ void Device::rasterize_triangle(const Triangle& triangle) if (!m_options.color_mask || !m_options.enable_color_write) continue; - Gfx::RGBA32* color_ptrs[4] = { - coverage_bits & 1 ? &m_render_target->scanline(by)[bx] : nullptr, - coverage_bits & 2 ? &m_render_target->scanline(by)[bx + 1] : nullptr, - coverage_bits & 4 ? &m_render_target->scanline(by + 1)[bx] : nullptr, - coverage_bits & 8 ? &m_render_target->scanline(by + 1)[bx + 1] : nullptr, + ColorType* color_ptrs[4] = { + coverage_bits & 1 ? &color_buffer->scanline(by)[bx] : nullptr, + coverage_bits & 2 ? &color_buffer->scanline(by)[bx + 1] : nullptr, + coverage_bits & 4 ? &color_buffer->scanline(by + 1)[bx] : nullptr, + coverage_bits & 8 ? &color_buffer->scanline(by + 1)[bx + 1] : nullptr, }; u32x4 dst_u32; @@ -552,7 +566,7 @@ void Device::rasterize_triangle(const Triangle& triangle) if (m_options.enable_blending) { INCREASE_STATISTICS_COUNTER(g_num_pixels_blended, maskcount(quad.mask)); - // Blend color values from pixel_staging into m_render_target + // Blend color values from pixel_staging into color_buffer Vector4<f32x4> const& src = quad.out_color; auto dst = to_vec4(dst_u32); @@ -572,20 +586,18 @@ void Device::rasterize_triangle(const Triangle& triangle) } if (m_options.color_mask == 0xffffffff) - store4_masked(to_rgba32(quad.out_color), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); + store4_masked(to_bgra32(quad.out_color), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); else - store4_masked((to_rgba32(quad.out_color) & m_options.color_mask) | (dst_u32 & ~m_options.color_mask), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); + store4_masked((to_bgra32(quad.out_color) & m_options.color_mask) | (dst_u32 & ~m_options.color_mask), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); } } } -Device::Device(const Gfx::IntSize& size) - : m_render_target(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors()) - , m_depth_buffer(make<DepthBuffer>(size)) - , m_stencil_buffer(MUST(StencilBuffer::try_create(size))) +Device::Device(Gfx::IntSize const& size) + : m_frame_buffer(FrameBuffer<ColorType, DepthType, StencilType>::try_create(size).release_value_but_fixme_should_propagate_errors()) { - m_options.scissor_box = m_render_target->rect(); - m_options.viewport = m_render_target->rect(); + m_options.scissor_box = m_frame_buffer->rect(); + m_options.viewport = m_frame_buffer->rect(); } DeviceInfo Device::info() const @@ -595,7 +607,7 @@ DeviceInfo Device::info() const .device_name = "SoftGPU", .num_texture_units = NUM_SAMPLERS, .num_lights = NUM_LIGHTS, - .stencil_bits = sizeof(u8) * 8, + .stencil_bits = sizeof(StencilType) * 8, .supports_npot_textures = true, }; } @@ -1101,49 +1113,39 @@ ALWAYS_INLINE bool Device::test_alpha(PixelQuad& quad) return any(quad.mask); } -void Device::resize(const Gfx::IntSize& size) +void Device::resize(Gfx::IntSize const& size) { - m_render_target = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors(); - m_depth_buffer = adopt_own(*new DepthBuffer(size)); + auto frame_buffer_or_error = FrameBuffer<ColorType, DepthType, StencilType>::try_create(size); + m_frame_buffer = MUST(frame_buffer_or_error); } -void Device::clear_color(const FloatVector4& color) +void Device::clear_color(FloatVector4 const& color) { - uint8_t r = static_cast<uint8_t>(clamp(color.x(), 0.0f, 1.0f) * 255); - 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(); + auto const fill_color = to_bgra32(color); + + auto fill_rect = m_frame_buffer->rect(); + if (m_options.scissor_enabled) fill_rect.intersect(window_coordinates_to_target_coordinates(m_options.scissor_box)); - Gfx::Painter painter { *m_render_target }; - painter.fill_rect(fill_rect, fill_color); - return; - } - m_render_target->fill(fill_color); + m_frame_buffer->color_buffer()->fill(fill_color, fill_rect); } -void Device::clear_depth(float depth) +void Device::clear_depth(DepthType depth) { - if (m_options.scissor_enabled) { - m_depth_buffer->clear(window_coordinates_to_target_coordinates(m_options.scissor_box), depth); - return; - } + auto clear_rect = m_frame_buffer->rect(); + if (m_options.scissor_enabled) + clear_rect.intersect(window_coordinates_to_target_coordinates(m_options.scissor_box)); - m_depth_buffer->clear(depth); + m_frame_buffer->depth_buffer()->fill(depth, clear_rect); } -void Device::clear_stencil(u8 value) +void Device::clear_stencil(StencilType value) { - Gfx::IntRect clear_rect = m_stencil_buffer->rect(); - + auto clear_rect = m_frame_buffer->rect(); if (m_options.scissor_enabled) clear_rect.intersect(window_coordinates_to_target_coordinates(m_options.scissor_box)); - m_stencil_buffer->clear(clear_rect, value); + m_frame_buffer->stencil_buffer()->fill(value, clear_rect); } void Device::blit_to_color_buffer_at_raster_position(Gfx::Bitmap const& source) @@ -1154,12 +1156,11 @@ void Device::blit_to_color_buffer_at_raster_position(Gfx::Bitmap const& source) INCREASE_STATISTICS_COUNTER(g_num_pixels, source.width() * source.height()); INCREASE_STATISTICS_COUNTER(g_num_pixels_shaded, source.width() * source.height()); - Gfx::Painter painter { *m_render_target }; auto const blit_rect = raster_rect_in_target_coordinates(source.size()); - painter.blit({ blit_rect.x(), blit_rect.y() }, source, source.rect(), 1.0f, true); + m_frame_buffer->color_buffer()->blit_from_bitmap(source, blit_rect); } -void Device::blit_to_depth_buffer_at_raster_position(Vector<float> const& depth_values, size_t width, size_t height) +void Device::blit_to_depth_buffer_at_raster_position(Vector<DepthType> const& depth_values, int width, int height) { if (!m_raster_position.valid) return; @@ -1168,21 +1169,19 @@ void Device::blit_to_depth_buffer_at_raster_position(Vector<float> const& depth_ auto const y1 = raster_rect.y(); auto const y2 = y1 + height; auto const x1 = raster_rect.x(); - int const x2 = x1 + width; + auto const x2 = x1 + width; auto index = 0; - for (int y = y2 - 1; y >= y1; --y) { - auto depth_line = m_depth_buffer->scanline(y); - for (int x = x1; x < x2; ++x) { - depth_line[x] = depth_values.at(index++); - } + for (auto y = y2 - 1; y >= y1; --y) { + auto depth_line = m_frame_buffer->depth_buffer()->scanline(y); + for (auto x = x1; x < x2; ++x) + depth_line[x] = depth_values[index++]; } } -void Device::blit_to(Gfx::Bitmap& target) +void Device::blit_color_buffer_to(Gfx::Bitmap& target) { - Gfx::Painter painter { target }; - painter.blit({ 0, 0 }, *m_render_target, m_render_target->rect(), 1.0f, false); + m_frame_buffer->color_buffer()->blit_to_bitmap(target, m_frame_buffer->rect()); if constexpr (ENABLE_STATISTICS_OVERLAY) draw_statistics_overlay(target); @@ -1205,7 +1204,7 @@ void Device::draw_statistics_overlay(Gfx::Bitmap& target) if (milliseconds > MILLISECONDS_PER_STATISTICS_PERIOD) { - int num_rendertarget_pixels = m_render_target->width() * m_render_target->height(); + int num_rendertarget_pixels = m_frame_buffer->rect().size().area(); StringBuilder builder; builder.append(String::formatted("Timings : {:.1}ms {:.1}FPS\n", @@ -1258,33 +1257,34 @@ void Device::set_light_model_params(const LightModelParameters& lighting_model) m_lighting_model = lighting_model; } -Gfx::RGBA32 Device::get_backbuffer_pixel(int x, int y) +ColorType Device::get_color_buffer_pixel(int x, int y) { // FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks - if (x < 0 || y < 0 || x >= m_render_target->width() || y >= m_render_target->height()) + if (x < 0 || y < 0 || x >= m_frame_buffer->rect().width() || y >= m_frame_buffer->rect().height()) return 0; - return m_render_target->scanline(y)[x]; + return m_frame_buffer->color_buffer()->scanline(y)[x]; } -float Device::get_depthbuffer_value(int x, int y) +DepthType Device::get_depthbuffer_value(int x, int y) { // FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks - if (x < 0 || y < 0 || x >= m_render_target->width() || y >= m_render_target->height()) + if (x < 0 || y < 0 || x >= m_frame_buffer->rect().width() || y >= m_frame_buffer->rect().height()) return 1.0f; - return m_depth_buffer->scanline(y)[x]; + return m_frame_buffer->depth_buffer()->scanline(y)[x]; } NonnullRefPtr<Image> Device::create_image(ImageFormat format, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers) { + VERIFY(format == ImageFormat::BGRA8888); VERIFY(width > 0); VERIFY(height > 0); VERIFY(depth > 0); VERIFY(levels > 0); VERIFY(layers > 0); - return adopt_ref(*new Image(format, width, height, depth, levels, layers)); + return adopt_ref(*new Image(width, height, depth, levels, layers)); } void Device::set_sampler_config(unsigned sampler, SamplerConfig const& config) diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h index 6a4b2f6ebb..f2e74b510c 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/Userland/Libraries/LibSoftGPU/Device.h @@ -9,7 +9,7 @@ #include <AK/Array.h> #include <AK/NonnullRefPtr.h> -#include <AK/OwnPtr.h> +#include <AK/RefPtr.h> #include <AK/Vector.h> #include <LibGfx/Bitmap.h> #include <LibGfx/Matrix3x3.h> @@ -17,9 +17,10 @@ #include <LibGfx/Rect.h> #include <LibGfx/Vector4.h> #include <LibSoftGPU/AlphaBlendFactors.h> +#include <LibSoftGPU/Buffer/FrameBuffer.h> +#include <LibSoftGPU/Buffer/Typed2DBuffer.h> #include <LibSoftGPU/Clipper.h> #include <LibSoftGPU/Config.h> -#include <LibSoftGPU/DepthBuffer.h> #include <LibSoftGPU/DeviceInfo.h> #include <LibSoftGPU/Enums.h> #include <LibSoftGPU/Image.h> @@ -27,7 +28,6 @@ #include <LibSoftGPU/Light/Light.h> #include <LibSoftGPU/Light/Material.h> #include <LibSoftGPU/Sampler.h> -#include <LibSoftGPU/StencilBuffer.h> #include <LibSoftGPU/Triangle.h> #include <LibSoftGPU/Vertex.h> @@ -100,13 +100,13 @@ struct RasterPosition { struct StencilConfiguration { StencilTestFunction test_function; - u8 reference_value; - u8 test_mask; + StencilType reference_value; + StencilType test_mask; StencilOperation on_stencil_test_fail; StencilOperation on_depth_test_fail; StencilOperation on_pass; - u8 write_mask; + StencilType write_mask; }; class Device final { @@ -116,21 +116,21 @@ public: DeviceInfo info() const; void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix3x3 const& normal_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units); - void resize(const Gfx::IntSize& min_size); - void clear_color(const FloatVector4&); - void clear_depth(float); - void clear_stencil(u8); - void blit_to(Gfx::Bitmap&); + void resize(Gfx::IntSize const& min_size); + void clear_color(FloatVector4 const&); + void clear_depth(DepthType); + void clear_stencil(StencilType); + void blit_color_buffer_to(Gfx::Bitmap& target); void blit_to_color_buffer_at_raster_position(Gfx::Bitmap const&); - void blit_to_depth_buffer_at_raster_position(Vector<float> const&, size_t, size_t); + void blit_to_depth_buffer_at_raster_position(Vector<DepthType> const&, int, int); void set_options(const RasterizerOptions&); void set_light_model_params(const LightModelParameters&); RasterizerOptions options() const { return m_options; } LightModelParameters light_model() const { return m_lighting_model; } - Gfx::RGBA32 get_backbuffer_pixel(int x, int y); - float get_depthbuffer_value(int x, int y); + ColorType get_color_buffer_pixel(int x, int y); + DepthType get_depthbuffer_value(int x, int y); - NonnullRefPtr<Image> create_image(ImageFormat, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers); + NonnullRefPtr<Image> create_image(ImageFormat format, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers); void set_sampler_config(unsigned, SamplerConfig const&); void set_light_state(unsigned, Light const&); @@ -151,9 +151,7 @@ private: void shade_fragments(PixelQuad&); bool test_alpha(PixelQuad&); - RefPtr<Gfx::Bitmap> m_render_target; - NonnullOwnPtr<DepthBuffer> m_depth_buffer; - NonnullOwnPtr<StencilBuffer> m_stencil_buffer; + RefPtr<FrameBuffer<ColorType, DepthType, StencilType>> m_frame_buffer {}; RasterizerOptions m_options; LightModelParameters m_lighting_model; Clipper m_clipper; diff --git a/Userland/Libraries/LibSoftGPU/Enums.h b/Userland/Libraries/LibSoftGPU/Enums.h index 79c31d2ac7..cde69a998a 100644 --- a/Userland/Libraries/LibSoftGPU/Enums.h +++ b/Userland/Libraries/LibSoftGPU/Enums.h @@ -6,8 +6,14 @@ #pragma once +#include <AK/Types.h> + namespace SoftGPU { +using ColorType = u32; // BGRA:8888 +using DepthType = float; +using StencilType = u8; + enum class AlphaTestFunction { Never, Always, diff --git a/Userland/Libraries/LibSoftGPU/Image.cpp b/Userland/Libraries/LibSoftGPU/Image.cpp index 34ad239f59..5cb0a4d60a 100644 --- a/Userland/Libraries/LibSoftGPU/Image.cpp +++ b/Userland/Libraries/LibSoftGPU/Image.cpp @@ -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 */ @@ -8,17 +9,14 @@ namespace SoftGPU { -Image::Image(ImageFormat format, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers) - : m_format(format) - , m_width(width) - , m_height(height) - , m_depth(depth) - , m_num_layers(layers) +Image::Image(unsigned width, unsigned height, unsigned depth, unsigned max_levels, unsigned layers) + : m_num_layers(layers) + , m_mipmap_buffers(FixedArray<RefPtr<Typed3DBuffer<ColorType>>>::must_create_but_fixme_should_propagate_errors(layers * max_levels)) { VERIFY(width > 0); VERIFY(height > 0); VERIFY(depth > 0); - VERIFY(levels > 0); + VERIFY(max_levels > 0); VERIFY(layers > 0); if ((width & (width - 1)) == 0) @@ -30,24 +28,20 @@ Image::Image(ImageFormat format, unsigned width, unsigned height, unsigned depth if ((depth & (depth - 1)) == 0) m_depth_is_power_of_two = true; - m_mipmap_sizes.append({ width, height, depth }); - m_mipmap_offsets.append(0); + unsigned level; + for (level = 0; level < max_levels; ++level) { + for (unsigned layer = 0; layer < layers; ++layer) + m_mipmap_buffers[layer * layers + level] = MUST(Typed3DBuffer<ColorType>::try_create(width, height, depth)); - m_mipchain_size += width * height * depth * element_size(format); + if (width <= 1 && height <= 1 && depth <= 1) + break; - while (--levels && (width > 1 || height > 1 || depth > 1)) { width = max(width / 2, 1); height = max(height / 2, 1); depth = max(depth / 2, 1); - m_mipmap_sizes.append({ width, height, depth }); - m_mipmap_offsets.append(m_mipchain_size); - - m_mipchain_size += width * height * depth * element_size(format); } - m_num_levels = m_mipmap_sizes.size(); - - m_data.resize(m_mipchain_size * m_num_layers); + m_num_levels = level + 1; } void Image::write_texels(unsigned layer, unsigned level, Vector3<unsigned> const& offset, Vector3<unsigned> const& size, void const* data, ImageDataLayout const& layout) diff --git a/Userland/Libraries/LibSoftGPU/Image.h b/Userland/Libraries/LibSoftGPU/Image.h index 884e5d59c7..b7a4a16a80 100644 --- a/Userland/Libraries/LibSoftGPU/Image.h +++ b/Userland/Libraries/LibSoftGPU/Image.h @@ -1,15 +1,19 @@ /* * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org> + * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include <AK/FixedArray.h> #include <AK/RefCounted.h> -#include <AK/Vector.h> +#include <AK/RefPtr.h> #include <LibGfx/Vector3.h> #include <LibGfx/Vector4.h> +#include <LibSoftGPU/Buffer/Typed3DBuffer.h> +#include <LibSoftGPU/Enums.h> #include <LibSoftGPU/ImageDataLayout.h> #include <LibSoftGPU/ImageFormat.h> @@ -17,29 +21,25 @@ namespace SoftGPU { class Image final : public RefCounted<Image> { public: - Image(ImageFormat format, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers); + Image(unsigned width, unsigned height, unsigned depth, unsigned max_levels, unsigned layers); - ImageFormat format() const { return m_format; } - unsigned width() const { return m_width; } - unsigned height() const { return m_height; } - unsigned depth() const { return m_depth; } - unsigned level_width(unsigned level) const { return m_mipmap_sizes[level].x(); } - unsigned level_height(unsigned level) const { return m_mipmap_sizes[level].y(); } - unsigned level_depth(unsigned level) const { return m_mipmap_sizes[level].z(); } + unsigned level_width(unsigned level) const { return m_mipmap_buffers[level]->width(); } + unsigned level_height(unsigned level) const { return m_mipmap_buffers[level]->height(); } + unsigned level_depth(unsigned level) const { return m_mipmap_buffers[level]->depth(); } unsigned num_levels() const { return m_num_levels; } unsigned num_layers() const { return m_num_layers; } bool width_is_power_of_two() const { return m_width_is_power_of_two; } bool height_is_power_of_two() const { return m_height_is_power_of_two; } bool depth_is_power_of_two() const { return m_depth_is_power_of_two; } - FloatVector4 texel(unsigned layer, unsigned level, unsigned x, unsigned y, unsigned z) const + FloatVector4 texel(unsigned layer, unsigned level, int x, int y, int z) const { - return unpack_color(texel_pointer(layer, level, x, y, z), m_format); + return unpack_color(texel_pointer(layer, level, x, y, z), ImageFormat::BGRA8888); } - void set_texel(unsigned layer, unsigned level, unsigned x, unsigned y, unsigned z, FloatVector4 const& color) + void set_texel(unsigned layer, unsigned level, int x, int y, int z, FloatVector4 const& color) { - pack_color(color, texel_pointer(layer, level, x, y, z), m_format); + pack_color(color, texel_pointer(layer, level, x, y, z), ImageFormat::BGRA8888); } void write_texels(unsigned layer, unsigned level, Vector3<unsigned> const& offset, Vector3<unsigned> const& size, void const* data, ImageDataLayout const& layout); @@ -47,30 +47,22 @@ public: void copy_texels(Image const& source, unsigned source_layer, unsigned source_level, Vector3<unsigned> const& source_offset, Vector3<unsigned> const& size, unsigned destination_layer, unsigned destination_level, Vector3<unsigned> const& destination_offset); private: - void const* texel_pointer(unsigned layer, unsigned level, unsigned x, unsigned y, unsigned z) const + void const* texel_pointer(unsigned layer, unsigned level, int x, int y, int z) const { - auto size = m_mipmap_sizes[level]; - return &m_data[m_mipchain_size * layer + m_mipmap_offsets[level] + (z * size.x() * size.y() + y * size.x() + x) * element_size(m_format)]; + return m_mipmap_buffers[layer * m_num_layers + level]->buffer_pointer(x, y, z); } - void* texel_pointer(unsigned layer, unsigned level, unsigned x, unsigned y, unsigned z) + void* texel_pointer(unsigned layer, unsigned level, int x, int y, int z) { - auto size = m_mipmap_sizes[level]; - return &m_data[m_mipchain_size * layer + m_mipmap_offsets[level] + (z * size.x() * size.y() + y * size.x() + x) * element_size(m_format)]; + return m_mipmap_buffers[layer * m_num_layers + level]->buffer_pointer(x, y, z); } private: - ImageFormat m_format { ImageFormat::RGBA8888 }; - unsigned m_width { 0 }; - unsigned m_height { 0 }; - unsigned m_depth { 0 }; unsigned m_num_levels { 0 }; unsigned m_num_layers { 0 }; - size_t m_mipchain_size { 0 }; - Vector<size_t, 16> m_mipmap_offsets; - Vector<Vector3<unsigned>, 16> m_mipmap_sizes; - Vector<u8> m_data; + FixedArray<RefPtr<Typed3DBuffer<ColorType>>> m_mipmap_buffers; + bool m_width_is_power_of_two { false }; bool m_height_is_power_of_two { false }; bool m_depth_is_power_of_two { false }; diff --git a/Userland/Libraries/LibSoftGPU/StencilBuffer.cpp b/Userland/Libraries/LibSoftGPU/StencilBuffer.cpp deleted file mode 100644 index b9aad2e626..0000000000 --- a/Userland/Libraries/LibSoftGPU/StencilBuffer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include <LibSoftGPU/StencilBuffer.h> - -namespace SoftGPU { - -ErrorOr<NonnullOwnPtr<StencilBuffer>> StencilBuffer::try_create(Gfx::IntSize const& size) -{ - auto rect = Gfx::IntRect { 0, 0, size.width(), size.height() }; - auto data = TRY(FixedArray<u8>::try_create(size.area())); - return adopt_own(*new StencilBuffer(rect, move(data))); -} - -StencilBuffer::StencilBuffer(Gfx::IntRect const& rect, FixedArray<u8> data) - : m_data(move(data)) - , m_rect(rect) -{ -} - -void StencilBuffer::clear(Gfx::IntRect rect, u8 value) -{ - rect.intersect(m_rect); - - for (int y = rect.top(); y <= rect.bottom(); ++y) { - auto* line = scanline(y); - for (int x = rect.left(); x <= rect.right(); ++x) - line[x] = value; - } -} - -u8* StencilBuffer::scanline(int y) -{ - VERIFY(m_rect.contains_vertically(y)); - return &m_data[y * m_rect.width()]; -} - -} diff --git a/Userland/Libraries/LibSoftGPU/StencilBuffer.h b/Userland/Libraries/LibSoftGPU/StencilBuffer.h deleted file mode 100644 index b79340557a..0000000000 --- a/Userland/Libraries/LibSoftGPU/StencilBuffer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Jelle Raaijmakers <jelle@gmta.nl> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <AK/Error.h> -#include <AK/FixedArray.h> -#include <AK/NonnullOwnPtr.h> -#include <AK/Try.h> -#include <LibGfx/Rect.h> -#include <LibGfx/Size.h> - -namespace SoftGPU { - -class StencilBuffer final { -public: - static ErrorOr<NonnullOwnPtr<StencilBuffer>> try_create(Gfx::IntSize const& size); - - void clear(Gfx::IntRect rect, u8 value); - Gfx::IntRect const& rect() const { return m_rect; } - u8* scanline(int y); - -private: - StencilBuffer(Gfx::IntRect const& rect, FixedArray<u8> data); - - FixedArray<u8> m_data; - Gfx::IntRect m_rect; -}; - -} |