summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorStephan Unverwerth <s.unverwerth@serenityos.org>2022-01-06 17:52:07 +0100
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2022-01-09 16:21:13 +0330
commitb4a18eaaf0c990306e7554335c8e349a758f07d8 (patch)
treec0c53f481603be6b044333fc68df0ee1b213de47 /Userland
parentd89c515609cf78896a9533301fc46a8055c87c19 (diff)
downloadserenity-b4a18eaaf0c990306e7554335c8e349a758f07d8.zip
LibSoftGPU: Make rasterization and shading member functions of Device
This adds member functions Device::rasterize_triangle() and Device::shade_fragments(). They were free standing functions/lambdas previously which led to a lot of parameters being passed around.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.cpp201
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.h7
2 files changed, 106 insertions, 102 deletions
diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp
index 8bd0999540..fb74d6d922 100644
--- a/Userland/Libraries/LibSoftGPU/Device.cpp
+++ b/Userland/Libraries/LibSoftGPU/Device.cpp
@@ -135,18 +135,17 @@ static constexpr void setup_blend_factors(BlendFactor mode, FloatVector4& consta
}
}
-template<typename PS>
-static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& render_target, DepthBuffer& depth_buffer, const Triangle& triangle, PS pixel_shader)
+void Device::rasterize_triangle(const Triangle& triangle)
{
INCREASE_STATISTICS_COUNTER(g_num_rasterized_triangles, 1);
// Since the algorithm is based on blocks of uniform size, we need
- // to ensure that our render_target size is actually a multiple of the block size
- VERIFY((render_target.width() % 2) == 0);
- VERIFY((render_target.height() % 2) == 0);
+ // to ensure that our m_render_target size is actually a multiple of the block size
+ VERIFY((m_render_target->width() % 2) == 0);
+ VERIFY((m_render_target->height() % 2) == 0);
// Return if alpha testing is a no-op
- if (options.enable_alpha_test && options.alpha_test_func == AlphaTestFunction::Never)
+ if (m_options.enable_alpha_test && m_options.alpha_test_func == AlphaTestFunction::Never)
return;
// Vertices
@@ -179,9 +178,9 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
float dst_factor_src_color = 0;
float dst_factor_dst_color = 0;
- if (options.enable_blending) {
+ if (m_options.enable_blending) {
setup_blend_factors(
- options.blend_source_factor,
+ m_options.blend_source_factor,
src_constant,
src_factor_src_alpha,
src_factor_dst_alpha,
@@ -189,7 +188,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
src_factor_dst_color);
setup_blend_factors(
- options.blend_destination_factor,
+ m_options.blend_destination_factor,
dst_constant,
dst_factor_src_alpha,
dst_factor_dst_alpha,
@@ -197,9 +196,9 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
dst_factor_dst_color);
}
- auto render_bounds = render_target.rect();
- auto window_scissor_rect = scissor_box_to_window_coordinates(options.scissor_box, render_target.rect());
- if (options.scissor_enabled)
+ auto render_bounds = m_render_target->rect();
+ auto window_scissor_rect = scissor_box_to_window_coordinates(m_options.scissor_box, m_render_target->rect());
+ if (m_options.scissor_enabled)
render_bounds.intersect(window_scissor_rect);
// Obey top-left rule:
@@ -268,7 +267,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
// Generate triangle coverage mask
quad.mask = test_point4(edge_values);
- if (options.scissor_enabled) {
+ if (m_options.scissor_enabled) {
quad.mask &= test_scissor4(quad.screen_coordinates);
}
@@ -286,21 +285,21 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
} * one_over_area;
float* depth_ptrs[4] = {
- &depth_buffer.scanline(by)[bx],
- &depth_buffer.scanline(by)[bx + 1],
- &depth_buffer.scanline(by + 1)[bx],
- &depth_buffer.scanline(by + 1)[bx + 1],
+ &m_depth_buffer->scanline(by)[bx],
+ &m_depth_buffer->scanline(by)[bx + 1],
+ &m_depth_buffer->scanline(by + 1)[bx],
+ &m_depth_buffer->scanline(by + 1)[bx + 1],
};
// AND the depth mask onto the coverage mask
- if (options.enable_depth_test) {
+ if (m_options.enable_depth_test) {
auto depth = load4_masked(depth_ptrs[0], depth_ptrs[1], depth_ptrs[2], depth_ptrs[3], quad.mask);
quad.depth = interpolate(vertex0.window_coordinates.z(), vertex1.window_coordinates.z(), vertex2.window_coordinates.z(), quad.barycentrics);
// FIXME: Also apply depth_offset_factor which depends on the depth gradient
- quad.depth += options.depth_offset_constant * NumericLimits<float>::epsilon();
+ quad.depth += m_options.depth_offset_constant * NumericLimits<float>::epsilon();
- switch (options.depth_func) {
+ switch (m_options.depth_func) {
case DepthTestFunction::Always:
break;
case DepthTestFunction::Never:
@@ -370,7 +369,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
quad.barycentrics = quad.barycentrics * w_coordinates * interpolated_w;
// FIXME: make this more generic. We want to interpolate more than just color and uv
- if (options.shade_smooth) {
+ if (m_options.shade_smooth) {
quad.vertex_color = interpolate(expand4(vertex0.color), expand4(vertex1.color), expand4(vertex2.color), quad.barycentrics);
} else {
quad.vertex_color = expand4(vertex0.color);
@@ -378,7 +377,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
quad.uv = interpolate(expand4(vertex0.tex_coord), expand4(vertex1.tex_coord), expand4(vertex2.tex_coord), quad.barycentrics);
- if (options.fog_enabled) {
+ if (m_options.fog_enabled) {
// Calculate depth of fragment for fog
//
// OpenGL 1.5 spec chapter 3.10: "An implementation may choose to approximate the
@@ -387,27 +386,27 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
quad.fog_depth = interpolate(expand4(vertex0_eye_absz), expand4(vertex1_eye_absz), expand4(vertex2_eye_absz), quad.barycentrics);
}
- pixel_shader(quad);
+ shade_fragments(quad);
- if (options.enable_alpha_test && options.alpha_test_func != AlphaTestFunction::Always) {
- switch (options.alpha_test_func) {
+ if (m_options.enable_alpha_test && m_options.alpha_test_func != AlphaTestFunction::Always) {
+ switch (m_options.alpha_test_func) {
case AlphaTestFunction::Less:
- quad.mask &= quad.out_color.w() < options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() < m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::Equal:
- quad.mask &= quad.out_color.w() == options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() == m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::LessOrEqual:
- quad.mask &= quad.out_color.w() <= options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() <= m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::Greater:
- quad.mask &= quad.out_color.w() > options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() > m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::NotEqual:
- quad.mask &= quad.out_color.w() != options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() != m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::GreaterOrEqual:
- quad.mask &= quad.out_color.w() >= options.alpha_test_ref_value;
+ quad.mask &= quad.out_color.w() >= m_options.alpha_test_ref_value;
break;
case AlphaTestFunction::Never:
case AlphaTestFunction::Always:
@@ -416,29 +415,29 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
}
// Write to depth buffer
- if (options.enable_depth_test && options.enable_depth_write) {
+ if (m_options.enable_depth_test && m_options.enable_depth_write) {
store4_masked(quad.depth, depth_ptrs[0], depth_ptrs[1], depth_ptrs[2], depth_ptrs[3], quad.mask);
}
// We will not update the color buffer at all
- if (!options.color_mask || !options.enable_color_write)
+ if (!m_options.color_mask || !m_options.enable_color_write)
continue;
Gfx::RGBA32* color_ptrs[4] = {
- &render_target.scanline(by)[bx],
- &render_target.scanline(by)[bx + 1],
- &render_target.scanline(by + 1)[bx],
- &render_target.scanline(by + 1)[bx + 1],
+ &m_render_target->scanline(by)[bx],
+ &m_render_target->scanline(by)[bx + 1],
+ &m_render_target->scanline(by + 1)[bx],
+ &m_render_target->scanline(by + 1)[bx + 1],
};
u32x4 dst_u32;
- if (options.enable_blending || options.color_mask != 0xffffffff)
+ if (m_options.enable_blending || m_options.color_mask != 0xffffffff)
dst_u32 = load4_masked(color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask);
- if (options.enable_blending) {
+ if (m_options.enable_blending) {
INCREASE_STATISTICS_COUNTER(g_num_pixels_blended, maskcount(quad.mask));
- // Blend color values from pixel_staging into render_target
+ // Blend color values from pixel_staging into m_render_target
Vector4<f32x4> const& src = quad.out_color;
auto dst = to_vec4(dst_u32);
@@ -457,10 +456,10 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
quad.out_color = src * src_factor + dst * dst_factor;
}
- if (options.color_mask == 0xffffffff)
+ 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);
else
- store4_masked((to_rgba32(quad.out_color) & options.color_mask) | (dst_u32 & ~options.color_mask), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask);
+ 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);
}
}
}
@@ -568,6 +567,8 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
// 5. The vertices are sorted (for the rasterizer, how are we doing this? 3Dfx did this top to bottom in terms of vertex y coordinates)
// 6. The vertices are then sent off to the rasterizer and drawn to the screen
+ m_enabled_texture_units = enabled_texture_units;
+
float scr_width = m_render_target->width();
float scr_height = m_render_target->height();
@@ -737,72 +738,70 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
triangle.vertices[1].tex_coord = texture_transform * triangle.vertices[1].tex_coord;
triangle.vertices[2].tex_coord = texture_transform * triangle.vertices[2].tex_coord;
- submit_triangle(triangle, enabled_texture_units);
+ rasterize_triangle(triangle);
}
}
-void Device::submit_triangle(const Triangle& triangle, Vector<size_t> const& enabled_texture_units)
+ALWAYS_INLINE void Device::shade_fragments(PixelQuad& quad)
{
- rasterize_triangle(m_options, *m_render_target, *m_depth_buffer, triangle, [this, &enabled_texture_units](PixelQuad& quad) {
- quad.out_color = quad.vertex_color;
-
- for (size_t i : enabled_texture_units) {
- // FIXME: implement GL_TEXTURE_1D, GL_TEXTURE_3D and GL_TEXTURE_CUBE_MAP
- auto const& sampler = m_samplers[i];
-
- auto texel = sampler.sample_2d({ quad.uv.x(), quad.uv.y() });
- INCREASE_STATISTICS_COUNTER(g_num_sampler_calls, 1);
-
- // FIXME: Implement more blend modes
- switch (sampler.config().fixed_function_texture_env_mode) {
- case TextureEnvMode::Modulate:
- quad.out_color = quad.out_color * texel;
- break;
- case TextureEnvMode::Replace:
- quad.out_color = texel;
- break;
- case TextureEnvMode::Decal: {
- auto src_alpha = quad.out_color.w();
- quad.out_color.set_x(mix(quad.out_color.x(), texel.x(), src_alpha));
- quad.out_color.set_y(mix(quad.out_color.y(), texel.y(), src_alpha));
- quad.out_color.set_z(mix(quad.out_color.z(), texel.z(), src_alpha));
- break;
- }
- default:
- VERIFY_NOT_REACHED();
- }
+ quad.out_color = quad.vertex_color;
+
+ for (size_t i : m_enabled_texture_units) {
+ // FIXME: implement GL_TEXTURE_1D, GL_TEXTURE_3D and GL_TEXTURE_CUBE_MAP
+ auto const& sampler = m_samplers[i];
+
+ auto texel = sampler.sample_2d({ quad.uv.x(), quad.uv.y() });
+ INCREASE_STATISTICS_COUNTER(g_num_sampler_calls, 1);
+
+ // FIXME: Implement more blend modes
+ switch (sampler.config().fixed_function_texture_env_mode) {
+ case TextureEnvMode::Modulate:
+ quad.out_color = quad.out_color * texel;
+ break;
+ case TextureEnvMode::Replace:
+ quad.out_color = texel;
+ break;
+ case TextureEnvMode::Decal: {
+ auto src_alpha = quad.out_color.w();
+ quad.out_color.set_x(mix(quad.out_color.x(), texel.x(), src_alpha));
+ quad.out_color.set_y(mix(quad.out_color.y(), texel.y(), src_alpha));
+ quad.out_color.set_z(mix(quad.out_color.z(), texel.z(), src_alpha));
+ break;
}
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ }
- // Calculate fog
- // Math from here: https://opengl-notes.readthedocs.io/en/latest/topics/texturing/aliasing.html
-
- // FIXME: exponential fog is not vectorized, we should add a SIMD exp function that calculates an approximation.
- if (m_options.fog_enabled) {
- auto factor = expand4(0.0f);
- switch (m_options.fog_mode) {
- case FogMode::Linear:
- factor = (m_options.fog_end - quad.fog_depth) / (m_options.fog_end - m_options.fog_start);
- break;
- case FogMode::Exp: {
- auto argument = -m_options.fog_density * quad.fog_depth;
- factor = exp(argument);
- } break;
- case FogMode::Exp2: {
- auto argument = m_options.fog_density * quad.fog_depth;
- argument *= -argument;
- factor = exp(argument);
- } break;
- default:
- VERIFY_NOT_REACHED();
- }
-
- // Mix texel's RGB with fog's RBG - leave alpha alone
- auto fog_color = expand4(m_options.fog_color);
- quad.out_color.set_x(mix(fog_color.x(), quad.out_color.x(), factor));
- quad.out_color.set_y(mix(fog_color.y(), quad.out_color.y(), factor));
- quad.out_color.set_z(mix(fog_color.z(), quad.out_color.z(), factor));
+ // Calculate fog
+ // Math from here: https://opengl-notes.readthedocs.io/en/latest/topics/texturing/aliasing.html
+
+ // FIXME: exponential fog is not vectorized, we should add a SIMD exp function that calculates an approximation.
+ if (m_options.fog_enabled) {
+ auto factor = expand4(0.0f);
+ switch (m_options.fog_mode) {
+ case FogMode::Linear:
+ factor = (m_options.fog_end - quad.fog_depth) / (m_options.fog_end - m_options.fog_start);
+ break;
+ case FogMode::Exp: {
+ auto argument = -m_options.fog_density * quad.fog_depth;
+ factor = exp(argument);
+ } break;
+ case FogMode::Exp2: {
+ auto argument = m_options.fog_density * quad.fog_depth;
+ argument *= -argument;
+ factor = exp(argument);
+ } break;
+ default:
+ VERIFY_NOT_REACHED();
}
- });
+
+ // Mix texel's RGB with fog's RBG - leave alpha alone
+ auto fog_color = expand4(m_options.fog_color);
+ quad.out_color.set_x(mix(fog_color.x(), quad.out_color.x(), factor));
+ quad.out_color.set_y(mix(fog_color.y(), quad.out_color.y(), factor));
+ quad.out_color.set_z(mix(fog_color.z(), quad.out_color.z(), factor));
+ }
}
void Device::resize(const Gfx::IntSize& min_size)
diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h
index f8e2c1dcb6..bd821c1b31 100644
--- a/Userland/Libraries/LibSoftGPU/Device.h
+++ b/Userland/Libraries/LibSoftGPU/Device.h
@@ -67,6 +67,8 @@ struct RasterizerOptions {
Array<TexCoordGenerationConfig, 4> texcoord_generation_config {};
};
+struct PixelQuad;
+
class Device final {
public:
Device(const Gfx::IntSize& min_size);
@@ -90,9 +92,11 @@ public:
void set_sampler_config(unsigned, SamplerConfig const&);
private:
- void submit_triangle(Triangle const& triangle, Vector<size_t> const& enabled_texture_units);
void draw_statistics_overlay(Gfx::Bitmap&);
+ void rasterize_triangle(const Triangle& triangle);
+ void shade_fragments(PixelQuad&);
+
private:
RefPtr<Gfx::Bitmap> m_render_target;
OwnPtr<DepthBuffer> m_depth_buffer;
@@ -102,6 +106,7 @@ private:
Vector<Triangle> m_processed_triangles;
Vector<Vertex> m_clipped_vertices;
Array<Sampler, NUM_SAMPLERS> m_samplers;
+ Vector<size_t> m_enabled_texture_units;
};
}