summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibSoftGPU
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibSoftGPU')
-rw-r--r--Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h57
-rw-r--r--Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h73
-rw-r--r--Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h72
-rw-r--r--Userland/Libraries/LibSoftGPU/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibSoftGPU/DepthBuffer.cpp44
-rw-r--r--Userland/Libraries/LibSoftGPU/DepthBuffer.h29
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.cpp154
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.h34
-rw-r--r--Userland/Libraries/LibSoftGPU/Enums.h6
-rw-r--r--Userland/Libraries/LibSoftGPU/Image.cpp30
-rw-r--r--Userland/Libraries/LibSoftGPU/Image.h46
-rw-r--r--Userland/Libraries/LibSoftGPU/StencilBuffer.cpp41
-rw-r--r--Userland/Libraries/LibSoftGPU/StencilBuffer.h33
13 files changed, 332 insertions, 289 deletions
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;
-};
-
-}