2021-04-24 02:57:01 +03:00
/*
2021-05-29 13:38:28 +03:00
* Copyright ( c ) 2021 , Stephan Unverwerth < s . unverwerth @ serenityos . org >
2021-12-16 23:26:15 +03:00
* Copyright ( c ) 2021 , Jesse Buhagiar < jooster669 @ gmail . com >
2022-01-17 00:48:46 +03:00
* Copyright ( c ) 2022 , Jelle Raaijmakers < jelle @ gmta . nl >
2021-04-24 02:57:01 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
# include <AK/AnyOf.h>
2022-03-27 19:54:31 +03:00
# include <AK/Error.h>
2022-01-09 15:11:12 +03:00
# include <AK/Math.h>
2022-01-17 00:48:46 +03:00
# include <AK/NumericLimits.h>
2022-01-06 19:06:46 +03:00
# include <AK/SIMDExtras.h>
# include <AK/SIMDMath.h>
2021-12-30 03:04:55 +03:00
# include <LibCore/ElapsedTimer.h>
2021-04-24 02:57:01 +03:00
# include <LibGfx/Painter.h>
2021-05-11 20:25:11 +03:00
# include <LibGfx/Vector2.h>
# include <LibGfx/Vector3.h>
2021-12-29 23:59:13 +03:00
# include <LibSoftGPU/Config.h>
2021-12-18 16:07:47 +03:00
# include <LibSoftGPU/Device.h>
2022-01-06 19:06:46 +03:00
# include <LibSoftGPU/PixelQuad.h>
# include <LibSoftGPU/SIMD.h>
2022-02-03 18:45:08 +03:00
# include <math.h>
2021-04-24 02:57:01 +03:00
2021-12-16 22:32:38 +03:00
namespace SoftGPU {
2021-04-24 02:57:01 +03:00
2022-04-29 15:52:47 +03:00
static u64 g_num_rasterized_triangles ;
static u64 g_num_pixels ;
static u64 g_num_pixels_shaded ;
static u64 g_num_pixels_blended ;
static u64 g_num_sampler_calls ;
static u64 g_num_stencil_writes ;
static u64 g_num_quads ;
2021-12-30 03:04:55 +03:00
2022-04-29 16:01:59 +03:00
using AK : : abs ;
2022-01-06 23:28:16 +03:00
using AK : : SIMD : : any ;
2022-01-06 19:06:46 +03:00
using AK : : SIMD : : exp ;
using AK : : SIMD : : expand4 ;
using AK : : SIMD : : f32x4 ;
2022-01-02 00:11:50 +03:00
using AK : : SIMD : : i32x4 ;
using AK : : SIMD : : load4_masked ;
using AK : : SIMD : : maskbits ;
using AK : : SIMD : : maskcount ;
using AK : : SIMD : : store4_masked ;
using AK : : SIMD : : to_f32x4 ;
2022-01-05 22:21:13 +03:00
using AK : : SIMD : : to_u32x4 ;
using AK : : SIMD : : u32x4 ;
2022-01-06 19:06:46 +03:00
2022-04-29 16:11:25 +03:00
static constexpr int subpixel_factor = 1 < < SUBPIXEL_BITS ;
// Returns positive values for counter-clockwise rotation of vertices. Note that it returns the
// area of a parallelogram with sides {a, b} and {b, c}, so _double_ the area of the triangle {a, b, c}.
constexpr static i32 edge_function ( IntVector2 const & a , IntVector2 const & b , IntVector2 const & c )
2021-04-24 02:57:01 +03:00
{
2022-04-17 21:44:34 +03:00
return ( c . y ( ) - a . y ( ) ) * ( b . x ( ) - a . x ( ) ) - ( c . x ( ) - a . x ( ) ) * ( b . y ( ) - a . y ( ) ) ;
2021-04-24 02:57:01 +03:00
}
2022-04-29 16:11:25 +03:00
constexpr static i32x4 edge_function4 ( IntVector2 const & a , IntVector2 const & b , Vector2 < i32x4 > const & c )
2022-01-02 00:11:50 +03:00
{
2022-04-17 21:44:34 +03:00
return ( c . y ( ) - a . y ( ) ) * ( b . x ( ) - a . x ( ) ) - ( c . x ( ) - a . x ( ) ) * ( b . y ( ) - a . y ( ) ) ;
2022-01-02 00:11:50 +03:00
}
2022-01-06 19:06:46 +03:00
template < typename T , typename U >
2022-04-29 15:57:25 +03:00
constexpr static auto interpolate ( T const & v0 , T const & v1 , T const & v2 , Vector3 < U > const & barycentric_coords )
2021-05-09 00:17:13 +03:00
{
return v0 * barycentric_coords . x ( ) + v1 * barycentric_coords . y ( ) + v2 * barycentric_coords . z ( ) ;
}
2022-03-27 16:20:51 +03:00
static GPU : : ColorType to_bgra32 ( FloatVector4 const & color )
2022-01-24 00:15:28 +03:00
{
auto clamped = color . clamped ( 0.0f , 1.0f ) ;
auto r = static_cast < u8 > ( clamped . x ( ) * 255 ) ;
auto g = static_cast < u8 > ( clamped . y ( ) * 255 ) ;
auto b = static_cast < u8 > ( clamped . z ( ) * 255 ) ;
auto a = static_cast < u8 > ( clamped . w ( ) * 255 ) ;
return a < < 24 | r < < 16 | g < < 8 | b ;
}
ALWAYS_INLINE static u32x4 to_bgra32 ( Vector4 < f32x4 > const & v )
2021-04-24 02:57:01 +03:00
{
2022-01-05 22:21:13 +03:00
auto clamped = v . clamped ( expand4 ( 0.0f ) , expand4 ( 1.0f ) ) ;
auto r = to_u32x4 ( clamped . x ( ) * 255 ) ;
auto g = to_u32x4 ( clamped . y ( ) * 255 ) ;
auto b = to_u32x4 ( clamped . z ( ) * 255 ) ;
auto a = to_u32x4 ( clamped . w ( ) * 255 ) ;
2021-08-18 14:20:00 +03:00
return a < < 24 | r < < 16 | g < < 8 | b ;
2021-04-24 02:57:01 +03:00
}
2022-01-30 16:39:02 +03:00
static Vector4 < f32x4 > to_vec4 ( u32x4 bgra )
2021-05-16 00:40:04 +03:00
{
2022-01-05 22:21:13 +03:00
auto constexpr one_over_255 = expand4 ( 1.0f / 255 ) ;
2021-05-16 00:40:04 +03:00
return {
2022-01-30 16:39:02 +03:00
to_f32x4 ( ( bgra > > 16 ) & 0xff ) * one_over_255 ,
to_f32x4 ( ( bgra > > 8 ) & 0xff ) * one_over_255 ,
to_f32x4 ( bgra & 0xff ) * one_over_255 ,
to_f32x4 ( ( bgra > > 24 ) & 0xff ) * one_over_255 ,
2021-05-16 00:40:04 +03:00
} ;
}
2022-01-06 20:47:35 +03:00
void Device : : setup_blend_factors ( )
2021-05-16 00:40:04 +03:00
{
2022-01-26 20:26:52 +03:00
m_alpha_blend_factors = { } ;
2021-05-16 00:40:04 +03:00
2022-01-06 20:47:35 +03:00
switch ( m_options . blend_source_factor ) {
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : Zero :
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : One :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_factor_src_color = 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusSrcColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . src_factor_src_color = - 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_factor_src_alpha = 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusSrcAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . src_factor_src_alpha = - 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : DstAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_factor_dst_alpha = 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusDstAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . src_factor_dst_alpha = - 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : DstColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_factor_dst_color = 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusDstColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . src_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . src_factor_dst_color = - 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcAlphaSaturate :
2022-01-06 20:47:35 +03:00
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( m_options . blend_destination_factor ) {
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : Zero :
2022-01-06 20:47:35 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : One :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_factor_src_color = 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusSrcColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . dst_factor_src_color = - 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_factor_src_alpha = 1 ;
2021-05-16 00:40:04 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusSrcAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . dst_factor_src_alpha = - 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : DstAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_factor_dst_alpha = 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusDstAlpha :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . dst_factor_dst_alpha = - 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : DstColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_factor_dst_color = 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : OneMinusDstColor :
2022-01-06 20:47:35 +03:00
m_alpha_blend_factors . dst_constant = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_alpha_blend_factors . dst_factor_dst_color = - 1 ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : BlendFactor : : SrcAlphaSaturate :
2021-05-16 00:40:04 +03:00
default :
VERIFY_NOT_REACHED ( ) ;
}
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
template < typename CB1 , typename CB2 , typename CB3 >
ALWAYS_INLINE void Device : : rasterize ( Gfx : : IntRect & render_bounds , CB1 set_coverage_mask , CB2 set_quad_depth , CB3 set_quad_attributes )
2021-04-24 02:57:01 +03:00
{
2021-12-27 14:28:10 +03:00
// Return if alpha testing is a no-op
2022-03-15 03:11:58 +03:00
if ( m_options . enable_alpha_test & & m_options . alpha_test_func = = GPU : : AlphaTestFunction : : Never )
2021-12-27 14:28:10 +03:00
return ;
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Buffers
2022-01-24 00:15:28 +03:00
auto color_buffer = m_frame_buffer - > color_buffer ( ) ;
auto depth_buffer = m_frame_buffer - > depth_buffer ( ) ;
auto stencil_buffer = m_frame_buffer - > stencil_buffer ( ) ;
2022-01-17 00:48:46 +03:00
// Stencil configuration and writing
2022-03-15 03:11:58 +03:00
auto const & stencil_configuration = m_stencil_configuration [ GPU : : Face : : Front ] ;
2022-01-17 00:48:46 +03:00
auto const stencil_reference_value = stencil_configuration . reference_value & stencil_configuration . test_mask ;
2022-03-27 16:20:51 +03:00
auto write_to_stencil = [ ] ( GPU : : StencilType * stencil_ptrs [ 4 ] , i32x4 stencil_value , GPU : : StencilOperation op , GPU : : StencilType reference_value , GPU : : StencilType write_mask , i32x4 pixel_mask ) {
2022-03-15 03:11:58 +03:00
if ( write_mask = = 0 | | op = = GPU : : StencilOperation : : Keep )
2022-01-17 00:48:46 +03:00
return ;
switch ( op ) {
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : Decrement :
2022-01-17 00:48:46 +03:00
stencil_value = ( stencil_value & ~ write_mask ) | ( max ( stencil_value - 1 , expand4 ( 0 ) ) & write_mask ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : DecrementWrap :
2022-01-17 00:48:46 +03:00
stencil_value = ( stencil_value & ~ write_mask ) | ( ( ( stencil_value - 1 ) & 0xFF ) & write_mask ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : Increment :
2022-01-17 00:48:46 +03:00
stencil_value = ( stencil_value & ~ write_mask ) | ( min ( stencil_value + 1 , expand4 ( 0xFF ) ) & write_mask ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : IncrementWrap :
2022-01-17 00:48:46 +03:00
stencil_value = ( stencil_value & ~ write_mask ) | ( ( ( stencil_value + 1 ) & 0xFF ) & write_mask ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : Invert :
2022-01-17 00:48:46 +03:00
stencil_value ^ = write_mask ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : Replace :
2022-01-17 00:48:46 +03:00
stencil_value = ( stencil_value & ~ write_mask ) | ( reference_value & write_mask ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilOperation : : Zero :
2022-01-17 00:48:46 +03:00
stencil_value & = ~ write_mask ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2022-01-17 01:07:01 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_stencil_writes , maskcount ( pixel_mask ) ) ;
2022-01-17 00:48:46 +03:00
store4_masked ( stencil_value , stencil_ptrs [ 0 ] , stencil_ptrs [ 1 ] , stencil_ptrs [ 2 ] , stencil_ptrs [ 3 ] , pixel_mask ) ;
} ;
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Limit rendering to framebuffer and scissor rects
render_bounds . intersect ( m_frame_buffer - > rect ( ) ) ;
if ( m_options . scissor_enabled )
render_bounds . intersect ( m_options . scissor_box ) ;
2021-05-11 20:25:11 +03:00
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Quad bounds
auto const render_bounds_left = render_bounds . left ( ) ;
auto const render_bounds_right = render_bounds . right ( ) ;
auto const render_bounds_top = render_bounds . top ( ) ;
auto const render_bounds_bottom = render_bounds . bottom ( ) ;
auto const qx0 = render_bounds_left & ~ 1 ;
auto const qx1 = render_bounds_right & ~ 1 ;
auto const qy0 = render_bounds_top & ~ 1 ;
auto const qy1 = render_bounds_bottom & ~ 1 ;
// Rasterize all quads
// FIXME: this could be embarrasingly parallel
for ( int qy = qy0 ; qy < = qy1 ; qy + = 2 ) {
for ( int qx = qx0 ; qx < = qx1 ; qx + = 2 ) {
PixelQuad quad ;
2022-01-02 00:11:50 +03:00
quad . screen_coordinates = {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
i32x4 { qx , qx + 1 , qx , qx + 1 } ,
i32x4 { qy , qy , qy + 1 , qy + 1 } ,
2022-01-02 00:11:50 +03:00
} ;
2021-04-24 02:57:01 +03:00
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Set coverage mask and test against render bounds
set_coverage_mask ( quad ) ;
2022-01-08 04:49:15 +03:00
quad . mask & = quad . screen_coordinates . x ( ) > = render_bounds_left
2022-03-06 20:50:04 +03:00
& & quad . screen_coordinates . x ( ) < = render_bounds_right
2022-01-08 04:49:15 +03:00
& & quad . screen_coordinates . y ( ) > = render_bounds_top
2022-03-06 20:50:04 +03:00
& & quad . screen_coordinates . y ( ) < = render_bounds_bottom ;
2022-05-08 02:58:57 +03:00
auto coverage_bits = maskbits ( quad . mask ) ;
if ( coverage_bits = = 0 )
2022-01-02 00:11:50 +03:00
continue ;
2022-01-02 01:29:51 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_quads , 1 ) ;
2022-01-02 00:11:50 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_pixels , maskcount ( quad . mask ) ) ;
2022-01-17 00:48:46 +03:00
// Stencil testing
2022-03-27 16:20:51 +03:00
GPU : : StencilType * stencil_ptrs [ 4 ] ;
2022-01-17 00:48:46 +03:00
i32x4 stencil_value ;
if ( m_options . enable_stencil_test ) {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
stencil_ptrs [ 0 ] = coverage_bits & 1 ? & stencil_buffer - > scanline ( qy ) [ qx ] : nullptr ;
stencil_ptrs [ 1 ] = coverage_bits & 2 ? & stencil_buffer - > scanline ( qy ) [ qx + 1 ] : nullptr ;
stencil_ptrs [ 2 ] = coverage_bits & 4 ? & stencil_buffer - > scanline ( qy + 1 ) [ qx ] : nullptr ;
stencil_ptrs [ 3 ] = coverage_bits & 8 ? & stencil_buffer - > scanline ( qy + 1 ) [ qx + 1 ] : nullptr ;
2022-01-17 00:48:46 +03:00
stencil_value = load4_masked ( stencil_ptrs [ 0 ] , stencil_ptrs [ 1 ] , stencil_ptrs [ 2 ] , stencil_ptrs [ 3 ] , quad . mask ) ;
stencil_value & = stencil_configuration . test_mask ;
i32x4 stencil_test_passed ;
switch ( stencil_configuration . test_function ) {
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : Always :
2022-01-17 00:48:46 +03:00
stencil_test_passed = expand4 ( ~ 0 ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : Equal :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value = = stencil_reference_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : Greater :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value > stencil_reference_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : GreaterOrEqual :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value > = stencil_reference_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : Less :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value < stencil_reference_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : LessOrEqual :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value < = stencil_reference_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : Never :
2022-01-17 00:48:46 +03:00
stencil_test_passed = expand4 ( 0 ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : StencilTestFunction : : NotEqual :
2022-01-17 00:48:46 +03:00
stencil_test_passed = stencil_value ! = stencil_reference_value ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
// Update stencil buffer for pixels that failed the stencil test
write_to_stencil (
stencil_ptrs ,
stencil_value ,
stencil_configuration . on_stencil_test_fail ,
stencil_reference_value ,
stencil_configuration . write_mask ,
quad . mask & ~ stencil_test_passed ) ;
// Update coverage mask + early quad rejection
quad . mask & = stencil_test_passed ;
2022-05-08 02:58:57 +03:00
coverage_bits = maskbits ( quad . mask ) ;
if ( coverage_bits = = 0 )
2022-01-17 00:48:46 +03:00
continue ;
}
// Depth testing
2022-03-27 16:20:51 +03:00
GPU : : DepthType * depth_ptrs [ 4 ] = {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
coverage_bits & 1 ? & depth_buffer - > scanline ( qy ) [ qx ] : nullptr ,
coverage_bits & 2 ? & depth_buffer - > scanline ( qy ) [ qx + 1 ] : nullptr ,
coverage_bits & 4 ? & depth_buffer - > scanline ( qy + 1 ) [ qx ] : nullptr ,
coverage_bits & 8 ? & depth_buffer - > scanline ( qy + 1 ) [ qx + 1 ] : nullptr ,
2022-01-02 00:11:50 +03:00
} ;
2022-01-06 19:52:07 +03:00
if ( m_options . enable_depth_test ) {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
set_quad_depth ( quad ) ;
2022-01-02 00:11:50 +03:00
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
auto depth = load4_masked ( depth_ptrs [ 0 ] , depth_ptrs [ 1 ] , depth_ptrs [ 2 ] , depth_ptrs [ 3 ] , quad . mask ) ;
2022-01-17 00:48:46 +03:00
i32x4 depth_test_passed ;
2022-01-06 19:52:07 +03:00
switch ( m_options . depth_func ) {
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : Always :
2022-01-17 00:48:46 +03:00
depth_test_passed = expand4 ( ~ 0 ) ;
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : Never :
2022-01-17 00:48:46 +03:00
depth_test_passed = expand4 ( 0 ) ;
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : Greater :
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth > depth ;
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : GreaterOrEqual :
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth > = depth ;
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : NotEqual :
2021-08-20 14:19:42 +03:00
# ifdef __SSE__
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth ! = depth ;
2021-08-20 14:19:42 +03:00
# else
2022-01-17 00:48:46 +03:00
depth_test_passed = i32x4 {
bit_cast < u32 > ( quad . depth [ 0 ] ) ! = bit_cast < u32 > ( depth [ 0 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 1 ] ) ! = bit_cast < u32 > ( depth [ 1 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 2 ] ) ! = bit_cast < u32 > ( depth [ 2 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 3 ] ) ! = bit_cast < u32 > ( depth [ 3 ] ) ? - 1 : 0 ,
} ;
2021-08-20 14:19:42 +03:00
# endif
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : Equal :
2021-08-20 14:19:42 +03:00
# ifdef __SSE__
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth = = depth ;
2021-08-20 14:19:42 +03:00
# else
2022-01-02 00:11:50 +03:00
//
// This is an interesting quirk that occurs due to us using the x87 FPU when Serenity is
2022-05-08 03:06:24 +03:00
// compiled for the i686 target. When we calculate our depth value to be stored in the buffer,
2022-01-24 00:15:28 +03:00
// it is an 80-bit x87 floating point number, however, when stored into the depth buffer, this is
2022-01-02 00:11:50 +03:00
// truncated to 32 bits. This 38 bit loss of precision means that when x87 `FCOMP` is eventually
// used here the comparison fails.
// This could be solved by using a `long double` for the depth buffer, however this would take
// up significantly more space and is completely overkill for a depth buffer. As such, comparing
// the first 32-bits of this depth value is "good enough" that if we get a hit on it being
// equal, we can pretty much guarantee that it's actually equal.
//
2022-01-17 00:48:46 +03:00
depth_test_passed = i32x4 {
bit_cast < u32 > ( quad . depth [ 0 ] ) = = bit_cast < u32 > ( depth [ 0 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 1 ] ) = = bit_cast < u32 > ( depth [ 1 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 2 ] ) = = bit_cast < u32 > ( depth [ 2 ] ) ? - 1 : 0 ,
bit_cast < u32 > ( quad . depth [ 3 ] ) = = bit_cast < u32 > ( depth [ 3 ] ) ? - 1 : 0 ,
} ;
2021-08-20 14:19:42 +03:00
# endif
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : LessOrEqual :
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth < = depth ;
2022-01-02 00:11:50 +03:00
break ;
2022-03-15 03:11:58 +03:00
case GPU : : DepthTestFunction : : Less :
2022-01-17 00:48:46 +03:00
depth_test_passed = quad . depth < depth ;
2022-01-02 00:11:50 +03:00
break ;
2022-01-17 00:48:46 +03:00
default :
VERIFY_NOT_REACHED ( ) ;
2021-04-24 02:57:01 +03:00
}
2021-05-11 20:25:14 +03:00
2022-01-17 00:48:46 +03:00
// Update stencil buffer for pixels that failed the depth test
if ( m_options . enable_stencil_test ) {
write_to_stencil (
stencil_ptrs ,
stencil_value ,
stencil_configuration . on_depth_test_fail ,
stencil_reference_value ,
stencil_configuration . write_mask ,
quad . mask & ~ depth_test_passed ) ;
}
// Update coverage mask + early quad rejection
quad . mask & = depth_test_passed ;
2022-05-08 02:58:57 +03:00
coverage_bits = maskbits ( quad . mask ) ;
if ( coverage_bits = = 0 )
2021-05-11 20:25:14 +03:00
continue ;
}
2022-01-17 00:48:46 +03:00
// Update stencil buffer for passed pixels
if ( m_options . enable_stencil_test ) {
write_to_stencil (
stencil_ptrs ,
stencil_value ,
stencil_configuration . on_pass ,
stencil_reference_value ,
stencil_configuration . write_mask ,
quad . mask ) ;
}
2022-01-02 00:11:50 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_pixels_shaded , maskcount ( quad . mask ) ) ;
2021-12-30 02:53:43 +03:00
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
set_quad_attributes ( quad ) ;
2022-01-06 19:52:07 +03:00
shade_fragments ( quad ) ;
2022-01-02 00:11:50 +03:00
2022-03-15 03:11:58 +03:00
if ( m_options . enable_alpha_test & & m_options . alpha_test_func ! = GPU : : AlphaTestFunction : : Always & & ! test_alpha ( quad ) )
2022-01-06 23:28:16 +03:00
continue ;
2021-05-16 18:09:44 +03:00
2021-12-27 14:28:10 +03:00
// Write to depth buffer
2022-01-17 00:48:46 +03:00
if ( m_options . enable_depth_test & & m_options . enable_depth_write )
2022-01-02 00:11:50 +03:00
store4_masked ( quad . depth , depth_ptrs [ 0 ] , depth_ptrs [ 1 ] , depth_ptrs [ 2 ] , depth_ptrs [ 3 ] , quad . mask ) ;
2021-12-27 14:28:10 +03:00
// We will not update the color buffer at all
2022-02-06 04:31:11 +03:00
if ( ( m_options . color_mask = = 0 ) | | ! m_options . enable_color_write )
2021-12-27 14:28:10 +03:00
continue ;
2022-03-27 16:20:51 +03:00
GPU : : ColorType * color_ptrs [ 4 ] = {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
coverage_bits & 1 ? & color_buffer - > scanline ( qy ) [ qx ] : nullptr ,
coverage_bits & 2 ? & color_buffer - > scanline ( qy ) [ qx + 1 ] : nullptr ,
coverage_bits & 4 ? & color_buffer - > scanline ( qy + 1 ) [ qx ] : nullptr ,
coverage_bits & 8 ? & color_buffer - > scanline ( qy + 1 ) [ qx + 1 ] : nullptr ,
2022-01-02 00:11:50 +03:00
} ;
2022-01-05 22:21:13 +03:00
u32x4 dst_u32 ;
2022-01-06 19:52:07 +03:00
if ( m_options . enable_blending | | m_options . color_mask ! = 0xffffffff )
2022-01-05 22:21:13 +03:00
dst_u32 = load4_masked ( color_ptrs [ 0 ] , color_ptrs [ 1 ] , color_ptrs [ 2 ] , color_ptrs [ 3 ] , quad . mask ) ;
2022-01-02 00:11:50 +03:00
2022-01-06 19:52:07 +03:00
if ( m_options . enable_blending ) {
2022-01-02 00:11:50 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_pixels_blended , maskcount ( quad . mask ) ) ;
2022-01-24 00:15:28 +03:00
// Blend color values from pixel_staging into color_buffer
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
auto const & src = quad . out_color ;
2022-01-05 22:21:13 +03:00
auto dst = to_vec4 ( dst_u32 ) ;
2022-01-02 00:11:50 +03:00
2022-01-06 20:47:35 +03:00
auto src_factor = expand4 ( m_alpha_blend_factors . src_constant )
+ src * m_alpha_blend_factors . src_factor_src_color
+ Vector4 < f32x4 > { src . w ( ) , src . w ( ) , src . w ( ) , src . w ( ) } * m_alpha_blend_factors . src_factor_src_alpha
+ dst * m_alpha_blend_factors . src_factor_dst_color
+ Vector4 < f32x4 > { dst . w ( ) , dst . w ( ) , dst . w ( ) , dst . w ( ) } * m_alpha_blend_factors . src_factor_dst_alpha ;
2022-01-02 00:11:50 +03:00
2022-01-06 20:47:35 +03:00
auto dst_factor = expand4 ( m_alpha_blend_factors . dst_constant )
+ src * m_alpha_blend_factors . dst_factor_src_color
+ Vector4 < f32x4 > { src . w ( ) , src . w ( ) , src . w ( ) , src . w ( ) } * m_alpha_blend_factors . dst_factor_src_alpha
+ dst * m_alpha_blend_factors . dst_factor_dst_color
+ Vector4 < f32x4 > { dst . w ( ) , dst . w ( ) , dst . w ( ) , dst . w ( ) } * m_alpha_blend_factors . dst_factor_dst_alpha ;
2022-01-02 00:11:50 +03:00
quad . out_color = src * src_factor + dst * dst_factor ;
2021-04-24 02:57:01 +03:00
}
2022-01-02 00:11:50 +03:00
2022-01-06 19:52:07 +03:00
if ( m_options . color_mask = = 0xffffffff )
2022-01-24 00:15:28 +03:00
store4_masked ( to_bgra32 ( quad . out_color ) , color_ptrs [ 0 ] , color_ptrs [ 1 ] , color_ptrs [ 2 ] , color_ptrs [ 3 ] , quad . mask ) ;
2022-01-05 22:21:13 +03:00
else
2022-01-24 00:15:28 +03:00
store4_masked ( ( to_bgra32 ( 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 ) ;
2021-04-24 02:57:01 +03:00
}
}
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
void Device : : rasterize_line_aliased ( GPU : : Vertex & from , GPU : : Vertex & to )
{
// FIXME: implement aliased lines; for now we fall back to anti-aliased logic
rasterize_line_antialiased ( from , to ) ;
}
void Device : : rasterize_line_antialiased ( GPU : : Vertex & from , GPU : : Vertex & to )
{
auto const from_coords = from . window_coordinates . xy ( ) ;
auto const to_coords = to . window_coordinates . xy ( ) ;
auto const line_width = ceilf ( m_options . line_width ) ;
auto const line_radius = line_width / 2 ;
auto render_bounds = Gfx : : IntRect {
min ( from_coords . x ( ) , to_coords . x ( ) ) ,
min ( from_coords . y ( ) , to_coords . y ( ) ) ,
abs ( from_coords . x ( ) - to_coords . x ( ) ) + 1 ,
abs ( from_coords . y ( ) - to_coords . y ( ) ) + 1 ,
} ;
render_bounds . inflate ( line_width , line_width ) ;
auto const from_coords4 = expand4 ( from_coords ) ;
auto const line_vector = to_coords - from_coords ;
auto const line_vector4 = expand4 ( line_vector ) ;
auto const line_dot4 = expand4 ( line_vector . dot ( line_vector ) ) ;
auto const from_depth4 = expand4 ( from . window_coordinates . z ( ) ) ;
auto const to_depth4 = expand4 ( to . window_coordinates . z ( ) ) ;
auto const from_color4 = expand4 ( from . color ) ;
auto const from_fog_depth4 = expand4 ( abs ( from . eye_coordinates . z ( ) ) ) ;
// Rasterize using a 2D signed distance field for a line segment
// FIXME: performance-wise, this might be the absolute worst way to draw an anti-aliased line
f32x4 distance_along_line ;
rasterize (
render_bounds ,
[ & from_coords4 , & distance_along_line , & line_vector4 , & line_dot4 , & line_radius ] ( auto & quad ) {
auto const screen_coordinates4 = to_vec2_f32x4 ( quad . screen_coordinates ) ;
auto const pixel_vector = screen_coordinates4 - from_coords4 ;
distance_along_line = AK : : SIMD : : clamp ( pixel_vector . dot ( line_vector4 ) / line_dot4 , 0.f , 1.f ) ;
auto distance_to_line = length ( pixel_vector - line_vector4 * distance_along_line ) - line_radius ;
// Add .5f to the distance so coverage transitions half a pixel before the actual border
quad . coverage = 1.f - AK : : SIMD : : clamp ( distance_to_line + 0.5f , 0.f , 1.f ) ;
quad . mask = quad . coverage > 0.f ;
} ,
[ & from_depth4 , & to_depth4 , & distance_along_line ] ( auto & quad ) {
quad . depth = mix ( from_depth4 , to_depth4 , distance_along_line ) ;
} ,
[ & from_color4 , & from , & from_fog_depth4 ] ( auto & quad ) {
// FIXME: interpolate color, tex coords and fog depth along the distance of the line
// in clip space (i.e. NOT distance_from_line)
quad . vertex_color = from_color4 ;
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i )
quad . texture_coordinates [ i ] = expand4 ( from . tex_coords [ i ] ) ;
quad . fog_depth = from_fog_depth4 ;
} ) ;
}
void Device : : rasterize_line ( GPU : : Vertex & from , GPU : : Vertex & to )
{
if ( m_options . line_smooth )
rasterize_line_antialiased ( from , to ) ;
else
rasterize_line_aliased ( from , to ) ;
}
void Device : : rasterize_point_aliased ( GPU : : Vertex & point )
{
// Determine aliased point width
constexpr size_t maximum_aliased_point_size = 64 ;
auto point_width = clamp ( round_to < int > ( m_options . point_size ) , 1 , maximum_aliased_point_size ) ;
// Determine aliased center coordinates
IntVector2 point_center ;
if ( point_width % 2 = = 1 )
point_center = point . window_coordinates . xy ( ) . to_type < int > ( ) ;
else
point_center = ( point . window_coordinates . xy ( ) + FloatVector2 { .5f , .5f } ) . to_type < int > ( ) ;
// Aliased points are rects; calculate boundaries around center
auto point_rect = Gfx : : IntRect {
point_center . x ( ) - point_width / 2 ,
point_center . y ( ) - point_width / 2 ,
point_width ,
point_width ,
} ;
// Rasterize the point as a rect
rasterize (
point_rect ,
[ ] ( auto & quad ) {
// We already passed in point_rect, so this doesn't matter
quad . mask = expand4 ( ~ 0 ) ;
} ,
[ & point ] ( auto & quad ) {
quad . depth = expand4 ( point . window_coordinates . z ( ) ) ;
} ,
[ & point ] ( auto & quad ) {
quad . vertex_color = expand4 ( point . color ) ;
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i )
quad . texture_coordinates [ i ] = expand4 ( point . tex_coords [ i ] ) ;
quad . fog_depth = expand4 ( abs ( point . eye_coordinates . z ( ) ) ) ;
} ) ;
}
void Device : : rasterize_point_antialiased ( GPU : : Vertex & point )
{
auto const center = point . window_coordinates . xy ( ) ;
auto const center4 = expand4 ( center ) ;
auto const radius = m_options . point_size / 2 ;
auto render_bounds = Gfx : : IntRect {
center . x ( ) - radius ,
center . y ( ) - radius ,
radius * 2 + 1 ,
radius * 2 + 1 ,
} ;
// Rasterize using a 2D signed distance field for a circle
rasterize (
render_bounds ,
[ & center4 , & radius ] ( auto & quad ) {
auto screen_coords = to_vec2_f32x4 ( quad . screen_coordinates ) ;
auto distance_to_point = length ( center4 - screen_coords ) - radius ;
// Add .5f to the distance so coverage transitions half a pixel before the actual border
quad . coverage = 1.f - AK : : SIMD : : clamp ( distance_to_point + .5f , 0.f , 1.f ) ;
quad . mask = quad . coverage > 0.f ;
} ,
[ & point ] ( auto & quad ) {
quad . depth = expand4 ( point . window_coordinates . z ( ) ) ;
} ,
[ & point ] ( auto & quad ) {
quad . vertex_color = expand4 ( point . color ) ;
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i )
quad . texture_coordinates [ i ] = expand4 ( point . tex_coords [ i ] ) ;
quad . fog_depth = expand4 ( abs ( point . eye_coordinates . z ( ) ) ) ;
} ) ;
}
void Device : : rasterize_point ( GPU : : Vertex & point )
{
// Divide texture coordinates R, S and T by Q
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i ) {
auto & tex_coord = point . tex_coords [ i ] ;
auto one_over_w = 1 / tex_coord . w ( ) ;
tex_coord = {
tex_coord . x ( ) * one_over_w ,
tex_coord . y ( ) * one_over_w ,
tex_coord . z ( ) * one_over_w ,
tex_coord . w ( ) ,
} ;
}
if ( m_options . point_smooth )
rasterize_point_antialiased ( point ) ;
else
rasterize_point_aliased ( point ) ;
}
void Device : : rasterize_triangle ( Triangle & triangle )
{
INCREASE_STATISTICS_COUNTER ( g_num_rasterized_triangles , 1 ) ;
auto v0 = ( triangle . vertices [ 0 ] . window_coordinates . xy ( ) * subpixel_factor ) . to_rounded < int > ( ) ;
auto v1 = ( triangle . vertices [ 1 ] . window_coordinates . xy ( ) * subpixel_factor ) . to_rounded < int > ( ) ;
auto v2 = ( triangle . vertices [ 2 ] . window_coordinates . xy ( ) * subpixel_factor ) . to_rounded < int > ( ) ;
auto triangle_area = edge_function ( v0 , v1 , v2 ) ;
if ( triangle_area = = 0 )
return ;
// Perform face culling
if ( m_options . enable_culling ) {
bool is_front = ( m_options . front_face = = GPU : : WindingOrder : : CounterClockwise ? triangle_area > 0 : triangle_area < 0 ) ;
if ( ! is_front & & m_options . cull_back )
return ;
if ( is_front & & m_options . cull_front )
return ;
}
// Force counter-clockwise ordering of vertices
if ( triangle_area < 0 ) {
swap ( triangle . vertices [ 0 ] , triangle . vertices [ 1 ] ) ;
swap ( v0 , v1 ) ;
triangle_area * = - 1 ;
}
auto const & vertex0 = triangle . vertices [ 0 ] ;
auto const & vertex1 = triangle . vertices [ 1 ] ;
auto const & vertex2 = triangle . vertices [ 2 ] ;
auto const one_over_area = 1.0f / triangle_area ;
// This function calculates the 3 edge values for the pixel relative to the triangle.
auto calculate_edge_values4 = [ v0 , v1 , v2 ] ( Vector2 < i32x4 > const & p ) - > Vector3 < i32x4 > {
return {
edge_function4 ( v1 , v2 , p ) ,
edge_function4 ( v2 , v0 , p ) ,
edge_function4 ( v0 , v1 , p ) ,
} ;
} ;
// Zero is used in testing against edge values below, applying the "top-left rule". If a pixel
// lies exactly on an edge shared by two triangles, we only render that pixel if the edge in
// question is a "top" or "left" edge. By setting either a 1 or 0, we effectively change the
// comparisons against the edge values below from "> 0" into ">= 0".
IntVector3 const zero {
( v2 . y ( ) < v1 . y ( ) | | ( v2 . y ( ) = = v1 . y ( ) & & v2 . x ( ) < v1 . x ( ) ) ) ? 0 : 1 ,
( v0 . y ( ) < v2 . y ( ) | | ( v0 . y ( ) = = v2 . y ( ) & & v0 . x ( ) < v2 . x ( ) ) ) ? 0 : 1 ,
( v1 . y ( ) < v0 . y ( ) | | ( v1 . y ( ) = = v0 . y ( ) & & v1 . x ( ) < v0 . x ( ) ) ) ? 0 : 1 ,
} ;
// This function tests whether a point as identified by its 3 edge values lies within the triangle
auto test_point4 = [ zero ] ( Vector3 < i32x4 > const & edges ) - > i32x4 {
return edges . x ( ) > = zero . x ( )
& & edges . y ( ) > = zero . y ( )
& & edges . z ( ) > = zero . z ( ) ;
} ;
// Calculate render bounds based on the triangle's vertices
Gfx : : IntRect render_bounds ;
render_bounds . set_left ( min ( min ( v0 . x ( ) , v1 . x ( ) ) , v2 . x ( ) ) / subpixel_factor ) ;
render_bounds . set_right ( max ( max ( v0 . x ( ) , v1 . x ( ) ) , v2 . x ( ) ) / subpixel_factor ) ;
render_bounds . set_top ( min ( min ( v0 . y ( ) , v1 . y ( ) ) , v2 . y ( ) ) / subpixel_factor ) ;
render_bounds . set_bottom ( max ( max ( v0 . y ( ) , v1 . y ( ) ) , v2 . y ( ) ) / subpixel_factor ) ;
// Calculate depth of fragment for fog;
// OpenGL 1.5 chapter 3.10: "An implementation may choose to approximate the
// eye-coordinate distance from the eye to each fragment center by |Ze|."
Vector3 < f32x4 > fog_depth ;
if ( m_options . fog_enabled ) {
fog_depth = {
expand4 ( abs ( vertex0 . eye_coordinates . z ( ) ) ) ,
expand4 ( abs ( vertex1 . eye_coordinates . z ( ) ) ) ,
expand4 ( abs ( vertex2 . eye_coordinates . z ( ) ) ) ,
} ;
}
auto const half_pixel_offset = Vector2 < i32x4 > { expand4 ( subpixel_factor / 2 ) , expand4 ( subpixel_factor / 2 ) } ;
auto const window_z_coordinates = Vector3 < f32x4 > {
expand4 ( vertex0 . window_coordinates . z ( ) ) ,
expand4 ( vertex1 . window_coordinates . z ( ) ) ,
expand4 ( vertex2 . window_coordinates . z ( ) ) ,
} ;
auto const window_w_coordinates = Vector3 < f32x4 > {
expand4 ( vertex0 . window_coordinates . w ( ) ) ,
expand4 ( vertex1 . window_coordinates . w ( ) ) ,
expand4 ( vertex2 . window_coordinates . w ( ) ) ,
} ;
2022-05-10 02:32:04 +03:00
// Calculate depth offset to apply
float depth_offset = 0.f ;
if ( m_options . depth_offset_enabled ) {
// Edge value deltas
auto edge_value_step_x = FloatVector3 {
static_cast < float > ( v1 . y ( ) - v2 . y ( ) ) ,
static_cast < float > ( v2 . y ( ) - v0 . y ( ) ) ,
static_cast < float > ( v0 . y ( ) - v1 . y ( ) ) ,
} ;
auto edge_value_step_y = FloatVector3 {
static_cast < float > ( v2 . x ( ) - v1 . x ( ) ) ,
static_cast < float > ( v0 . x ( ) - v2 . x ( ) ) ,
static_cast < float > ( v1 . x ( ) - v0 . x ( ) ) ,
} ;
// Barycentric deltas
auto barycentric_step_x = edge_value_step_x * one_over_area ;
auto barycentric_step_y = edge_value_step_y * one_over_area ;
// Depth delta vector and slope (magnitude)
auto depth_coordinates = FloatVector3 {
vertex0 . window_coordinates . z ( ) ,
vertex1 . window_coordinates . z ( ) ,
vertex2 . window_coordinates . z ( ) ,
} ;
auto depth_step = FloatVector2 {
depth_coordinates . dot ( barycentric_step_x ) ,
depth_coordinates . dot ( barycentric_step_y ) ,
} ;
auto depth_max_slope = depth_step . length ( ) ;
// Calculate total depth offset
depth_offset = depth_max_slope * m_options . depth_offset_factor + NumericLimits < float > : : epsilon ( ) * m_options . depth_offset_constant ;
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
rasterize (
render_bounds ,
[ & ] ( auto & quad ) {
auto edge_values = calculate_edge_values4 ( quad . screen_coordinates * subpixel_factor + half_pixel_offset ) ;
quad . mask = test_point4 ( edge_values ) ;
quad . barycentrics = {
to_f32x4 ( edge_values . x ( ) ) ,
to_f32x4 ( edge_values . y ( ) ) ,
to_f32x4 ( edge_values . z ( ) ) ,
} ;
} ,
2022-05-10 02:32:04 +03:00
[ & ] ( auto & quad ) {
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Determine each edge's ratio to the total area
quad . barycentrics = quad . barycentrics * one_over_area ;
// Because the Z coordinates were divided by W, we can interpolate between them
2022-05-10 02:32:04 +03:00
quad . depth = window_z_coordinates . dot ( quad . barycentrics ) + depth_offset ;
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
} ,
[ & ] ( auto & quad ) {
auto const interpolated_reciprocal_w = window_w_coordinates . dot ( quad . barycentrics ) ;
quad . barycentrics = quad . barycentrics * window_w_coordinates / interpolated_reciprocal_w ;
// FIXME: make this more generic. We want to interpolate more than just color and uv
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 ) ;
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i )
quad . texture_coordinates [ i ] = interpolate ( expand4 ( vertex0 . tex_coords [ i ] ) , expand4 ( vertex1 . tex_coords [ i ] ) , expand4 ( vertex2 . tex_coords [ i ] ) , quad . barycentrics ) ;
if ( m_options . fog_enabled )
quad . fog_depth = fog_depth . dot ( quad . barycentrics ) ;
} ) ;
}
2022-01-24 00:15:28 +03:00
Device : : Device ( Gfx : : IntSize const & size )
2022-03-27 16:20:51 +03:00
: m_frame_buffer ( FrameBuffer < GPU : : ColorType , GPU : : DepthType , GPU : : StencilType > : : try_create ( size ) . release_value_but_fixme_should_propagate_errors ( ) )
2021-04-24 02:57:01 +03:00
{
2022-01-24 00:15:28 +03:00
m_options . scissor_box = m_frame_buffer - > rect ( ) ;
m_options . viewport = m_frame_buffer - > rect ( ) ;
2021-04-24 02:57:01 +03:00
}
2022-03-15 02:07:25 +03:00
GPU : : DeviceInfo Device : : info ( ) const
2021-12-23 04:38:20 +03:00
{
return {
. vendor_name = " SerenityOS " ,
. device_name = " SoftGPU " ,
2022-03-27 16:43:51 +03:00
. num_texture_units = GPU : : NUM_SAMPLERS ,
2022-01-17 00:48:46 +03:00
. num_lights = NUM_LIGHTS ,
2022-03-27 16:20:51 +03:00
. stencil_bits = sizeof ( GPU : : StencilType ) * 8 ,
2022-01-15 19:20:31 +03:00
. supports_npot_textures = true ,
2021-12-23 04:38:20 +03:00
} ;
}
2022-03-27 16:50:06 +03:00
static void generate_texture_coordinates ( GPU : : Vertex & vertex , GPU : : RasterizerOptions const & options )
2021-12-30 02:56:41 +03:00
{
2022-01-15 23:08:57 +03:00
auto generate_coordinate = [ & ] ( size_t texcoord_index , size_t config_index ) - > float {
auto mode = options . texcoord_generation_config [ texcoord_index ] [ config_index ] . mode ;
2021-12-30 02:56:41 +03:00
switch ( mode ) {
2022-03-15 03:11:58 +03:00
case GPU : : TexCoordGenerationMode : : ObjectLinear : {
2022-01-15 23:08:57 +03:00
auto coefficients = options . texcoord_generation_config [ texcoord_index ] [ config_index ] . coefficients ;
2021-12-30 02:56:41 +03:00
return coefficients . dot ( vertex . position ) ;
}
2022-03-15 03:11:58 +03:00
case GPU : : TexCoordGenerationMode : : EyeLinear : {
2022-01-15 23:08:57 +03:00
auto coefficients = options . texcoord_generation_config [ texcoord_index ] [ config_index ] . coefficients ;
2021-12-30 02:56:41 +03:00
return coefficients . dot ( vertex . eye_coordinates ) ;
}
2022-03-15 03:11:58 +03:00
case GPU : : TexCoordGenerationMode : : SphereMap : {
2021-12-30 02:56:41 +03:00
auto const eye_unit = vertex . eye_coordinates . normalized ( ) ;
2022-03-06 21:12:01 +03:00
FloatVector3 const eye_unit_xyz = eye_unit . xyz ( ) ;
2021-12-30 02:56:41 +03:00
auto const normal = vertex . normal ;
auto reflection = eye_unit_xyz - normal * 2 * normal . dot ( eye_unit_xyz ) ;
reflection . set_z ( reflection . z ( ) + 1 ) ;
2022-03-10 22:43:28 +03:00
auto const reflection_value = reflection [ config_index ] ;
2021-12-30 02:56:41 +03:00
return reflection_value / ( 2 * reflection . length ( ) ) + 0.5f ;
}
2022-03-15 03:11:58 +03:00
case GPU : : TexCoordGenerationMode : : ReflectionMap : {
2021-12-30 02:56:41 +03:00
auto const eye_unit = vertex . eye_coordinates . normalized ( ) ;
2022-03-06 21:12:01 +03:00
FloatVector3 const eye_unit_xyz = eye_unit . xyz ( ) ;
2021-12-30 02:56:41 +03:00
auto const normal = vertex . normal ;
auto reflection = eye_unit_xyz - normal * 2 * normal . dot ( eye_unit_xyz ) ;
2022-03-10 22:43:28 +03:00
return reflection [ config_index ] ;
2021-12-30 02:56:41 +03:00
}
2022-03-15 03:11:58 +03:00
case GPU : : TexCoordGenerationMode : : NormalMap : {
2022-03-10 22:43:28 +03:00
return vertex . normal [ config_index ] ;
2021-12-30 02:56:41 +03:00
}
default :
VERIFY_NOT_REACHED ( ) ;
}
} ;
2022-01-15 23:08:57 +03:00
for ( size_t i = 0 ; i < vertex . tex_coords . size ( ) ; + + i ) {
auto & tex_coord = vertex . tex_coords [ i ] ;
auto const enabled_coords = options . texcoord_generation_enabled_coordinates [ i ] ;
tex_coord = {
2022-03-15 03:11:58 +03:00
( ( enabled_coords & GPU : : TexCoordGenerationCoordinate : : S ) > 0 ) ? generate_coordinate ( i , 0 ) : tex_coord . x ( ) ,
( ( enabled_coords & GPU : : TexCoordGenerationCoordinate : : T ) > 0 ) ? generate_coordinate ( i , 1 ) : tex_coord . y ( ) ,
( ( enabled_coords & GPU : : TexCoordGenerationCoordinate : : R ) > 0 ) ? generate_coordinate ( i , 2 ) : tex_coord . z ( ) ,
( ( enabled_coords & GPU : : TexCoordGenerationCoordinate : : Q ) > 0 ) ? generate_coordinate ( i , 3 ) : tex_coord . w ( ) ,
2022-01-15 23:08:57 +03:00
} ;
}
2021-12-30 02:56:41 +03:00
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
void Device : : calculate_vertex_lighting ( GPU : : Vertex & vertex ) const
{
if ( ! m_options . lighting_enabled )
return ;
auto const & material = m_materials . at ( 0 ) ;
auto ambient = material . ambient ;
auto diffuse = material . diffuse ;
auto emissive = material . emissive ;
auto specular = material . specular ;
if ( m_options . color_material_enabled
& & ( m_options . color_material_face = = GPU : : ColorMaterialFace : : Front | | m_options . color_material_face = = GPU : : ColorMaterialFace : : FrontAndBack ) ) {
switch ( m_options . color_material_mode ) {
case GPU : : ColorMaterialMode : : Ambient :
ambient = vertex . color ;
break ;
case GPU : : ColorMaterialMode : : AmbientAndDiffuse :
ambient = vertex . color ;
diffuse = vertex . color ;
break ;
case GPU : : ColorMaterialMode : : Diffuse :
diffuse = vertex . color ;
break ;
case GPU : : ColorMaterialMode : : Emissive :
emissive = vertex . color ;
break ;
case GPU : : ColorMaterialMode : : Specular :
specular = vertex . color ;
break ;
}
}
FloatVector4 result_color = emissive + ambient * m_lighting_model . scene_ambient_color ;
for ( auto const & light : m_lights ) {
if ( ! light . is_enabled )
continue ;
// We need to save the length here because the attenuation factor requires a non-normalized vector!
auto sgi_arrow_operator = [ ] ( FloatVector4 const & p1 , FloatVector4 const & p2 , float & output_length ) {
FloatVector3 light_vector ;
if ( ( p1 . w ( ) ! = 0.f ) & & ( p2 . w ( ) = = 0.f ) )
light_vector = p2 . xyz ( ) ;
else if ( ( p1 . w ( ) = = 0.f ) & & ( p2 . w ( ) ! = 0.f ) )
light_vector = - p1 . xyz ( ) ;
else
light_vector = p2 . xyz ( ) - p1 . xyz ( ) ;
output_length = light_vector . length ( ) ;
if ( output_length = = 0.f )
return light_vector ;
return light_vector / output_length ;
} ;
auto sgi_dot_operator = [ ] ( FloatVector3 const & d1 , FloatVector3 const & d2 ) {
return AK : : max ( d1 . dot ( d2 ) , 0.0f ) ;
} ;
float vertex_to_light_length = 0.f ;
FloatVector3 vertex_to_light = sgi_arrow_operator ( vertex . eye_coordinates , light . position , vertex_to_light_length ) ;
// Light attenuation value.
float light_attenuation_factor = 1.0f ;
if ( light . position . w ( ) ! = 0.0f )
light_attenuation_factor = 1.0f / ( light . constant_attenuation + ( light . linear_attenuation * vertex_to_light_length ) + ( light . quadratic_attenuation * vertex_to_light_length * vertex_to_light_length ) ) ;
// Spotlight factor
float spotlight_factor = 1.0f ;
if ( light . spotlight_cutoff_angle ! = 180.0f ) {
auto const vertex_to_light_dot_spotlight_direction = sgi_dot_operator ( vertex_to_light , light . spotlight_direction . normalized ( ) ) ;
auto const cos_spotlight_cutoff = AK : : cos < float > ( light . spotlight_cutoff_angle * AK : : Pi < float > / 180.f ) ;
if ( vertex_to_light_dot_spotlight_direction > = cos_spotlight_cutoff )
spotlight_factor = AK : : pow < float > ( vertex_to_light_dot_spotlight_direction , light . spotlight_exponent ) ;
else
spotlight_factor = 0.0f ;
}
// FIXME: The spec allows for splitting the colors calculated here into multiple different colors (primary/secondary color). Investigate what this means.
( void ) m_lighting_model . color_control ;
// FIXME: Two sided lighting should be implemented eventually (I believe this is where the normals are -ve and then lighting is calculated with the BACK material)
( void ) m_lighting_model . two_sided_lighting ;
// Ambient
auto const ambient_component = ambient * light . ambient_intensity ;
// Diffuse
auto const normal_dot_vertex_to_light = sgi_dot_operator ( vertex . normal , vertex_to_light ) ;
auto const diffuse_component = diffuse * light . diffuse_intensity * normal_dot_vertex_to_light ;
// Specular
FloatVector4 specular_component = { 0.0f , 0.0f , 0.0f , 0.0f } ;
if ( normal_dot_vertex_to_light > 0.0f ) {
FloatVector3 half_vector_normalized ;
if ( ! m_lighting_model . viewer_at_infinity ) {
half_vector_normalized = vertex_to_light + FloatVector3 ( 0.0f , 0.0f , 1.0f ) ;
} else {
auto const vertex_to_eye_point = sgi_arrow_operator ( vertex . eye_coordinates , { 0.f , 0.f , 0.f , 1.f } , vertex_to_light_length ) ;
half_vector_normalized = vertex_to_light + vertex_to_eye_point ;
}
half_vector_normalized . normalize ( ) ;
auto const normal_dot_half_vector = sgi_dot_operator ( vertex . normal , half_vector_normalized ) ;
auto const specular_coefficient = AK : : pow ( normal_dot_half_vector , material . shininess ) ;
specular_component = specular * light . specular_intensity * specular_coefficient ;
}
auto color = ambient_component + diffuse_component + specular_component ;
color = color * light_attenuation_factor * spotlight_factor ;
result_color + = color ;
}
vertex . color = result_color ;
vertex . color . set_w ( diffuse . w ( ) ) ; // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material"
vertex . color . clamp ( 0.0f , 1.0f ) ;
}
2022-03-15 03:11:58 +03:00
void Device : : draw_primitives ( GPU : : PrimitiveType primitive_type , FloatMatrix4x4 const & model_view_transform , FloatMatrix4x4 const & projection_transform ,
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
FloatMatrix4x4 const & texture_transform , Vector < GPU : : Vertex > & vertices , Vector < size_t > const & enabled_texture_units )
2021-12-16 23:26:15 +03:00
{
// At this point, the user has effectively specified that they are done with defining the geometry
// of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
//
2022-01-07 15:04:05 +03:00
// 1. Transform all of the vertices in the current vertex list into eye space by multiplying the model-view matrix
2021-12-16 23:26:15 +03:00
// 2. Transform all of the vertices from eye space into clip space by multiplying by the projection matrix
// 3. If culling is enabled, we cull the desired faces (https://learnopengl.com/Advanced-OpenGL/Face-culling)
// 4. Each element of the vertex is then divided by w to bring the positions into NDC (Normalized Device Coordinates)
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// 5. The triangle's vertices are sorted in a counter-clockwise orientation
// 6. The triangles are then sent off to the rasterizer and drawn to the screen
if ( vertices . is_empty ( ) )
return ;
2021-12-16 23:26:15 +03:00
2022-01-06 19:52:07 +03:00
m_enabled_texture_units = enabled_texture_units ;
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Set up normals transform by taking the upper left 3x3 elements from the model view matrix
// See section 2.11.3 of the OpenGL 1.5 spec
auto const normal_transform = model_view_transform . submatrix_from_topleft < 3 > ( ) . transpose ( ) . inverse ( ) ;
// Generate texture coordinates if at least one coordinate is enabled
bool texture_coordinate_generation_enabled = any_of (
m_options . texcoord_generation_enabled_coordinates ,
[ ] ( auto coordinates_enabled ) { return coordinates_enabled ! = GPU : : TexCoordGenerationCoordinate : : None ; } ) ;
// First, transform all vertices
for ( auto & vertex : vertices ) {
vertex . eye_coordinates = model_view_transform * vertex . position ;
vertex . normal = normal_transform * vertex . normal ;
if ( m_options . normalization_enabled )
vertex . normal . normalize ( ) ;
calculate_vertex_lighting ( vertex ) ;
vertex . clip_coordinates = projection_transform * vertex . eye_coordinates ;
if ( texture_coordinate_generation_enabled )
generate_texture_coordinates ( vertex , m_options ) ;
for ( size_t i = 0 ; i < GPU : : NUM_SAMPLERS ; + + i )
vertex . tex_coords [ i ] = texture_transform * vertex . tex_coords [ i ] ;
}
// Window coordinate calculation
auto const viewport = m_options . viewport ;
auto const viewport_half_width = viewport . width ( ) / 2.f ;
auto const viewport_half_height = viewport . height ( ) / 2.f ;
auto const viewport_center_x = viewport . x ( ) + viewport_half_width ;
auto const viewport_center_y = viewport . y ( ) + viewport_half_height ;
auto const depth_half_range = ( m_options . depth_max - m_options . depth_min ) / 2 ;
auto const depth_halfway = ( m_options . depth_min + m_options . depth_max ) / 2 ;
auto calculate_vertex_window_coordinates = [ & ] ( GPU : : Vertex & vertex ) {
auto const one_over_w = 1 / vertex . clip_coordinates . w ( ) ;
auto const ndc_coordinates = vertex . clip_coordinates . xyz ( ) * one_over_w ;
vertex . window_coordinates = {
viewport_center_x + ndc_coordinates . x ( ) * viewport_half_width ,
viewport_center_y + ndc_coordinates . y ( ) * viewport_half_height ,
depth_halfway + ndc_coordinates . z ( ) * depth_half_range ,
one_over_w ,
} ;
} ;
// Process points
if ( primitive_type = = GPU : : PrimitiveType : : Points ) {
m_clipper . clip_points_against_frustum ( vertices ) ;
for ( auto & vertex : vertices ) {
calculate_vertex_window_coordinates ( vertex ) ;
rasterize_point ( vertex ) ;
}
return ;
}
// Process lines, line loop and line strips
auto rasterize_line_segment = [ & ] ( GPU : : Vertex & from , GPU : : Vertex & to ) {
if ( ! m_clipper . clip_line_against_frustum ( from , to ) )
return ;
calculate_vertex_window_coordinates ( from ) ;
calculate_vertex_window_coordinates ( to ) ;
rasterize_line ( from , to ) ;
} ;
if ( primitive_type = = GPU : : PrimitiveType : : Lines ) {
if ( vertices . size ( ) < 2 )
return ;
for ( size_t i = 0 ; i < vertices . size ( ) - 1 ; i + = 2 )
rasterize_line_segment ( vertices [ i ] , vertices [ i + 1 ] ) ;
return ;
} else if ( primitive_type = = GPU : : PrimitiveType : : LineLoop ) {
if ( vertices . size ( ) < 2 )
return ;
for ( size_t i = 0 ; i < vertices . size ( ) ; + + i )
rasterize_line_segment ( vertices [ i ] , vertices [ ( i + 1 ) % vertices . size ( ) ] ) ;
return ;
} else if ( primitive_type = = GPU : : PrimitiveType : : LineStrip ) {
if ( vertices . size ( ) < 2 )
return ;
for ( size_t i = 0 ; i < vertices . size ( ) - 1 ; + + i )
rasterize_line_segment ( vertices [ i ] , vertices [ i + 1 ] ) ;
return ;
}
2021-12-16 23:26:15 +03:00
// Let's construct some triangles
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
m_triangle_list . clear_with_capacity ( ) ;
m_processed_triangles . clear_with_capacity ( ) ;
2022-03-15 03:11:58 +03:00
if ( primitive_type = = GPU : : PrimitiveType : : Triangles ) {
2021-12-17 00:43:39 +03:00
Triangle triangle ;
2022-01-11 03:37:12 +03:00
if ( vertices . size ( ) < 3 )
return ;
for ( size_t i = 0 ; i < vertices . size ( ) - 2 ; i + = 3 ) {
2021-12-16 23:26:15 +03:00
triangle . vertices [ 0 ] = vertices . at ( i ) ;
triangle . vertices [ 1 ] = vertices . at ( i + 1 ) ;
triangle . vertices [ 2 ] = vertices . at ( i + 2 ) ;
m_triangle_list . append ( triangle ) ;
}
2022-03-15 03:11:58 +03:00
} else if ( primitive_type = = GPU : : PrimitiveType : : Quads ) {
2021-12-16 23:26:15 +03:00
// We need to construct two triangles to form the quad
2021-12-17 00:43:39 +03:00
Triangle triangle ;
2022-01-11 03:37:12 +03:00
if ( vertices . size ( ) < 4 )
return ;
for ( size_t i = 0 ; i < vertices . size ( ) - 3 ; i + = 4 ) {
2021-12-16 23:26:15 +03:00
// Triangle 1
triangle . vertices [ 0 ] = vertices . at ( i ) ;
triangle . vertices [ 1 ] = vertices . at ( i + 1 ) ;
triangle . vertices [ 2 ] = vertices . at ( i + 2 ) ;
m_triangle_list . append ( triangle ) ;
// Triangle 2
triangle . vertices [ 0 ] = vertices . at ( i + 2 ) ;
triangle . vertices [ 1 ] = vertices . at ( i + 3 ) ;
triangle . vertices [ 2 ] = vertices . at ( i ) ;
m_triangle_list . append ( triangle ) ;
}
2022-03-15 03:11:58 +03:00
} else if ( primitive_type = = GPU : : PrimitiveType : : TriangleFan ) {
2021-12-17 00:43:39 +03:00
Triangle triangle ;
2021-12-16 23:26:15 +03:00
triangle . vertices [ 0 ] = vertices . at ( 0 ) ; // Root vertex is always the vertex defined first
2022-01-20 04:20:42 +03:00
// This is technically `n-2` triangles. We start at index 1
for ( size_t i = 1 ; i < vertices . size ( ) - 1 ; i + + ) {
2021-12-16 23:26:15 +03:00
triangle . vertices [ 1 ] = vertices . at ( i ) ;
triangle . vertices [ 2 ] = vertices . at ( i + 1 ) ;
m_triangle_list . append ( triangle ) ;
}
2022-03-15 03:11:58 +03:00
} else if ( primitive_type = = GPU : : PrimitiveType : : TriangleStrip ) {
2021-12-17 00:43:39 +03:00
Triangle triangle ;
2022-01-12 15:10:00 +03:00
if ( vertices . size ( ) < 3 )
return ;
2021-12-16 23:26:15 +03:00
for ( size_t i = 0 ; i < vertices . size ( ) - 2 ; i + + ) {
2021-12-24 16:48:00 +03:00
if ( i % 2 = = 0 ) {
triangle . vertices [ 0 ] = vertices . at ( i ) ;
triangle . vertices [ 1 ] = vertices . at ( i + 1 ) ;
triangle . vertices [ 2 ] = vertices . at ( i + 2 ) ;
} else {
triangle . vertices [ 0 ] = vertices . at ( i + 1 ) ;
triangle . vertices [ 1 ] = vertices . at ( i ) ;
triangle . vertices [ 2 ] = vertices . at ( i + 2 ) ;
}
2021-12-16 23:26:15 +03:00
m_triangle_list . append ( triangle ) ;
}
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Clip triangles
2021-12-30 02:47:53 +03:00
for ( auto & triangle : m_triangle_list ) {
2022-01-13 05:03:35 +03:00
m_clipped_vertices . clear_with_capacity ( ) ;
m_clipped_vertices . append ( triangle . vertices [ 0 ] ) ;
m_clipped_vertices . append ( triangle . vertices [ 1 ] ) ;
m_clipped_vertices . append ( triangle . vertices [ 2 ] ) ;
m_clipper . clip_triangle_against_frustum ( m_clipped_vertices ) ;
if ( m_clipped_vertices . size ( ) < 3 )
continue ;
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
for ( auto & vertex : m_clipped_vertices )
calculate_vertex_window_coordinates ( vertex ) ;
2021-12-16 23:26:15 +03:00
2021-12-17 00:43:39 +03:00
Triangle tri ;
2021-12-16 23:26:15 +03:00
tri . vertices [ 0 ] = m_clipped_vertices [ 0 ] ;
for ( size_t i = 1 ; i < m_clipped_vertices . size ( ) - 1 ; i + + ) {
tri . vertices [ 1 ] = m_clipped_vertices [ i ] ;
tri . vertices [ 2 ] = m_clipped_vertices [ i + 1 ] ;
m_processed_triangles . append ( tri ) ;
}
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
for ( auto & triangle : m_processed_triangles )
2022-01-06 19:52:07 +03:00
rasterize_triangle ( triangle ) ;
2021-12-16 23:26:15 +03:00
}
2022-01-06 19:52:07 +03:00
ALWAYS_INLINE void Device : : shade_fragments ( PixelQuad & quad )
2021-04-24 02:57:01 +03:00
{
2022-01-06 19:52:07 +03:00
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 ] ;
2022-03-06 21:12:01 +03:00
auto texel = sampler . sample_2d ( quad . texture_coordinates [ i ] . xy ( ) ) ;
2022-01-06 19:52:07 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_sampler_calls , 1 ) ;
// FIXME: Implement more blend modes
switch ( sampler . config ( ) . fixed_function_texture_env_mode ) {
2022-03-27 15:55:31 +03:00
case GPU : : TextureEnvMode : : Modulate :
2022-01-06 19:52:07 +03:00
quad . out_color = quad . out_color * texel ;
break ;
2022-03-27 15:55:31 +03:00
case GPU : : TextureEnvMode : : Replace :
2022-01-06 19:52:07 +03:00
quad . out_color = texel ;
break ;
2022-03-27 15:55:31 +03:00
case GPU : : TextureEnvMode : : Decal : {
2022-03-06 02:21:58 +03:00
auto dst_alpha = texel . w ( ) ;
quad . out_color . set_x ( mix ( quad . out_color . x ( ) , texel . x ( ) , dst_alpha ) ) ;
quad . out_color . set_y ( mix ( quad . out_color . y ( ) , texel . y ( ) , dst_alpha ) ) ;
quad . out_color . set_z ( mix ( quad . out_color . z ( ) , texel . z ( ) , dst_alpha ) ) ;
2022-01-06 19:52:07 +03:00
break ;
2021-05-30 14:39:31 +03:00
}
2022-03-09 13:27:50 +03:00
case GPU : : TextureEnvMode : : Add :
quad . out_color . set_x ( quad . out_color . x ( ) + texel . x ( ) ) ;
quad . out_color . set_y ( quad . out_color . y ( ) + texel . y ( ) ) ;
quad . out_color . set_z ( quad . out_color . z ( ) + texel . z ( ) ) ;
quad . out_color . set_w ( quad . out_color . w ( ) * texel . w ( ) ) ; // FIXME: If texture format is `GL_INTENSITY` alpha components must be added (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml)
break ;
2022-01-06 19:52:07 +03:00
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2021-05-30 14:39:31 +03:00
2022-01-06 19:52:07 +03:00
// 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 ) {
2022-04-29 16:19:01 +03:00
f32x4 factor ;
2022-01-06 19:52:07 +03:00
switch ( m_options . fog_mode ) {
2022-03-15 03:11:58 +03:00
case GPU : : FogMode : : Linear :
2022-01-06 19:52:07 +03:00
factor = ( m_options . fog_end - quad . fog_depth ) / ( m_options . fog_end - m_options . fog_start ) ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : FogMode : : Exp : {
2022-01-06 19:52:07 +03:00
auto argument = - m_options . fog_density * quad . fog_depth ;
factor = exp ( argument ) ;
} break ;
2022-03-15 03:11:58 +03:00
case GPU : : FogMode : : Exp2 : {
2022-01-06 19:52:07 +03:00
auto argument = m_options . fog_density * quad . fog_depth ;
argument * = - argument ;
factor = exp ( argument ) ;
} break ;
default :
VERIFY_NOT_REACHED ( ) ;
2021-08-25 11:10:44 +03:00
}
2022-01-06 19:52:07 +03:00
// 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 ) ) ;
}
LibGL+LibGPU+LibSoftGPU: Implement point and line drawing
Implement (anti)aliased point drawing and anti-aliased line drawing.
Supported through LibGL's `GL_POINTS`, `GL_LINES`, `GL_LINE_LOOP` and
`GL_LINE_STRIP`.
In order to support this, `LibSoftGPU`s rasterization logic was
reworked. Now, any primitive can be drawn by invoking `rasterize()`
which takes care of the quad loop and fragment testing logic. Three
callbacks need to be passed:
* `set_coverage_mask`: the primitive needs to provide initial coverage
mask information so fragments can be discarded early.
* `set_quad_depth`: fragments survived stencil testing, so depth values
need to be set so depth testing can take place.
* `set_quad_attributes`: fragments survived depth testing, so fragment
shading is going to take place. All attributes like color, tex coords
and fog depth need to be set so alpha testing and eventually,
fragment rasterization can take place.
As of this commit, there are four instantiations of this function:
* Triangle rasterization
* Points - aliased
* Points - anti-aliased
* Lines - anti-aliased
In order to standardize vertex processing for all primitive types,
things like vertex transformation, lighting and tex coord generation
are now taking place before clipping.
2022-05-08 03:13:14 +03:00
// Multiply coverage with the fragment's alpha to obtain the final alpha value
quad . out_color . set_w ( quad . out_color . w ( ) * quad . coverage ) ;
2021-04-24 02:57:01 +03:00
}
2022-01-06 23:28:16 +03:00
ALWAYS_INLINE bool Device : : test_alpha ( PixelQuad & quad )
{
auto const alpha = quad . out_color . w ( ) ;
auto const ref_value = expand4 ( m_options . alpha_test_ref_value ) ;
switch ( m_options . alpha_test_func ) {
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : Less :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha < ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : Equal :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha = = ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : LessOrEqual :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha < = ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : Greater :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha > ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : NotEqual :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha ! = ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : GreaterOrEqual :
2022-01-06 23:28:16 +03:00
quad . mask & = alpha > = ref_value ;
break ;
2022-03-15 03:11:58 +03:00
case GPU : : AlphaTestFunction : : Never :
case GPU : : AlphaTestFunction : : Always :
2022-01-06 23:28:16 +03:00
default :
VERIFY_NOT_REACHED ( ) ;
}
return any ( quad . mask ) ;
}
2022-01-24 00:15:28 +03:00
void Device : : resize ( Gfx : : IntSize const & size )
2021-04-24 02:57:01 +03:00
{
2022-03-27 16:20:51 +03:00
auto frame_buffer_or_error = FrameBuffer < GPU : : ColorType , GPU : : DepthType , GPU : : StencilType > : : try_create ( size ) ;
2022-01-24 00:15:28 +03:00
m_frame_buffer = MUST ( frame_buffer_or_error ) ;
2021-04-24 02:57:01 +03:00
}
2022-01-24 00:15:28 +03:00
void Device : : clear_color ( FloatVector4 const & color )
2021-04-24 02:57:01 +03:00
{
2022-01-24 00:15:28 +03:00
auto const fill_color = to_bgra32 ( color ) ;
2022-01-31 19:45:14 +03:00
auto clear_rect = m_frame_buffer - > rect ( ) ;
2022-01-24 00:15:28 +03:00
if ( m_options . scissor_enabled )
2022-01-31 19:45:14 +03:00
clear_rect . intersect ( m_options . scissor_box ) ;
2021-04-24 02:57:01 +03:00
2022-01-31 19:45:14 +03:00
m_frame_buffer - > color_buffer ( ) - > fill ( fill_color , clear_rect ) ;
2021-04-24 02:57:01 +03:00
}
2022-03-27 16:20:51 +03:00
void Device : : clear_depth ( GPU : : DepthType depth )
2021-04-24 02:57:01 +03:00
{
2022-01-24 00:15:28 +03:00
auto clear_rect = m_frame_buffer - > rect ( ) ;
if ( m_options . scissor_enabled )
2022-01-31 19:45:14 +03:00
clear_rect . intersect ( m_options . scissor_box ) ;
2021-11-27 21:00:16 +03:00
2022-01-24 00:15:28 +03:00
m_frame_buffer - > depth_buffer ( ) - > fill ( depth , clear_rect ) ;
2021-04-24 02:57:01 +03:00
}
2022-03-27 16:20:51 +03:00
void Device : : clear_stencil ( GPU : : StencilType value )
2022-01-17 00:48:46 +03:00
{
2022-01-24 00:15:28 +03:00
auto clear_rect = m_frame_buffer - > rect ( ) ;
2022-01-17 00:48:46 +03:00
if ( m_options . scissor_enabled )
2022-01-31 19:45:14 +03:00
clear_rect . intersect ( m_options . scissor_box ) ;
2022-01-17 00:48:46 +03:00
2022-01-24 00:15:28 +03:00
m_frame_buffer - > stencil_buffer ( ) - > fill ( value , clear_rect ) ;
2022-01-17 00:48:46 +03:00
}
2022-01-12 19:07:53 +03:00
void Device : : blit_to_color_buffer_at_raster_position ( Gfx : : Bitmap const & source )
2021-12-01 19:19:22 +03:00
{
2022-01-12 19:07:53 +03:00
if ( ! m_raster_position . valid )
return ;
2021-12-30 03:04:55 +03:00
INCREASE_STATISTICS_COUNTER ( g_num_pixels , source . width ( ) * source . height ( ) ) ;
INCREASE_STATISTICS_COUNTER ( g_num_pixels_shaded , source . width ( ) * source . height ( ) ) ;
2022-01-31 19:45:14 +03:00
auto const blit_rect = get_rasterization_rect_of_size ( { source . width ( ) , source . height ( ) } ) ;
2022-01-24 00:15:28 +03:00
m_frame_buffer - > color_buffer ( ) - > blit_from_bitmap ( source , blit_rect ) ;
2021-12-01 19:19:22 +03:00
}
2022-03-27 16:20:51 +03:00
void Device : : blit_to_depth_buffer_at_raster_position ( Vector < GPU : : DepthType > const & depth_values , int width , int height )
2022-01-12 19:08:59 +03:00
{
if ( ! m_raster_position . valid )
return ;
2022-01-31 19:45:14 +03:00
auto const raster_rect = get_rasterization_rect_of_size ( { width , height } ) ;
2022-01-12 19:08:59 +03:00
auto const y1 = raster_rect . y ( ) ;
auto const y2 = y1 + height ;
auto const x1 = raster_rect . x ( ) ;
2022-01-24 00:15:28 +03:00
auto const x2 = x1 + width ;
2022-01-12 19:08:59 +03:00
auto index = 0 ;
2022-01-31 19:45:14 +03:00
for ( auto y = y1 ; y < y2 ; + + y ) {
2022-01-24 00:15:28 +03:00
auto depth_line = m_frame_buffer - > depth_buffer ( ) - > scanline ( y ) ;
for ( auto x = x1 ; x < x2 ; + + x )
depth_line [ x ] = depth_values [ index + + ] ;
2022-01-12 19:08:59 +03:00
}
}
2022-01-24 00:15:28 +03:00
void Device : : blit_color_buffer_to ( Gfx : : Bitmap & target )
2021-04-24 02:57:01 +03:00
{
2022-01-31 19:45:14 +03:00
m_frame_buffer - > color_buffer ( ) - > blit_flipped_to_bitmap ( target , m_frame_buffer - > rect ( ) ) ;
2021-12-30 03:04:55 +03:00
if constexpr ( ENABLE_STATISTICS_OVERLAY )
draw_statistics_overlay ( target ) ;
}
void Device : : draw_statistics_overlay ( Gfx : : Bitmap & target )
{
static Core : : ElapsedTimer timer ;
static String debug_string ;
static int frame_counter ;
frame_counter + + ;
int milliseconds = 0 ;
if ( timer . is_valid ( ) )
milliseconds = timer . elapsed ( ) ;
else
timer . start ( ) ;
Gfx : : Painter painter { target } ;
2022-01-09 00:30:56 +03:00
if ( milliseconds > MILLISECONDS_PER_STATISTICS_PERIOD ) {
2021-12-30 03:04:55 +03:00
2022-01-24 00:15:28 +03:00
int num_rendertarget_pixels = m_frame_buffer - > rect ( ) . size ( ) . area ( ) ;
2021-12-30 03:04:55 +03:00
StringBuilder builder ;
builder . append ( String : : formatted ( " Timings : {:.1}ms {:.1}FPS \n " ,
static_cast < double > ( milliseconds ) / frame_counter ,
( milliseconds > 0 ) ? 1000.0 * frame_counter / milliseconds : 9999.0 ) ) ;
builder . append ( String : : formatted ( " Triangles : {} \n " , g_num_rasterized_triangles ) ) ;
2022-01-02 01:29:51 +03:00
builder . append ( String : : formatted ( " SIMD usage : {}% \n " , g_num_quads > 0 ? g_num_pixels_shaded * 25 / g_num_quads : 0 ) ) ;
2022-01-17 01:07:01 +03:00
builder . append ( String : : formatted ( " Pixels : {}, Stencil: {}%, Shaded: {}%, Blended: {}%, Overdraw: {}% \n " ,
2021-12-30 03:04:55 +03:00
g_num_pixels ,
2022-01-17 01:07:01 +03:00
g_num_pixels > 0 ? g_num_stencil_writes * 100 / g_num_pixels : 0 ,
2022-01-08 04:57:35 +03:00
g_num_pixels > 0 ? g_num_pixels_shaded * 100 / g_num_pixels : 0 ,
g_num_pixels_shaded > 0 ? g_num_pixels_blended * 100 / g_num_pixels_shaded : 0 ,
num_rendertarget_pixels > 0 ? g_num_pixels_shaded * 100 / num_rendertarget_pixels - 100 : 0 ) ) ;
2021-12-30 03:04:55 +03:00
builder . append ( String : : formatted ( " Sampler calls: {} \n " , g_num_sampler_calls ) ) ;
debug_string = builder . to_string ( ) ;
frame_counter = 0 ;
timer . start ( ) ;
}
g_num_rasterized_triangles = 0 ;
g_num_pixels = 0 ;
g_num_pixels_shaded = 0 ;
g_num_pixels_blended = 0 ;
g_num_sampler_calls = 0 ;
2022-01-17 01:07:01 +03:00
g_num_stencil_writes = 0 ;
2022-01-02 01:29:51 +03:00
g_num_quads = 0 ;
2021-12-30 03:04:55 +03:00
auto & font = Gfx : : FontDatabase : : default_fixed_width_font ( ) ;
for ( int y = - 1 ; y < 2 ; y + + )
for ( int x = - 1 ; x < 2 ; x + + )
if ( x ! = 0 & & y ! = 0 )
painter . draw_text ( target . rect ( ) . translated ( x + 2 , y + 2 ) , debug_string , font , Gfx : : TextAlignment : : TopLeft , Gfx : : Color : : Black ) ;
painter . draw_text ( target . rect ( ) . translated ( 2 , 2 ) , debug_string , font , Gfx : : TextAlignment : : TopLeft , Gfx : : Color : : White ) ;
2021-04-24 02:57:01 +03:00
}
2022-03-27 16:50:06 +03:00
void Device : : set_options ( GPU : : RasterizerOptions const & options )
2021-04-24 02:57:01 +03:00
{
m_options = options ;
2022-01-06 20:47:35 +03:00
if ( m_options . enable_blending )
setup_blend_factors ( ) ;
2021-04-24 02:57:01 +03:00
}
2022-03-27 16:32:40 +03:00
void Device : : set_light_model_params ( GPU : : LightModelParameters const & lighting_model )
2022-01-08 16:40:39 +03:00
{
m_lighting_model = lighting_model ;
}
2022-03-27 16:20:51 +03:00
GPU : : ColorType Device : : get_color_buffer_pixel ( int x , int y )
2021-05-24 18:52:24 +03:00
{
// FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks
2022-02-06 04:31:11 +03:00
if ( ! m_frame_buffer - > rect ( ) . contains ( x , y ) )
2021-05-24 18:52:24 +03:00
return 0 ;
2022-01-24 00:15:28 +03:00
return m_frame_buffer - > color_buffer ( ) - > scanline ( y ) [ x ] ;
2021-05-24 18:52:24 +03:00
}
2022-03-27 16:20:51 +03:00
GPU : : DepthType Device : : get_depthbuffer_value ( int x , int y )
2021-05-24 18:52:24 +03:00
{
// FIXME: Reading individual pixels is very slow, rewrite this to transfer whole blocks
2022-02-06 04:31:11 +03:00
if ( ! m_frame_buffer - > rect ( ) . contains ( x , y ) )
2021-05-24 18:52:24 +03:00
return 1.0f ;
2022-01-24 00:15:28 +03:00
return m_frame_buffer - > depth_buffer ( ) - > scanline ( y ) [ x ] ;
2021-05-24 18:52:24 +03:00
}
2022-03-27 17:26:50 +03:00
NonnullRefPtr < GPU : : Image > Device : : create_image ( GPU : : ImageFormat format , unsigned width , unsigned height , unsigned depth , unsigned levels , unsigned layers )
2021-12-19 02:02:32 +03:00
{
2022-03-15 15:19:03 +03:00
VERIFY ( format = = GPU : : ImageFormat : : BGRA8888 ) ;
2021-12-19 02:02:32 +03:00
VERIFY ( width > 0 ) ;
VERIFY ( height > 0 ) ;
VERIFY ( depth > 0 ) ;
VERIFY ( levels > 0 ) ;
VERIFY ( layers > 0 ) ;
2022-03-27 17:26:50 +03:00
return adopt_ref ( * new Image ( this , width , height , depth , levels , layers ) ) ;
2021-12-19 02:02:32 +03:00
}
2022-03-27 15:55:31 +03:00
void Device : : set_sampler_config ( unsigned sampler , GPU : : SamplerConfig const & config )
2021-12-20 18:14:40 +03:00
{
2022-03-27 17:26:50 +03:00
VERIFY ( config . bound_image . is_null ( ) | | config . bound_image - > ownership_token ( ) = = this ) ;
2021-12-20 18:14:40 +03:00
m_samplers [ sampler ] . set_config ( config ) ;
}
2022-03-27 16:02:20 +03:00
void Device : : set_light_state ( unsigned int light_id , GPU : : Light const & light )
2022-01-07 17:41:46 +03:00
{
m_lights . at ( light_id ) = light ;
}
2022-03-27 16:07:53 +03:00
void Device : : set_material_state ( GPU : : Face face , GPU : : Material const & material )
2022-01-08 09:46:58 +03:00
{
2022-01-16 04:09:27 +03:00
m_materials [ face ] = material ;
2022-01-08 09:46:58 +03:00
}
2022-03-27 16:20:51 +03:00
void Device : : set_stencil_configuration ( GPU : : Face face , GPU : : StencilConfiguration const & stencil_configuration )
2022-01-17 00:48:46 +03:00
{
m_stencil_configuration [ face ] = stencil_configuration ;
}
2022-03-27 16:24:10 +03:00
void Device : : set_raster_position ( GPU : : RasterPosition const & raster_position )
2022-01-12 19:07:53 +03:00
{
m_raster_position = raster_position ;
}
void Device : : set_raster_position ( FloatVector4 const & position , FloatMatrix4x4 const & model_view_transform , FloatMatrix4x4 const & projection_transform )
{
auto const eye_coordinates = model_view_transform * position ;
auto const clip_coordinates = projection_transform * eye_coordinates ;
// FIXME: implement clipping
m_raster_position . valid = true ;
auto ndc_coordinates = clip_coordinates / clip_coordinates . w ( ) ;
ndc_coordinates . set_w ( clip_coordinates . w ( ) ) ;
auto const viewport = m_options . viewport ;
auto const viewport_half_width = viewport . width ( ) / 2.0f ;
auto const viewport_half_height = viewport . height ( ) / 2.0f ;
auto const viewport_center_x = viewport . x ( ) + viewport_half_width ;
auto const viewport_center_y = viewport . y ( ) + viewport_half_height ;
auto const depth_half_range = ( m_options . depth_max - m_options . depth_min ) / 2 ;
auto const depth_halfway = ( m_options . depth_min + m_options . depth_max ) / 2 ;
// FIXME: implement other raster position properties such as color and texcoords
m_raster_position . window_coordinates = {
viewport_center_x + ndc_coordinates . x ( ) * viewport_half_width ,
viewport_center_y + ndc_coordinates . y ( ) * viewport_half_height ,
depth_halfway + ndc_coordinates . z ( ) * depth_half_range ,
ndc_coordinates . w ( ) ,
} ;
m_raster_position . eye_coordinate_distance = eye_coordinates . length ( ) ;
}
2022-04-15 20:49:24 +03:00
Gfx : : IntRect Device : : get_rasterization_rect_of_size ( Gfx : : IntSize size ) const
2022-01-12 19:07:53 +03:00
{
2022-02-03 18:45:08 +03:00
// Round the X and Y floating point coordinates to the nearest integer; OpenGL 1.5 spec:
// "Any fragments whose centers lie inside of this rectangle (or on its bottom or left
// boundaries) are produced in correspondence with this particular group of elements."
2022-01-31 19:45:14 +03:00
return {
2022-04-15 20:49:24 +03:00
round_to < int > ( m_raster_position . window_coordinates . x ( ) ) ,
round_to < int > ( m_raster_position . window_coordinates . y ( ) ) ,
2022-01-12 19:07:53 +03:00
size . width ( ) ,
size . height ( ) ,
} ;
}
2021-04-24 02:57:01 +03:00
}
2022-03-27 19:54:31 +03:00
extern " C " {
GPU : : Device * serenity_gpu_create_device ( Gfx : : IntSize const & size )
{
return make < SoftGPU : : Device > ( size ) . leak_ptr ( ) ;
}
}