diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index 5b2b0b1bea4865..89c3de6de13da2 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/Userland/Libraries/LibGL/SoftwareGLContext.cpp @@ -19,6 +19,7 @@ #include #include #include +#include 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 00000000000000..7945339fe9c069 --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +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 +class FrameBuffer final : public RefCounted> { +public: + static ErrorOr>> try_create(Gfx::IntSize const& size) + { + Gfx::IntRect rect = { 0, 0, size.width(), size.height() }; + auto color_buffer = TRY(Typed2DBuffer::try_create(size)); + auto depth_buffer = TRY(Typed2DBuffer::try_create(size)); + auto stencil_buffer = TRY(Typed2DBuffer::try_create(size)); + return adopt_ref(*new FrameBuffer(rect, color_buffer, depth_buffer, stencil_buffer)); + } + + NonnullRefPtr> color_buffer() { return m_color_buffer; } + NonnullRefPtr> depth_buffer() { return m_depth_buffer; } + NonnullRefPtr> stencil_buffer() { return m_stencil_buffer; } + Gfx::IntRect rect() const { return m_rect; } + +private: + FrameBuffer(Gfx::IntRect rect, NonnullRefPtr> color_buffer, NonnullRefPtr> depth_buffer, NonnullRefPtr> stencil_buffer) + : m_color_buffer(color_buffer) + , m_depth_buffer(depth_buffer) + , m_stencil_buffer(stencil_buffer) + , m_rect(rect) + { + } + + NonnullRefPtr> m_color_buffer; + NonnullRefPtr> m_depth_buffer; + NonnullRefPtr> 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 00000000000000..b056f4257e2210 --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace SoftGPU { + +/** + * Typed2DBuffer wraps TypedBuffer and only interacts on the 2D plane with z = 0. + */ +template +class Typed2DBuffer final : public RefCounted> { +public: + static ErrorOr> try_create(Gfx::IntSize const& size) + { + auto buffer = TRY(Typed3DBuffer::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 + { + 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 + { + 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> buffer) + : m_buffer(buffer) + { + } + + NonnullRefPtr> m_buffer; +}; + +} diff --git a/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h b/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h new file mode 100644 index 00000000000000..567ffdd6ad7071 --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace SoftGPU { + +/** + * TypedBuffer 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 +class Typed3DBuffer final : public RefCounted> { +public: + static ErrorOr>> try_create(int width, int height, int depth) + { + VERIFY(width > 0 && height > 0 && depth > 0); + auto data = TRY(FixedArray::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 data) + : m_data(move(data)) + , m_depth(depth) + , m_height(height) + , m_width(width) + { + } + + FixedArray 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 03ee632187489c..bc99282ec6795a 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 e2507545a94e21..00000000000000 --- a/Userland/Libraries/LibSoftGPU/DepthBuffer.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -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 4fb6f078b5c386..00000000000000 --- a/Userland/Libraries/LibSoftGPU/DepthBuffer.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Stephan Unverwerth - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -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 ffd2d4abf81531..b66f2f11c99158 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& v) +static ColorType to_bgra32(FloatVector4 const& color) +{ + auto clamped = color.clamped(0.0f, 1.0f); + auto r = static_cast(clamped.x() * 255); + auto g = static_cast(clamped.y() * 255); + auto b = static_cast(clamped.z() * 255); + auto a = static_cast(clamped.w() * 255); + return a << 24 | r << 16 | g << 8 | b; +} + +ALWAYS_INLINE static u32x4 to_bgra32(Vector4 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 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(size)) - , m_stencil_buffer(MUST(StencilBuffer::try_create(size))) +Device::Device(Gfx::IntSize const& size) + : m_frame_buffer(FrameBuffer::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::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(clamp(color.x(), 0.0f, 1.0f) * 255); - uint8_t g = static_cast(clamp(color.y(), 0.0f, 1.0f) * 255); - uint8_t b = static_cast(clamp(color.z(), 0.0f, 1.0f) * 255); - uint8_t a = static_cast(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 const& depth_values, size_t width, size_t height) +void Device::blit_to_depth_buffer_at_raster_position(Vector 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 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 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 6a4b2f6ebb869b..f2e74b510cd41e 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/Userland/Libraries/LibSoftGPU/Device.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -17,9 +17,10 @@ #include #include #include +#include +#include #include #include -#include #include #include #include @@ -27,7 +28,6 @@ #include #include #include -#include #include #include @@ -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 @@ class Device final { 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 const& vertices, Vector 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 const&, size_t, size_t); + void blit_to_depth_buffer_at_raster_position(Vector 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 create_image(ImageFormat, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers); + NonnullRefPtr 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 @@ class Device final { void shade_fragments(PixelQuad&); bool test_alpha(PixelQuad&); - RefPtr m_render_target; - NonnullOwnPtr m_depth_buffer; - NonnullOwnPtr m_stencil_buffer; + RefPtr> 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 79c31d2ac780c2..cde69a998abf94 100644 --- a/Userland/Libraries/LibSoftGPU/Enums.h +++ b/Userland/Libraries/LibSoftGPU/Enums.h @@ -6,8 +6,14 @@ #pragma once +#include + 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 34ad239f59adf5..5cb0a4d60a001c 100644 --- a/Userland/Libraries/LibSoftGPU/Image.cpp +++ b/Userland/Libraries/LibSoftGPU/Image.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Stephan Unverwerth + * Copyright (c) 2022, Jelle Raaijmakers * * 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>>::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::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 const& offset, Vector3 const& size, void const* data, ImageDataLayout const& layout) diff --git a/Userland/Libraries/LibSoftGPU/Image.h b/Userland/Libraries/LibSoftGPU/Image.h index 884e5d59c7b41c..b7a4a16a809f88 100644 --- a/Userland/Libraries/LibSoftGPU/Image.h +++ b/Userland/Libraries/LibSoftGPU/Image.h @@ -1,15 +1,19 @@ /* * Copyright (c) 2021, Stephan Unverwerth + * Copyright (c) 2022, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include -#include +#include #include #include +#include +#include #include #include @@ -17,29 +21,25 @@ namespace SoftGPU { class Image final : public RefCounted { 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 const& offset, Vector3 const& size, void const* data, ImageDataLayout const& layout); @@ -47,30 +47,22 @@ class Image final : public RefCounted { void copy_texels(Image const& source, unsigned source_layer, unsigned source_level, Vector3 const& source_offset, Vector3 const& size, unsigned destination_layer, unsigned destination_level, Vector3 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 m_mipmap_offsets; - Vector, 16> m_mipmap_sizes; - Vector m_data; + FixedArray>> 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 b9aad2e626da58..00000000000000 --- a/Userland/Libraries/LibSoftGPU/StencilBuffer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace SoftGPU { - -ErrorOr> StencilBuffer::try_create(Gfx::IntSize const& size) -{ - auto rect = Gfx::IntRect { 0, 0, size.width(), size.height() }; - auto data = TRY(FixedArray::try_create(size.area())); - return adopt_own(*new StencilBuffer(rect, move(data))); -} - -StencilBuffer::StencilBuffer(Gfx::IntRect const& rect, FixedArray 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 b79340557a7912..00000000000000 --- a/Userland/Libraries/LibSoftGPU/StencilBuffer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace SoftGPU { - -class StencilBuffer final { -public: - static ErrorOr> 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 data); - - FixedArray m_data; - Gfx::IntRect m_rect; -}; - -}