Skip to content

Commit

Permalink
LibSoftGPU: Generalize pixel buffers and standardize on BGRA8888
Browse files Browse the repository at this point in the history
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. :^)
  • Loading branch information
gmta authored and linusg committed Feb 22, 2022
1 parent 72ec2c2 commit db0616c
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 313 deletions.
28 changes: 4 additions & 24 deletions Userland/Libraries/LibGL/SoftwareGLContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <LibGfx/Vector4.h>
#include <LibSoftGPU/Device.h>
#include <LibSoftGPU/Enums.h>
#include <LibSoftGPU/ImageFormat.h>

namespace GL {

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
57 changes: 57 additions & 0 deletions Userland/Libraries/LibSoftGPU/Buffer/FrameBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2022, Jelle Raaijmakers <[email protected]>
*
* 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;
};

}
73 changes: 73 additions & 0 deletions Userland/Libraries/LibSoftGPU/Buffer/Typed2DBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2022, Jelle Raaijmakers <[email protected]>
*
* 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;
};

}
72 changes: 72 additions & 0 deletions Userland/Libraries/LibSoftGPU/Buffer/Typed3DBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2022, Jelle Raaijmakers <[email protected]>
*
* 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;
};

}
2 changes: 0 additions & 2 deletions Userland/Libraries/LibSoftGPU/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
set(SOURCES
Clipper.cpp
DepthBuffer.cpp
Device.cpp
Image.cpp
Sampler.cpp
StencilBuffer.cpp
)

add_compile_options(-Wno-psabi)
Expand Down
44 changes: 0 additions & 44 deletions Userland/Libraries/LibSoftGPU/DepthBuffer.cpp

This file was deleted.

29 changes: 0 additions & 29 deletions Userland/Libraries/LibSoftGPU/DepthBuffer.h

This file was deleted.

Loading

0 comments on commit db0616c

Please sign in to comment.