mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 10:07:44 +00:00
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.
This commit is contained in:
parent
dda5987684
commit
1540c56e6c
10 changed files with 148 additions and 82 deletions
|
@ -506,6 +506,7 @@ extern "C" {
|
||||||
#define GL_REPEAT 0x2901
|
#define GL_REPEAT 0x2901
|
||||||
#define GL_CLAMP_TO_BORDER 0x812D
|
#define GL_CLAMP_TO_BORDER 0x812D
|
||||||
#define GL_CLAMP_TO_EDGE 0x812F
|
#define GL_CLAMP_TO_EDGE 0x812F
|
||||||
|
#define GL_GENERATE_MIPMAP 0x8191
|
||||||
#define GL_MIRRORED_REPEAT 0x8370
|
#define GL_MIRRORED_REPEAT 0x8370
|
||||||
#define GL_SUBTRACT 0x84E7
|
#define GL_SUBTRACT 0x84E7
|
||||||
#define GL_TEXTURE_FILTER_CONTROL 0x8500
|
#define GL_TEXTURE_FILTER_CONTROL 0x8500
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
|
|
||||||
* Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <LibGL/GL/gl.h>
|
|
||||||
|
|
||||||
namespace GL {
|
|
||||||
|
|
||||||
class MipMap {
|
|
||||||
public:
|
|
||||||
void set_width(GLsizei width) { m_width = width; }
|
|
||||||
void set_height(GLsizei height) { m_height = height; }
|
|
||||||
GLsizei width() const { return m_width; }
|
|
||||||
GLsizei height() const { return m_height; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
GLsizei m_width { 0 };
|
|
||||||
GLsizei m_height { 0 };
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
virtual bool is_texture_3d() const { return false; }
|
virtual bool is_texture_3d() const { return false; }
|
||||||
virtual bool is_cube_map() const { return false; }
|
virtual bool is_cube_map() const { return false; }
|
||||||
|
|
||||||
|
RefPtr<GPU::Image> device_image() const { return m_device_image; }
|
||||||
RefPtr<GPU::Image> device_image() { return m_device_image; }
|
RefPtr<GPU::Image> device_image() { return m_device_image; }
|
||||||
void set_device_image(RefPtr<GPU::Image> image) { m_device_image = image; }
|
void set_device_image(RefPtr<GPU::Image> image) { m_device_image = image; }
|
||||||
|
|
||||||
|
|
|
@ -19,20 +19,15 @@ void Texture2D::download_texture_data(GLuint lod, GPU::ImageDataLayout output_la
|
||||||
|
|
||||||
void Texture2D::upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels)
|
void Texture2D::upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels)
|
||||||
{
|
{
|
||||||
// NOTE: Some target, format, and internal formats are currently unsupported.
|
|
||||||
// Considering we control this library, and `gl.h` itself, we don't need to add any
|
|
||||||
// checks here to see if we support them; the program will simply fail to compile..
|
|
||||||
|
|
||||||
auto& mip = m_mipmaps[lod];
|
|
||||||
m_internal_format = internal_format;
|
m_internal_format = internal_format;
|
||||||
mip.set_width(input_layout.selection.width);
|
|
||||||
mip.set_height(input_layout.selection.height);
|
|
||||||
|
|
||||||
// No pixel data was supplied; leave the texture memory uninitialized.
|
// No pixel data was supplied; leave the texture memory uninitialized.
|
||||||
if (pixels == nullptr)
|
if (pixels == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
replace_sub_texture_data(lod, input_layout, { 0, 0, 0 }, pixels);
|
replace_sub_texture_data(lod, input_layout, { 0, 0, 0 }, pixels);
|
||||||
|
if (lod == 0 && m_generate_mipmaps)
|
||||||
|
device_image()->regenerate_mipmaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture2D::replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels)
|
void Texture2D::replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels)
|
||||||
|
@ -42,7 +37,18 @@ void Texture2D::replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_
|
||||||
// once used for rendering for the first time.
|
// once used for rendering for the first time.
|
||||||
VERIFY(!device_image().is_null());
|
VERIFY(!device_image().is_null());
|
||||||
|
|
||||||
device_image()->write_texels(0, lod, output_offset, pixels, input_layout);
|
device_image()->write_texels(lod, output_offset, pixels, input_layout);
|
||||||
|
if (lod == 0 && m_generate_mipmaps)
|
||||||
|
device_image()->regenerate_mipmaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture2D::set_generate_mipmaps(bool generate_mipmaps)
|
||||||
|
{
|
||||||
|
if (m_generate_mipmaps == generate_mipmaps)
|
||||||
|
return;
|
||||||
|
m_generate_mipmaps = generate_mipmaps;
|
||||||
|
if (generate_mipmaps && !device_image().is_null())
|
||||||
|
device_image()->regenerate_mipmaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
|
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
|
|
||||||
#include <AK/Array.h>
|
|
||||||
#include <AK/IntegralMath.h>
|
#include <AK/IntegralMath.h>
|
||||||
#include <LibGL/GL/gl.h>
|
#include <LibGL/GL/gl.h>
|
||||||
#include <LibGL/Tex/MipMap.h>
|
|
||||||
#include <LibGL/Tex/Sampler2D.h>
|
#include <LibGL/Tex/Sampler2D.h>
|
||||||
#include <LibGPU/ImageDataLayout.h>
|
#include <LibGPU/ImageDataLayout.h>
|
||||||
|
|
||||||
|
@ -31,24 +29,17 @@ public:
|
||||||
void upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels);
|
void upload_texture_data(GLuint lod, GLenum internal_format, GPU::ImageDataLayout input_layout, GLvoid const* pixels);
|
||||||
void replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels);
|
void replace_sub_texture_data(GLuint lod, GPU::ImageDataLayout input_layout, Vector3<i32> const& output_offset, GLvoid const* pixels);
|
||||||
|
|
||||||
MipMap const& mipmap(unsigned lod) const
|
void set_generate_mipmaps(bool generate_mipmaps);
|
||||||
{
|
|
||||||
if (lod >= m_mipmaps.size())
|
|
||||||
return m_mipmaps.last();
|
|
||||||
|
|
||||||
return m_mipmaps.at(lod);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLenum internal_format() const { return m_internal_format; }
|
GLenum internal_format() const { return m_internal_format; }
|
||||||
Sampler2D const& sampler() const { return m_sampler; }
|
Sampler2D const& sampler() const { return m_sampler; }
|
||||||
Sampler2D& sampler() { return m_sampler; }
|
Sampler2D& sampler() { return m_sampler; }
|
||||||
|
|
||||||
int width_at_lod(unsigned level) const { return (level >= m_mipmaps.size()) ? 0 : m_mipmaps.at(level).width(); }
|
int width_at_lod(unsigned level) const { return static_cast<int>(device_image()->width_at_level(level)); }
|
||||||
int height_at_lod(unsigned level) const { return (level >= m_mipmaps.size()) ? 0 : m_mipmaps.at(level).height(); }
|
int height_at_lod(unsigned level) const { return static_cast<int>(device_image()->height_at_level(level)); }
|
||||||
|
int depth_at_lod(unsigned level) const { return static_cast<int>(device_image()->depth_at_level(level)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// FIXME: Mipmaps are currently unused, but we have the plumbing for it at least
|
bool m_generate_mipmaps { false };
|
||||||
Array<MipMap, LOG2_MAX_TEXTURE_SIZE> m_mipmaps;
|
|
||||||
GLenum m_internal_format;
|
GLenum m_internal_format;
|
||||||
Sampler2D m_sampler;
|
Sampler2D m_sampler;
|
||||||
};
|
};
|
||||||
|
|
|
@ -582,10 +582,11 @@ void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
|
||||||
RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
|
RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
|
||||||
|
|
||||||
// FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
|
// FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
|
||||||
RETURN_WITH_ERROR_IF(!(pname == GL_TEXTURE_MIN_FILTER
|
RETURN_WITH_ERROR_IF(pname != GL_GENERATE_MIPMAP
|
||||||
|| pname == GL_TEXTURE_MAG_FILTER
|
&& pname != GL_TEXTURE_MIN_FILTER
|
||||||
|| pname == GL_TEXTURE_WRAP_S
|
&& pname != GL_TEXTURE_MAG_FILTER
|
||||||
|| pname == GL_TEXTURE_WRAP_T),
|
&& pname != GL_TEXTURE_WRAP_S
|
||||||
|
&& pname != GL_TEXTURE_WRAP_T,
|
||||||
GL_INVALID_ENUM);
|
GL_INVALID_ENUM);
|
||||||
|
|
||||||
// We assume GL_TEXTURE_2D (see above)
|
// We assume GL_TEXTURE_2D (see above)
|
||||||
|
@ -593,6 +594,10 @@ void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
|
||||||
VERIFY(!texture_2d.is_null());
|
VERIFY(!texture_2d.is_null());
|
||||||
|
|
||||||
switch (pname) {
|
switch (pname) {
|
||||||
|
case GL_GENERATE_MIPMAP:
|
||||||
|
RETURN_WITH_ERROR_IF(param != GL_TRUE && param != GL_FALSE, GL_INVALID_ENUM);
|
||||||
|
texture_2d->set_generate_mipmaps(param == GL_TRUE);
|
||||||
|
break;
|
||||||
case GL_TEXTURE_MIN_FILTER:
|
case GL_TEXTURE_MIN_FILTER:
|
||||||
RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
|
RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
|
||||||
|| param == GL_LINEAR
|
|| param == GL_LINEAR
|
||||||
|
@ -652,7 +657,7 @@ void GLContext::gl_tex_parameterfv(GLenum target, GLenum pname, GLfloat const* p
|
||||||
RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
|
RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
|
||||||
|
|
||||||
// FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
|
// FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
|
||||||
RETURN_WITH_ERROR_IF(!(pname == GL_TEXTURE_BORDER_COLOR), GL_INVALID_ENUM);
|
RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_BORDER_COLOR, GL_INVALID_ENUM);
|
||||||
|
|
||||||
// We assume GL_TEXTURE_2D (see above)
|
// We assume GL_TEXTURE_2D (see above)
|
||||||
auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
|
auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Stephan Unverwerth <s.unverwerth@serenityos.org>
|
* Copyright (c) 2022, Stephan Unverwerth <s.unverwerth@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +22,13 @@ public:
|
||||||
|
|
||||||
virtual ~Image() { }
|
virtual ~Image() { }
|
||||||
|
|
||||||
|
virtual u32 width_at_level(u32 level) const = 0;
|
||||||
|
virtual u32 height_at_level(u32 level) const = 0;
|
||||||
|
virtual u32 depth_at_level(u32 level) const = 0;
|
||||||
|
virtual u32 number_of_levels() const = 0;
|
||||||
|
|
||||||
|
virtual void regenerate_mipmaps() = 0;
|
||||||
|
|
||||||
virtual void write_texels(u32 level, Vector3<i32> const& output_offset, void const* input_data, ImageDataLayout const&) = 0;
|
virtual void write_texels(u32 level, Vector3<i32> const& output_offset, void const* input_data, ImageDataLayout const&) = 0;
|
||||||
virtual void read_texels(u32 level, Vector3<i32> const& input_offset, void* output_data, ImageDataLayout const&) const = 0;
|
virtual void read_texels(u32 level, Vector3<i32> const& input_offset, void* output_data, ImageDataLayout const&) const = 0;
|
||||||
virtual void copy_texels(Image const& source, u32 source_level, Vector3<u32> const& source_offset, Vector3<u32> const& size, u32 destination_level, Vector3<u32> const& destination_offset) = 0;
|
virtual void copy_texels(Image const& source, u32 source_level, Vector3<u32> const& source_offset, Vector3<u32> const& size, u32 destination_level, Vector3<u32> const& destination_offset) = 0;
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibGfx/Bitmap.h>
|
||||||
|
#include <LibGfx/Painter.h>
|
||||||
|
#include <LibGfx/Size.h>
|
||||||
#include <LibSoftGPU/Image.h>
|
#include <LibSoftGPU/Image.h>
|
||||||
#include <LibSoftGPU/PixelConverter.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);
|
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
|
GPU::ImageDataLayout Image::image_data_layout(u32 level, Vector3<i32> offset) const
|
||||||
{
|
{
|
||||||
auto const width = level_width(level);
|
auto const width = width_at_level(level);
|
||||||
auto const height = level_height(level);
|
auto const height = height_at_level(level);
|
||||||
auto const depth = level_depth(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
|
// FIXME: we are directly writing to FloatVector4s. We should probably find a better way to do this
|
||||||
return {
|
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)
|
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 output_layout = image_data_layout(level, output_offset);
|
||||||
auto texel_data = texel_pointer(level, 0, 0, 0);
|
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
|
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);
|
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);
|
auto const& src_image = static_cast<Image const&>(source);
|
||||||
|
|
||||||
VERIFY(source_level < src_image.num_levels());
|
VERIFY(source_level < src_image.number_of_levels());
|
||||||
VERIFY(source_offset.x() + size.x() <= src_image.level_width(source_level));
|
VERIFY(source_offset.x() + size.x() <= src_image.width_at_level(source_level));
|
||||||
VERIFY(source_offset.y() + size.y() <= src_image.level_height(source_level));
|
VERIFY(source_offset.y() + size.y() <= src_image.height_at_level(source_level));
|
||||||
VERIFY(source_offset.z() + size.z() <= src_image.level_depth(source_level));
|
VERIFY(source_offset.z() + size.z() <= src_image.depth_at_level(source_level));
|
||||||
VERIFY(destination_level < num_levels());
|
VERIFY(destination_level < number_of_levels());
|
||||||
VERIFY(destination_offset.x() + size.x() <= level_width(destination_level));
|
VERIFY(destination_offset.x() + size.x() <= width_at_level(destination_level));
|
||||||
VERIFY(destination_offset.y() + size.y() <= level_height(destination_level));
|
VERIFY(destination_offset.y() + size.y() <= height_at_level(destination_level));
|
||||||
VERIFY(destination_offset.z() + size.z() <= level_depth(destination_level));
|
VERIFY(destination_offset.z() + size.z() <= depth_at_level(destination_level));
|
||||||
|
|
||||||
for (u32 z = 0; z < size.z(); ++z) {
|
for (u32 z = 0; z < size.z(); ++z) {
|
||||||
for (u32 y = 0; y < size.y(); ++y) {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,16 @@ class Image final : public GPU::Image {
|
||||||
public:
|
public:
|
||||||
Image(void const* ownership_token, GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels);
|
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(); }
|
virtual u32 width_at_level(u32 level) const override { return m_mipmap_buffers[level]->width(); }
|
||||||
u32 level_height(u32 level) const { return m_mipmap_buffers[level]->height(); }
|
virtual u32 height_at_level(u32 level) const override { return m_mipmap_buffers[level]->height(); }
|
||||||
u32 level_depth(u32 level) const { return m_mipmap_buffers[level]->depth(); }
|
virtual u32 depth_at_level(u32 level) const override { return m_mipmap_buffers[level]->depth(); }
|
||||||
u32 num_levels() const { return m_num_levels; }
|
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 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 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; }
|
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;
|
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
|
FloatVector4 texel(u32 level, int x, int y, int z) const
|
||||||
{
|
{
|
||||||
|
@ -57,7 +58,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 m_num_levels { 0 };
|
u32 m_number_of_levels { 0 };
|
||||||
|
|
||||||
GPU::PixelFormat m_pixel_format;
|
GPU::PixelFormat m_pixel_format;
|
||||||
FixedArray<RefPtr<Typed3DBuffer<FloatVector4>>> m_mipmap_buffers;
|
FixedArray<RefPtr<Typed3DBuffer<FloatVector4>>> m_mipmap_buffers;
|
||||||
|
|
|
@ -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.
|
// 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.
|
// u16 should be plenty enough for texture dimensions and would allow textures of up to 65536x65536x65536 pixels.
|
||||||
auto texel_coordinates = uv;
|
auto texel_coordinates = uv;
|
||||||
texel_coordinates.set_x(texel_coordinates.x() * static_cast<float>(image.level_width(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.level_height(base_level)));
|
texel_coordinates.set_y(texel_coordinates.y() * static_cast<float>(image.height_at_level(base_level)));
|
||||||
auto dtdx = ddx(texel_coordinates);
|
auto dtdx = ddx(texel_coordinates);
|
||||||
auto dtdy = ddy(texel_coordinates);
|
auto dtdy = ddy(texel_coordinates);
|
||||||
auto scale_factor = max(dtdx.dot(dtdx), dtdy.dot(dtdy));
|
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);
|
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)
|
// 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 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 lambda_xy = log2_approximate(scale_factor) * .5f + texture_lod_bias;
|
||||||
auto level = clamp(lambda_xy, min_level, max_level);
|
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);
|
auto const& image = *static_ptr_cast<Image>(m_config.bound_image);
|
||||||
|
|
||||||
u32x4 const width = {
|
u32x4 const width = {
|
||||||
image.level_width(level[0]),
|
image.width_at_level(level[0]),
|
||||||
image.level_width(level[1]),
|
image.width_at_level(level[1]),
|
||||||
image.level_width(level[2]),
|
image.width_at_level(level[2]),
|
||||||
image.level_width(level[3]),
|
image.width_at_level(level[3]),
|
||||||
};
|
};
|
||||||
u32x4 const height = {
|
u32x4 const height = {
|
||||||
image.level_height(level[0]),
|
image.height_at_level(level[0]),
|
||||||
image.level_height(level[1]),
|
image.height_at_level(level[1]),
|
||||||
image.level_height(level[2]),
|
image.height_at_level(level[2]),
|
||||||
image.level_height(level[3]),
|
image.height_at_level(level[3]),
|
||||||
};
|
};
|
||||||
|
|
||||||
auto f_width = to_f32x4(width);
|
auto f_width = to_f32x4(width);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue