summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephan Unverwerth <s.unverwerth@serenityos.org>2021-12-19 00:02:32 +0100
committerBrian Gianforcaro <b.gianfo@gmail.com>2021-12-24 05:10:28 -0800
commitb8bb72abbe66fa1e4ad5e4b197347e481e68f4bc (patch)
treee3e6bcf560983642f1fcaaa2414f4060c108f755
parent91ccf9958ff2be4026b9bfa21a2a3fa52fc7f265 (diff)
downloadserenity-b8bb72abbe66fa1e4ad5e4b197347e481e68f4bc.zip
LibSoftGPU: Add device method for creating images
-rw-r--r--Userland/Libraries/LibSoftGPU/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.cpp11
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.h5
-rw-r--r--Userland/Libraries/LibSoftGPU/Sampler.cpp119
-rw-r--r--Userland/Libraries/LibSoftGPU/Sampler.h56
5 files changed, 192 insertions, 0 deletions
diff --git a/Userland/Libraries/LibSoftGPU/CMakeLists.txt b/Userland/Libraries/LibSoftGPU/CMakeLists.txt
index b2870d3cb2..11e603e1b8 100644
--- a/Userland/Libraries/LibSoftGPU/CMakeLists.txt
+++ b/Userland/Libraries/LibSoftGPU/CMakeLists.txt
@@ -3,6 +3,7 @@ set(SOURCES
DepthBuffer.cpp
Device.cpp
Image.cpp
+ Sampler.cpp
)
serenity_lib(LibSoftGPU softgpu)
diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp
index 9c39ba01a4..e678c2db0d 100644
--- a/Userland/Libraries/LibSoftGPU/Device.cpp
+++ b/Userland/Libraries/LibSoftGPU/Device.cpp
@@ -801,4 +801,15 @@ float Device::get_depthbuffer_value(int x, int y)
return m_depth_buffer->scanline(y)[x];
}
+NonnullRefPtr<Image> Device::create_image(ImageFormat format, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers)
+{
+ 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));
+}
+
}
diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h
index 2835588882..789eb5ea9a 100644
--- a/Userland/Libraries/LibSoftGPU/Device.h
+++ b/Userland/Libraries/LibSoftGPU/Device.h
@@ -7,6 +7,7 @@
#pragma once
#include <AK/Array.h>
+#include <AK/NonnullRefPtr.h>
#include <AK/OwnPtr.h>
#include <LibGL/GL/gl.h>
#include <LibGL/Tex/Texture2D.h>
@@ -17,6 +18,8 @@
#include <LibGfx/Vector4.h>
#include <LibSoftGPU/Clipper.h>
#include <LibSoftGPU/DepthBuffer.h>
+#include <LibSoftGPU/Image.h>
+#include <LibSoftGPU/ImageFormat.h>
#include <LibSoftGPU/Triangle.h>
#include <LibSoftGPU/Vertex.h>
@@ -74,6 +77,8 @@ public:
Gfx::RGBA32 get_backbuffer_pixel(int x, int y);
float get_depthbuffer_value(int x, int y);
+ NonnullRefPtr<Image> create_image(ImageFormat, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers);
+
private:
void submit_triangle(Triangle const& triangle, GL::TextureUnit::BoundList const& bound_texture_units);
diff --git a/Userland/Libraries/LibSoftGPU/Sampler.cpp b/Userland/Libraries/LibSoftGPU/Sampler.cpp
new file mode 100644
index 0000000000..fe6b5db98a
--- /dev/null
+++ b/Userland/Libraries/LibSoftGPU/Sampler.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibSoftGPU/Image.h>
+#include <LibSoftGPU/Sampler.h>
+#include <math.h>
+
+namespace SoftGPU {
+
+constexpr static float fracf(float value)
+{
+ return value - floorf(value);
+}
+
+constexpr static float wrap_repeat(float value)
+{
+ return fracf(value);
+}
+
+static constexpr float wrap_clamp(float value)
+{
+ return clamp(value, 0.0f, 1.0f);
+}
+
+static constexpr float wrap_clamp_to_edge(float value, unsigned num_texels)
+{
+ float const clamp_limit = 1.f / (2 * num_texels);
+ return clamp(value, clamp_limit, 1.0f - clamp_limit);
+}
+
+static constexpr float wrap_mirrored_repeat(float value, unsigned num_texels)
+{
+ float integer = floorf(value);
+ float frac = value - integer;
+ bool iseven = fmodf(integer, 2.0f) == 0.0f;
+ return wrap_clamp_to_edge(iseven ? frac : 1 - frac, num_texels);
+}
+
+constexpr static float wrap(float value, TextureWrapMode mode, unsigned num_texels)
+{
+ switch (mode) {
+ case TextureWrapMode::Repeat:
+ return wrap_repeat(value);
+ case TextureWrapMode::MirroredRepeat:
+ return wrap_mirrored_repeat(value, num_texels);
+ case TextureWrapMode::Clamp:
+ return wrap_clamp(value);
+ case TextureWrapMode::ClampToBorder:
+ case TextureWrapMode::ClampToEdge:
+ return wrap_clamp_to_edge(value, num_texels);
+ default:
+ VERIFY_NOT_REACHED();
+ }
+}
+
+FloatVector4 Sampler::sample_2d(FloatVector2 const& uv) const
+{
+ if (m_config.bound_image.is_null())
+ return { 0, 0, 0, 1 };
+
+ auto const& image = *m_config.bound_image;
+
+ unsigned const layer = 0;
+ // FIXME: calculate actual mipmap level to use
+ unsigned const level = 0;
+
+ unsigned width = image.level_width(level);
+ unsigned height = image.level_height(level);
+
+ float s = wrap(uv.x(), m_config.texture_wrap_u, width);
+ float t = wrap(uv.y(), m_config.texture_wrap_v, height);
+
+ float u = s * width;
+ float v = t * height;
+
+ if (m_config.texture_mag_filter == TextureFilter::Nearest) {
+ unsigned i = min(static_cast<unsigned>(u), width - 1);
+ unsigned j = min(static_cast<unsigned>(v), height - 1);
+ return image.texel(layer, level, i, j, 0);
+ }
+
+ int i0 = m_config.texture_wrap_u == TextureWrapMode::Repeat ? static_cast<unsigned>(floorf(u - 0.5f)) % width : floorf(u - 0.5f);
+ int j0 = m_config.texture_wrap_v == TextureWrapMode::Repeat ? static_cast<unsigned>(floorf(v - 0.5f)) % height : floorf(v - 0.5f);
+
+ int i1 = m_config.texture_wrap_u == TextureWrapMode::Repeat ? (i0 + 1) % width : i0 + 1;
+ int j1 = m_config.texture_wrap_v == TextureWrapMode::Repeat ? (j0 + 1) % height : j0 + 1;
+
+ FloatVector4 t0, t1, t2, t3;
+
+ if (m_config.texture_wrap_u == TextureWrapMode::Repeat && m_config.texture_wrap_v == TextureWrapMode::Repeat) {
+ t0 = image.texel(layer, level, i0, j0, 0);
+ t1 = image.texel(layer, level, i1, j0, 0);
+ t2 = image.texel(layer, level, i0, j1, 0);
+ t3 = image.texel(layer, level, i1, j1, 0);
+ } else {
+ int w = static_cast<int>(width);
+ int h = static_cast<int>(height);
+ t0 = (i0 < 0 || i0 >= w || j0 < 0 || j0 >= h) ? m_config.border_color : image.texel(layer, level, i0, j0, 0);
+ t1 = (i1 < 0 || i1 >= w || j0 < 0 || j0 >= h) ? m_config.border_color : image.texel(layer, level, i1, j0, 0);
+ t2 = (i0 < 0 || i0 >= w || j1 < 0 || j1 >= h) ? m_config.border_color : image.texel(layer, level, i0, j1, 0);
+ t3 = (i1 < 0 || i1 >= w || j1 < 0 || j1 >= h) ? m_config.border_color : image.texel(layer, level, i1, j1, 0);
+ }
+
+ float alpha = fracf(u - 0.5f);
+ float beta = fracf(v - 0.5f);
+
+ float one_minus_alpha = 1 - alpha;
+ float one_minus_beta = 1 - beta;
+
+ auto lerp_0 = t0 * one_minus_alpha + t1 * alpha;
+ auto lerp_1 = t2 * one_minus_alpha + t3 * alpha;
+
+ return lerp_0 * one_minus_beta + lerp_1 * beta;
+}
+
+}
diff --git a/Userland/Libraries/LibSoftGPU/Sampler.h b/Userland/Libraries/LibSoftGPU/Sampler.h
new file mode 100644
index 0000000000..66335735c6
--- /dev/null
+++ b/Userland/Libraries/LibSoftGPU/Sampler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefPtr.h>
+#include <LibGfx/Vector2.h>
+#include <LibGfx/Vector4.h>
+#include <LibSoftGPU/Image.h>
+
+namespace SoftGPU {
+
+enum class TextureFilter {
+ Nearest,
+ Linear,
+};
+
+enum class MipMapFilter {
+ None,
+ Nearest,
+ Linear,
+};
+
+enum class TextureWrapMode {
+ Repeat,
+ MirroredRepeat,
+ Clamp,
+ ClampToBorder,
+ ClampToEdge,
+};
+
+struct SamplerConfig final {
+ RefPtr<Image> bound_image;
+ MipMapFilter mipmap_filter { MipMapFilter::Nearest };
+ TextureFilter texture_mag_filter { TextureFilter::Linear };
+ TextureFilter texture_min_filter { TextureFilter::Linear };
+ TextureWrapMode texture_wrap_u { TextureWrapMode::Repeat };
+ TextureWrapMode texture_wrap_v { TextureWrapMode::Repeat };
+ TextureWrapMode texture_wrap_w { TextureWrapMode::Repeat };
+ FloatVector4 border_color { 0, 0, 0, 1 };
+};
+
+class Sampler final {
+public:
+ FloatVector4 sample_2d(FloatVector2 const& uv) const;
+
+ void set_config(SamplerConfig const& config) { m_config = config; }
+
+private:
+ SamplerConfig m_config;
+};
+
+}