summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibSoftGPU
diff options
context:
space:
mode:
authorJelle Raaijmakers <jelle@gmta.nl>2022-09-04 22:18:16 +0200
committerLinus Groh <mail@linusgroh.de>2022-09-11 22:37:07 +0100
commit1540c56e6c82bfbdb1fd9b29c34c7ac1a399a1d3 (patch)
tree656ce8801f6a392f4ba6802702cb7652ca00b7b2 /Userland/Libraries/LibSoftGPU
parentdda5987684227e31e1d7b2fca749d43f734bfc47 (diff)
downloadserenity-1540c56e6c82bfbdb1fd9b29c34c7ac1a399a1d3.zip
LibGL+LibGPU+LibSoftGPU: Implement `GL_GENERATE_MIPMAP`
We can now generate texture mipmaps on the fly if the client requests it. This fixes the missing textures in our PrBoom+ port.
Diffstat (limited to 'Userland/Libraries/LibSoftGPU')
-rw-r--r--Userland/Libraries/LibSoftGPU/Image.cpp106
-rw-r--r--Userland/Libraries/LibSoftGPU/Image.h11
-rw-r--r--Userland/Libraries/LibSoftGPU/Sampler.cpp22
3 files changed, 109 insertions, 30 deletions
diff --git a/Userland/Libraries/LibSoftGPU/Image.cpp b/Userland/Libraries/LibSoftGPU/Image.cpp
index 795fc93206..5535c8c0e7 100644
--- a/Userland/Libraries/LibSoftGPU/Image.cpp
+++ b/Userland/Libraries/LibSoftGPU/Image.cpp
@@ -5,6 +5,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/Painter.h>
+#include <LibGfx/Size.h>
#include <LibSoftGPU/Image.h>
#include <LibSoftGPU/PixelConverter.h>
@@ -42,14 +45,14 @@ Image::Image(void const* ownership_token, GPU::PixelFormat const& pixel_format,
depth = max(depth / 2, 1);
}
- m_num_levels = level + 1;
+ m_number_of_levels = level + 1;
}
GPU::ImageDataLayout Image::image_data_layout(u32 level, Vector3<i32> offset) const
{
- auto const width = level_width(level);
- auto const height = level_height(level);
- auto const depth = level_depth(level);
+ auto const width = width_at_level(level);
+ auto const height = height_at_level(level);
+ auto const depth = depth_at_level(level);
// FIXME: we are directly writing to FloatVector4s. We should probably find a better way to do this
return {
@@ -76,7 +79,7 @@ GPU::ImageDataLayout Image::image_data_layout(u32 level, Vector3<i32> offset) co
void Image::write_texels(u32 level, Vector3<i32> const& output_offset, void const* input_data, GPU::ImageDataLayout const& input_layout)
{
- VERIFY(level < num_levels());
+ VERIFY(level < number_of_levels());
auto output_layout = image_data_layout(level, output_offset);
auto texel_data = texel_pointer(level, 0, 0, 0);
@@ -98,7 +101,7 @@ void Image::write_texels(u32 level, Vector3<i32> const& output_offset, void cons
void Image::read_texels(u32 level, Vector3<i32> const& input_offset, void* output_data, GPU::ImageDataLayout const& output_layout) const
{
- VERIFY(level < num_levels());
+ VERIFY(level < number_of_levels());
auto input_layout = image_data_layout(level, input_offset);
@@ -114,14 +117,14 @@ void Image::copy_texels(GPU::Image const& source, u32 source_level, Vector3<u32>
auto const& src_image = static_cast<Image const&>(source);
- VERIFY(source_level < src_image.num_levels());
- VERIFY(source_offset.x() + size.x() <= src_image.level_width(source_level));
- VERIFY(source_offset.y() + size.y() <= src_image.level_height(source_level));
- VERIFY(source_offset.z() + size.z() <= src_image.level_depth(source_level));
- VERIFY(destination_level < num_levels());
- VERIFY(destination_offset.x() + size.x() <= level_width(destination_level));
- VERIFY(destination_offset.y() + size.y() <= level_height(destination_level));
- VERIFY(destination_offset.z() + size.z() <= level_depth(destination_level));
+ VERIFY(source_level < src_image.number_of_levels());
+ VERIFY(source_offset.x() + size.x() <= src_image.width_at_level(source_level));
+ VERIFY(source_offset.y() + size.y() <= src_image.height_at_level(source_level));
+ VERIFY(source_offset.z() + size.z() <= src_image.depth_at_level(source_level));
+ VERIFY(destination_level < number_of_levels());
+ VERIFY(destination_offset.x() + size.x() <= width_at_level(destination_level));
+ VERIFY(destination_offset.y() + size.y() <= height_at_level(destination_level));
+ VERIFY(destination_offset.z() + size.z() <= depth_at_level(destination_level));
for (u32 z = 0; z < size.z(); ++z) {
for (u32 y = 0; y < size.y(); ++y) {
@@ -133,4 +136,79 @@ void Image::copy_texels(GPU::Image const& source, u32 source_level, Vector3<u32>
}
}
+static GPU::ImageDataLayout image_data_layout_for_bitmap(Gfx::Bitmap& bitmap)
+{
+ VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
+ return GPU::ImageDataLayout {
+ .pixel_type = {
+ .format = GPU::PixelFormat::BGRA,
+ .bits = GPU::PixelComponentBits::B8_8_8_8,
+ .data_type = GPU::PixelDataType::UnsignedInt,
+ .components_order = GPU::ComponentsOrder::Reversed,
+ },
+ .dimensions = {
+ .width = static_cast<u32>(bitmap.width()),
+ .height = static_cast<u32>(bitmap.height()),
+ .depth = 1,
+ },
+ .selection = {
+ .width = static_cast<u32>(bitmap.width()),
+ .height = static_cast<u32>(bitmap.height()),
+ .depth = 1,
+ },
+ };
+}
+
+void Image::regenerate_mipmaps()
+{
+ // FIXME: currently this only works for 2D Images
+ VERIFY(depth_at_level(0) == 1);
+
+ auto empty_bitmap_for_level = [&](u32 level) -> NonnullRefPtr<Gfx::Bitmap> {
+ Gfx::IntSize size = { width_at_level(level), height_at_level(level) };
+ return MUST(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, size));
+ };
+ auto copy_image_into_bitmap = [&](u32 level) -> NonnullRefPtr<Gfx::Bitmap> {
+ auto bitmap = empty_bitmap_for_level(level);
+
+ auto input_layout = image_data_layout(level, { 0, 0, 0 });
+ auto const* input_data = texel_pointer(level, 0, 0, 0);
+
+ auto output_layout = image_data_layout_for_bitmap(bitmap);
+ auto* output_data = bitmap->scanline(0);
+
+ PixelConverter converter { input_layout, output_layout };
+ MUST(converter.convert(input_data, output_data, {}));
+ return bitmap;
+ };
+ auto copy_bitmap_into_level = [&](NonnullRefPtr<Gfx::Bitmap> bitmap, u32 level) {
+ VERIFY(level >= 1);
+
+ auto input_layout = image_data_layout_for_bitmap(bitmap);
+ auto const* input_data = bitmap->scanline(0);
+
+ auto output_layout = image_data_layout(level, { 0, 0, 0 });
+ auto* output_data = texel_pointer(level, 0, 0, 0);
+
+ PixelConverter converter { input_layout, output_layout };
+ MUST(converter.convert(input_data, output_data, {}));
+ };
+
+ // For levels 1..number_of_levels-1, we generate downscaled versions of the level above
+ for (u32 level = 1; level < m_number_of_levels; ++level) {
+ auto higher_level_bitmap = copy_image_into_bitmap(level - 1);
+ auto current_level_bitmap = empty_bitmap_for_level(level);
+
+ Gfx::Painter current_level_painter { current_level_bitmap };
+ current_level_painter.draw_scaled_bitmap(
+ current_level_bitmap->rect(),
+ higher_level_bitmap,
+ higher_level_bitmap->rect(),
+ 1.f,
+ Gfx::Painter::ScalingMode::BilinearBlend);
+
+ copy_bitmap_into_level(current_level_bitmap, level);
+ }
+}
+
}
diff --git a/Userland/Libraries/LibSoftGPU/Image.h b/Userland/Libraries/LibSoftGPU/Image.h
index 23c7941155..8e702d9a54 100644
--- a/Userland/Libraries/LibSoftGPU/Image.h
+++ b/Userland/Libraries/LibSoftGPU/Image.h
@@ -22,15 +22,16 @@ class Image final : public GPU::Image {
public:
Image(void const* ownership_token, GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels);
- u32 level_width(u32 level) const { return m_mipmap_buffers[level]->width(); }
- u32 level_height(u32 level) const { return m_mipmap_buffers[level]->height(); }
- u32 level_depth(u32 level) const { return m_mipmap_buffers[level]->depth(); }
- u32 num_levels() const { return m_num_levels; }
+ virtual u32 width_at_level(u32 level) const override { return m_mipmap_buffers[level]->width(); }
+ virtual u32 height_at_level(u32 level) const override { return m_mipmap_buffers[level]->height(); }
+ virtual u32 depth_at_level(u32 level) const override { return m_mipmap_buffers[level]->depth(); }
+ virtual u32 number_of_levels() const override { return m_number_of_levels; }
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; }
GPU::ImageDataLayout image_data_layout(u32 level, Vector3<i32> offset) const;
+ virtual void regenerate_mipmaps() override;
FloatVector4 texel(u32 level, int x, int y, int z) const
{
@@ -57,7 +58,7 @@ public:
}
private:
- u32 m_num_levels { 0 };
+ u32 m_number_of_levels { 0 };
GPU::PixelFormat m_pixel_format;
FixedArray<RefPtr<Typed3DBuffer<FloatVector4>>> m_mipmap_buffers;
diff --git a/Userland/Libraries/LibSoftGPU/Sampler.cpp b/Userland/Libraries/LibSoftGPU/Sampler.cpp
index b027e99af4..d85d7dd845 100644
--- a/Userland/Libraries/LibSoftGPU/Sampler.cpp
+++ b/Userland/Libraries/LibSoftGPU/Sampler.cpp
@@ -117,8 +117,8 @@ Vector4<AK::SIMD::f32x4> Sampler::sample_2d(Vector2<AK::SIMD::f32x4> const& uv)
// FIXME: Static casting from u32 to float could silently truncate here.
// u16 should be plenty enough for texture dimensions and would allow textures of up to 65536x65536x65536 pixels.
auto texel_coordinates = uv;
- texel_coordinates.set_x(texel_coordinates.x() * static_cast<float>(image.level_width(base_level)));
- texel_coordinates.set_y(texel_coordinates.y() * static_cast<float>(image.level_height(base_level)));
+ texel_coordinates.set_x(texel_coordinates.x() * static_cast<float>(image.width_at_level(base_level)));
+ texel_coordinates.set_y(texel_coordinates.y() * static_cast<float>(image.height_at_level(base_level)));
auto dtdx = ddx(texel_coordinates);
auto dtdy = ddy(texel_coordinates);
auto scale_factor = max(dtdx.dot(dtdx), dtdy.dot(dtdy));
@@ -138,7 +138,7 @@ Vector4<AK::SIMD::f32x4> Sampler::sample_2d(Vector2<AK::SIMD::f32x4> const& uv)
auto texture_lod_bias = AK::clamp(m_config.level_of_detail_bias, -MAX_TEXTURE_LOD_BIAS, MAX_TEXTURE_LOD_BIAS);
// FIXME: Instead of clamping to num_levels - 1, actually make the max mipmap level configurable with glTexParameteri(GL_TEXTURE_MAX_LEVEL, max_level)
auto min_level = expand4(static_cast<float>(base_level));
- auto max_level = expand4(static_cast<float>(image.num_levels()) - 1.f);
+ auto max_level = expand4(static_cast<float>(image.number_of_levels()) - 1.f);
auto lambda_xy = log2_approximate(scale_factor) * .5f + texture_lod_bias;
auto level = clamp(lambda_xy, min_level, max_level);
@@ -157,16 +157,16 @@ Vector4<AK::SIMD::f32x4> Sampler::sample_2d_lod(Vector2<AK::SIMD::f32x4> const&
auto const& image = *static_ptr_cast<Image>(m_config.bound_image);
u32x4 const width = {
- image.level_width(level[0]),
- image.level_width(level[1]),
- image.level_width(level[2]),
- image.level_width(level[3]),
+ image.width_at_level(level[0]),
+ image.width_at_level(level[1]),
+ image.width_at_level(level[2]),
+ image.width_at_level(level[3]),
};
u32x4 const height = {
- image.level_height(level[0]),
- image.level_height(level[1]),
- image.level_height(level[2]),
- image.level_height(level[3]),
+ image.height_at_level(level[0]),
+ image.height_at_level(level[1]),
+ image.height_at_level(level[2]),
+ image.height_at_level(level[3]),
};
auto f_width = to_f32x4(width);