2021-01-06 08:58:01 -03:00
/*
* Copyright ( c ) 2021 , Jesse Buhagiar < jooster669 @ gmail . com >
2021-05-29 07:38:28 -03:00
* Copyright ( c ) 2021 , Stephan Unverwerth < s . unverwerth @ serenityos . org >
2021-01-06 08:58:01 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/Assertions.h>
# include <AK/Debug.h>
# include <AK/Format.h>
# include <AK/QuickSort.h>
2021-05-09 00:55:15 -03:00
# include <AK/TemporaryChange.h>
# include <AK/Variant.h>
2021-01-06 08:58:01 -03:00
# include <AK/Vector.h>
2021-12-16 16:32:38 -03:00
# include <LibGL/SoftwareGLContext.h>
2021-04-23 20:57:01 -03:00
# include <LibGfx/Bitmap.h>
# include <LibGfx/Painter.h>
2021-01-06 08:58:01 -03:00
# include <LibGfx/Vector4.h>
2021-12-18 10:07:47 -03:00
# include <LibSoftGPU/Device.h>
2022-01-15 22:09:27 -03:00
# include <LibSoftGPU/Enums.h>
2021-01-06 08:58:01 -03:00
using AK : : dbgln ;
namespace GL {
2021-12-20 20:52:14 -03:00
static constexpr size_t MODELVIEW_MATRIX_STACK_LIMIT = 64 ;
static constexpr size_t PROJECTION_MATRIX_STACK_LIMIT = 8 ;
static constexpr size_t TEXTURE_MATRIX_STACK_LIMIT = 8 ;
2021-04-23 13:21:13 -03:00
2021-05-09 00:55:15 -03:00
# define APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(name, ...) \
if ( should_append_to_listing ( ) ) { \
append_to_listing < & SoftwareGLContext : : name > ( __VA_ARGS__ ) ; \
if ( ! should_execute_after_appending_to_listing ( ) ) \
return ; \
}
2021-12-01 09:27:24 -03:00
# define APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(name, arg) \
if ( should_append_to_listing ( ) ) { \
auto ptr = store_in_listing ( arg ) ; \
append_to_listing < & SoftwareGLContext : : name > ( * ptr ) ; \
if ( ! should_execute_after_appending_to_listing ( ) ) \
return ; \
}
2021-05-29 12:40:33 -03:00
# define RETURN_WITH_ERROR_IF(condition, error) \
if ( condition ) { \
2021-05-29 13:18:54 -03:00
if ( m_error = = GL_NO_ERROR ) \
m_error = error ; \
2021-05-29 12:40:33 -03:00
return ; \
}
# define RETURN_VALUE_WITH_ERROR_IF(condition, error, return_value) \
if ( condition ) { \
2021-05-29 13:18:54 -03:00
if ( m_error = = GL_NO_ERROR ) \
m_error = error ; \
2021-05-29 12:40:33 -03:00
return return_value ; \
}
2021-04-23 20:57:01 -03:00
SoftwareGLContext : : SoftwareGLContext ( Gfx : : Bitmap & frontbuffer )
2022-01-08 10:43:03 -03:00
: m_viewport ( frontbuffer . rect ( ) )
, m_frontbuffer ( frontbuffer )
2021-04-23 20:57:01 -03:00
, m_rasterizer ( frontbuffer . size ( ) )
2021-12-22 22:38:20 -03:00
, m_device_info ( m_rasterizer . info ( ) )
2021-04-23 20:57:01 -03:00
{
2021-12-22 22:38:20 -03:00
m_texture_units . resize ( m_device_info . num_texture_units ) ;
m_active_texture_unit = & m_texture_units [ 0 ] ;
2022-01-07 11:41:46 -03:00
// Query the number lights from the device and set set up their state
// locally in the GL
m_light_states . resize ( m_device_info . num_lights ) ;
// Set-up light0's state, as it has a different default state
// to the other lights, as per the OpenGL 1.5 spec
auto & light0 = m_light_states . at ( 0 ) ;
light0 . diffuse_intensity = { 1.0f , 1.0f , 1.0f , 1.0f } ;
light0 . specular_intensity = { 1.0f , 1.0f , 1.0f , 1.0f } ;
m_light_state_is_dirty = true ;
2021-04-23 20:57:01 -03:00
}
2021-12-01 18:06:27 -03:00
Optional < ContextParameter > SoftwareGLContext : : get_context_parameter ( GLenum name )
{
switch ( name ) {
case GL_ALPHA_BITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = sizeof ( float ) * 8 } } ;
case GL_ALPHA_TEST :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_alpha_test_enabled } } ;
2021-12-01 18:06:27 -03:00
case GL_BLEND :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_blend_enabled } } ;
2021-12-01 18:06:27 -03:00
case GL_BLEND_DST_ALPHA :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_blend_destination_factor ) } } ;
case GL_BLEND_SRC_ALPHA :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_blend_source_factor ) } } ;
case GL_BLUE_BITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = sizeof ( float ) * 8 } } ;
2022-01-12 23:03:35 -03:00
case GL_COLOR_MATERIAL :
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_color_material_enabled } } ;
case GL_COLOR_MATERIAL_FACE :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_color_material_face ) } } ;
case GL_COLOR_MATERIAL_MODE :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_color_material_mode ) } } ;
2021-12-01 18:06:27 -03:00
case GL_CULL_FACE :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_cull_faces } } ;
2021-12-01 18:06:27 -03:00
case GL_DEPTH_BITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = sizeof ( float ) * 8 } } ;
case GL_DEPTH_TEST :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_depth_test_enabled } } ;
2021-12-01 18:27:48 -03:00
case GL_DITHER :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_dither_enabled } } ;
2021-12-01 18:06:27 -03:00
case GL_DOUBLEBUFFER :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = true } } ;
2022-01-08 13:10:56 -03:00
case GL_FOG : {
auto fog_enabled = m_rasterizer . options ( ) . fog_enabled ;
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = fog_enabled } } ;
}
2021-12-01 18:06:27 -03:00
case GL_GREEN_BITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = sizeof ( float ) * 8 } } ;
2021-12-01 18:18:40 -03:00
case GL_LIGHTING :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_lighting_enabled } } ;
2022-01-07 08:32:32 -03:00
case GL_MAX_LIGHTS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_device_info . num_lights ) } } ;
2021-12-20 20:56:13 -03:00
case GL_MAX_MODELVIEW_STACK_DEPTH :
return ContextParameter { . type = GL_INT , . value = { . integer_value = MODELVIEW_MATRIX_STACK_LIMIT } } ;
case GL_MAX_PROJECTION_STACK_DEPTH :
return ContextParameter { . type = GL_INT , . value = { . integer_value = PROJECTION_MATRIX_STACK_LIMIT } } ;
2021-12-01 18:06:27 -03:00
case GL_MAX_TEXTURE_SIZE :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 4096 } } ;
2021-12-20 20:56:13 -03:00
case GL_MAX_TEXTURE_STACK_DEPTH :
return ContextParameter { . type = GL_INT , . value = { . integer_value = TEXTURE_MATRIX_STACK_LIMIT } } ;
2021-12-01 18:06:27 -03:00
case GL_MAX_TEXTURE_UNITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = static_cast < GLint > ( m_texture_units . size ( ) ) } } ;
2022-01-08 13:10:56 -03:00
case GL_NORMALIZE :
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_normalize } } ;
2021-12-01 18:06:27 -03:00
case GL_PACK_ALIGNMENT :
return ContextParameter { . type = GL_INT , . value = { . integer_value = m_pack_alignment } } ;
2021-12-24 10:42:33 -03:00
case GL_PACK_IMAGE_HEIGHT :
return ContextParameter { . type = GL_BOOL , . value = { . integer_value = 0 } } ;
case GL_PACK_LSB_FIRST :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = false } } ;
case GL_PACK_ROW_LENGTH :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 0 } } ;
case GL_PACK_SKIP_PIXELS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 0 } } ;
case GL_PACK_SKIP_ROWS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 0 } } ;
case GL_PACK_SWAP_BYTES :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = false } } ;
2021-12-01 18:06:27 -03:00
case GL_RED_BITS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = sizeof ( float ) * 8 } } ;
case GL_SCISSOR_BOX : {
auto scissor_box = m_rasterizer . options ( ) . scissor_box ;
return ContextParameter {
. type = GL_INT ,
. count = 4 ,
. value = {
. integer_list = {
scissor_box . x ( ) ,
scissor_box . y ( ) ,
scissor_box . width ( ) ,
scissor_box . height ( ) ,
} }
} ;
} break ;
2022-01-08 13:10:56 -03:00
case GL_SCISSOR_TEST : {
auto scissor_enabled = m_rasterizer . options ( ) . scissor_enabled ;
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = scissor_enabled } } ;
}
2021-12-01 18:06:27 -03:00
case GL_STENCIL_BITS :
2022-01-16 18:48:46 -03:00
return ContextParameter { . type = GL_INT , . value = { . integer_value = m_device_info . stencil_bits } } ;
case GL_STENCIL_CLEAR_VALUE :
return ContextParameter { . type = GL_INT , . value = { . integer_value = m_clear_stencil } } ;
2021-12-01 18:06:27 -03:00
case GL_STENCIL_TEST :
2022-01-08 13:10:56 -03:00
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = m_stencil_test_enabled } } ;
2021-12-01 18:06:27 -03:00
case GL_TEXTURE_1D :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = m_active_texture_unit - > texture_1d_enabled ( ) } } ;
case GL_TEXTURE_2D :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = m_active_texture_unit - > texture_2d_enabled ( ) } } ;
case GL_TEXTURE_3D :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = m_active_texture_unit - > texture_3d_enabled ( ) } } ;
case GL_TEXTURE_CUBE_MAP :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = m_active_texture_unit - > texture_cube_map_enabled ( ) } } ;
2022-01-08 13:10:56 -03:00
case GL_TEXTURE_GEN_Q :
case GL_TEXTURE_GEN_R :
case GL_TEXTURE_GEN_S :
case GL_TEXTURE_GEN_T : {
auto generation_enabled = texture_coordinate_generation ( name ) . enabled ;
return ContextParameter { . type = GL_BOOL , . is_capability = true , . value = { . boolean_value = generation_enabled } } ;
}
2021-12-01 18:06:27 -03:00
case GL_UNPACK_ALIGNMENT :
return ContextParameter { . type = GL_INT , . value = { . integer_value = m_unpack_alignment } } ;
2021-12-24 10:42:33 -03:00
case GL_UNPACK_IMAGE_HEIGHT :
return ContextParameter { . type = GL_BOOL , . value = { . integer_value = 0 } } ;
case GL_UNPACK_LSB_FIRST :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = false } } ;
2021-12-01 18:06:27 -03:00
case GL_UNPACK_ROW_LENGTH :
return ContextParameter { . type = GL_INT , . value = { . integer_value = m_unpack_row_length } } ;
2021-12-24 10:42:33 -03:00
case GL_UNPACK_SKIP_PIXELS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 0 } } ;
case GL_UNPACK_SKIP_ROWS :
return ContextParameter { . type = GL_INT , . value = { . integer_value = 0 } } ;
case GL_UNPACK_SWAP_BYTES :
return ContextParameter { . type = GL_BOOL , . value = { . boolean_value = false } } ;
2022-01-08 10:43:03 -03:00
case GL_VIEWPORT :
return ContextParameter {
. type = GL_INT ,
. count = 4 ,
. value = {
. integer_list = {
m_viewport . x ( ) ,
m_viewport . y ( ) ,
m_viewport . width ( ) ,
m_viewport . height ( ) ,
} }
} ;
2021-12-01 18:06:27 -03:00
default :
dbgln_if ( GL_DEBUG , " get_context_parameter({:#x}): unknown context parameter " , name ) ;
return { } ;
}
}
2021-01-06 08:58:01 -03:00
void SoftwareGLContext : : gl_begin ( GLenum mode )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_begin , mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( mode < GL_TRIANGLES | | mode > GL_POLYGON , GL_INVALID_ENUM ) ;
2021-04-23 10:48:28 -03:00
2021-01-06 08:58:01 -03:00
m_current_draw_mode = mode ;
2021-04-23 12:45:09 -03:00
m_in_draw_state = true ; // Certain commands will now generate an error
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_clear ( GLbitfield mask )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_clear , mask ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-24 10:46:45 -03:00
RETURN_WITH_ERROR_IF ( mask & ~ ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) , GL_INVALID_ENUM ) ;
2021-05-06 18:17:35 -03:00
if ( mask & GL_COLOR_BUFFER_BIT )
m_rasterizer . clear_color ( m_clear_color ) ;
if ( mask & GL_DEPTH_BUFFER_BIT )
m_rasterizer . clear_depth ( static_cast < float > ( m_clear_depth ) ) ;
2021-12-01 12:20:22 -03:00
2021-12-24 10:46:45 -03:00
if ( mask & GL_STENCIL_BUFFER_BIT )
2022-01-16 18:48:46 -03:00
m_rasterizer . clear_stencil ( m_clear_stencil ) ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_clear_color ( GLclampf red , GLclampf green , GLclampf blue , GLclampf alpha )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_clear_color , red , green , blue , alpha ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
m_clear_color = { red , green , blue , alpha } ;
}
2021-05-06 18:17:35 -03:00
void SoftwareGLContext : : gl_clear_depth ( GLdouble depth )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_clear_depth , depth ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-06 18:17:35 -03:00
m_clear_depth = depth ;
}
2021-12-01 12:20:22 -03:00
void SoftwareGLContext : : gl_clear_stencil ( GLint s )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_clear_stencil , s ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-16 18:48:46 -03:00
m_clear_stencil = static_cast < u8 > ( s & ( ( 1 < < m_device_info . stencil_bits ) - 1 ) ) ;
2021-12-01 12:20:22 -03:00
}
2021-01-06 08:58:01 -03:00
void SoftwareGLContext : : gl_color ( GLdouble r , GLdouble g , GLdouble b , GLdouble a )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_color , r , g , b , a ) ;
2022-01-12 22:55:54 -03:00
m_current_vertex_color = {
static_cast < float > ( r ) ,
static_cast < float > ( g ) ,
static_cast < float > ( b ) ,
static_cast < float > ( a ) ,
} ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_end ( )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_end ) ;
2021-04-23 12:45:09 -03:00
// Make sure we had a `glBegin` before this call...
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( ! m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-12-01 21:21:40 -03:00
m_in_draw_state = false ;
2021-12-16 17:26:15 -03:00
// FIXME: Add support for the remaining primitive types.
if ( m_current_draw_mode ! = GL_TRIANGLES
& & m_current_draw_mode ! = GL_TRIANGLE_FAN
& & m_current_draw_mode ! = GL_TRIANGLE_STRIP
& & m_current_draw_mode ! = GL_QUADS
2021-12-26 21:23:44 -03:00
& & m_current_draw_mode ! = GL_QUAD_STRIP
2021-12-16 17:26:15 -03:00
& & m_current_draw_mode ! = GL_POLYGON ) {
m_vertex_list . clear_with_capacity ( ) ;
2021-12-01 21:21:40 -03:00
dbgln_if ( GL_DEBUG , " gl_end: draw mode {:#x} unsupported " , m_current_draw_mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
2021-01-06 08:58:01 -03:00
}
2021-12-22 18:14:18 -03:00
Vector < size_t , 32 > enabled_texture_units ;
for ( size_t i = 0 ; i < m_texture_units . size ( ) ; + + i ) {
if ( m_texture_units [ i ] . texture_2d_enabled ( ) )
enabled_texture_units . append ( i ) ;
2021-12-12 17:43:21 -03:00
}
2021-12-22 18:02:57 -03:00
sync_device_config ( ) ;
2021-12-22 19:58:36 -03:00
SoftGPU : : PrimitiveType primitive_type ;
switch ( m_current_draw_mode ) {
case GL_TRIANGLES :
primitive_type = SoftGPU : : PrimitiveType : : Triangles ;
break ;
case GL_TRIANGLE_STRIP :
2021-12-26 21:23:44 -03:00
case GL_QUAD_STRIP :
2021-12-22 19:58:36 -03:00
primitive_type = SoftGPU : : PrimitiveType : : TriangleStrip ;
break ;
case GL_TRIANGLE_FAN :
case GL_POLYGON :
primitive_type = SoftGPU : : PrimitiveType : : TriangleFan ;
break ;
case GL_QUADS :
primitive_type = SoftGPU : : PrimitiveType : : Quads ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2021-12-29 20:27:17 -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 & mv_elements = m_model_view_matrix . elements ( ) ;
2022-01-10 20:50:17 -03:00
auto const model_view_transposed = FloatMatrix3x3 (
mv_elements [ 0 ] [ 0 ] , mv_elements [ 1 ] [ 0 ] , mv_elements [ 2 ] [ 0 ] ,
mv_elements [ 0 ] [ 1 ] , mv_elements [ 1 ] [ 1 ] , mv_elements [ 2 ] [ 1 ] ,
mv_elements [ 0 ] [ 2 ] , mv_elements [ 1 ] [ 2 ] , mv_elements [ 2 ] [ 2 ] ) ;
2022-01-11 20:54:14 -03:00
auto const & normal_transform = model_view_transposed . inverse ( ) ;
2021-12-29 20:27:17 -03:00
2021-12-29 20:47:53 -03:00
m_rasterizer . draw_primitives ( primitive_type , m_model_view_matrix , normal_transform , m_projection_matrix , m_texture_matrix , m_vertex_list , enabled_texture_units ) ;
2021-01-06 08:58:01 -03:00
2021-12-16 17:26:15 -03:00
m_vertex_list . clear_with_capacity ( ) ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_frustum ( GLdouble left , GLdouble right , GLdouble bottom , GLdouble top , GLdouble near_val , GLdouble far_val )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_frustum , left , right , bottom , top , near_val , far_val ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
// Let's do some math!
// FIXME: Are we losing too much precision by doing this?
float a = static_cast < float > ( ( right + left ) / ( right - left ) ) ;
float b = static_cast < float > ( ( top + bottom ) / ( top - bottom ) ) ;
float c = static_cast < float > ( - ( ( far_val + near_val ) / ( far_val - near_val ) ) ) ;
float d = static_cast < float > ( - ( ( 2 * ( far_val * near_val ) ) / ( far_val - near_val ) ) ) ;
FloatMatrix4x4 frustum {
( ( 2 * ( float ) near_val ) / ( ( float ) right - ( float ) left ) ) , 0 , a , 0 ,
0 , ( ( 2 * ( float ) near_val ) / ( ( float ) top - ( float ) bottom ) ) , b , 0 ,
0 , 0 , c , d ,
0 , 0 , - 1 , 0
} ;
2021-12-20 20:52:14 -03:00
if ( m_current_matrix_mode = = GL_PROJECTION )
2021-01-06 08:58:01 -03:00
m_projection_matrix = m_projection_matrix * frustum ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_MODELVIEW )
2021-01-06 08:58:01 -03:00
m_projection_matrix = m_model_view_matrix * frustum ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * frustum ;
else
VERIFY_NOT_REACHED ( ) ;
2021-04-23 10:48:28 -03:00
}
2021-04-24 10:10:15 -03:00
void SoftwareGLContext : : gl_ortho ( GLdouble left , GLdouble right , GLdouble bottom , GLdouble top , GLdouble near_val , GLdouble far_val )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_ortho , left , right , bottom , top , near_val , far_val ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( left = = right | | bottom = = top | | near_val = = far_val , GL_INVALID_VALUE ) ;
2021-04-24 10:10:15 -03:00
auto rl = right - left ;
auto tb = top - bottom ;
auto fn = far_val - near_val ;
auto tx = - ( right + left ) / rl ;
auto ty = - ( top + bottom ) / tb ;
auto tz = - ( far_val + near_val ) / fn ;
FloatMatrix4x4 projection {
static_cast < float > ( 2 / rl ) , 0 , 0 , static_cast < float > ( tx ) ,
0 , static_cast < float > ( 2 / tb ) , 0 , static_cast < float > ( ty ) ,
0 , 0 , static_cast < float > ( - 2 / fn ) , static_cast < float > ( tz ) ,
0 , 0 , 0 , 1
} ;
2021-12-20 20:52:14 -03:00
if ( m_current_matrix_mode = = GL_PROJECTION )
2021-04-24 10:10:15 -03:00
m_projection_matrix = m_projection_matrix * projection ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_MODELVIEW )
2021-04-24 10:10:15 -03:00
m_projection_matrix = m_model_view_matrix * projection ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * projection ;
else
VERIFY_NOT_REACHED ( ) ;
2021-04-24 10:10:15 -03:00
}
2021-04-23 10:48:28 -03:00
GLenum SoftwareGLContext : : gl_get_error ( )
{
2021-05-29 12:40:33 -03:00
if ( m_in_draw_state )
2021-04-23 12:45:09 -03:00
return GL_INVALID_OPERATION ;
2021-05-29 13:18:54 -03:00
auto last_error = m_error ;
m_error = GL_NO_ERROR ;
return last_error ;
2021-01-06 08:58:01 -03:00
}
GLubyte * SoftwareGLContext : : gl_get_string ( GLenum name )
{
2021-05-29 12:40:33 -03:00
RETURN_VALUE_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION , nullptr ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
switch ( name ) {
case GL_VENDOR :
2021-12-22 22:38:20 -03:00
return reinterpret_cast < GLubyte * > ( const_cast < char * > ( m_device_info . vendor_name . characters ( ) ) ) ;
2021-01-06 08:58:01 -03:00
case GL_RENDERER :
2021-12-22 22:38:20 -03:00
return reinterpret_cast < GLubyte * > ( const_cast < char * > ( m_device_info . device_name . characters ( ) ) ) ;
2021-01-06 08:58:01 -03:00
case GL_VERSION :
2021-08-16 13:18:41 -03:00
return reinterpret_cast < GLubyte * > ( const_cast < char * > ( " 1.5 " ) ) ;
case GL_EXTENSIONS :
return reinterpret_cast < GLubyte * > ( const_cast < char * > ( " " ) ) ;
2021-12-01 14:13:30 -03:00
case GL_SHADING_LANGUAGE_VERSION :
return reinterpret_cast < GLubyte * > ( const_cast < char * > ( " 0.0 " ) ) ;
2021-01-06 08:58:01 -03:00
default :
2021-12-01 18:06:27 -03:00
dbgln_if ( GL_DEBUG , " gl_get_string({:#x}): unknown name " , name ) ;
2021-01-06 08:58:01 -03:00
break ;
}
2021-05-29 12:40:33 -03:00
RETURN_VALUE_WITH_ERROR_IF ( true , GL_INVALID_ENUM , nullptr ) ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_load_identity ( )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_load_identity ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = FloatMatrix4x4 : : identity ( ) ;
else if ( m_current_matrix_mode = = GL_MODELVIEW )
m_model_view_matrix = FloatMatrix4x4 : : identity ( ) ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = FloatMatrix4x4 : : identity ( ) ;
2021-01-06 08:58:01 -03:00
else
VERIFY_NOT_REACHED ( ) ;
}
2021-04-24 09:43:42 -03:00
void SoftwareGLContext : : gl_load_matrix ( const FloatMatrix4x4 & matrix )
{
2021-12-01 09:27:24 -03:00
APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED ( gl_load_matrix , matrix ) ;
2021-05-09 00:55:15 -03:00
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-24 09:43:42 -03:00
if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = matrix ;
else if ( m_current_matrix_mode = = GL_MODELVIEW )
m_model_view_matrix = matrix ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = matrix ;
2021-04-24 09:43:42 -03:00
else
VERIFY_NOT_REACHED ( ) ;
}
2021-01-06 08:58:01 -03:00
void SoftwareGLContext : : gl_matrix_mode ( GLenum mode )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_matrix_mode , mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-20 20:52:14 -03:00
RETURN_WITH_ERROR_IF ( mode < GL_MODELVIEW | | mode > GL_TEXTURE , GL_INVALID_ENUM ) ;
2021-04-23 10:48:28 -03:00
2021-01-06 08:58:01 -03:00
m_current_matrix_mode = mode ;
}
void SoftwareGLContext : : gl_push_matrix ( )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_push_matrix ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
switch ( m_current_matrix_mode ) {
case GL_PROJECTION :
2021-12-20 20:52:14 -03:00
RETURN_WITH_ERROR_IF ( m_projection_matrix_stack . size ( ) > = PROJECTION_MATRIX_STACK_LIMIT , GL_STACK_OVERFLOW ) ;
2021-01-06 08:58:01 -03:00
m_projection_matrix_stack . append ( m_projection_matrix ) ;
break ;
case GL_MODELVIEW :
2021-12-20 20:52:14 -03:00
RETURN_WITH_ERROR_IF ( m_model_view_matrix_stack . size ( ) > = MODELVIEW_MATRIX_STACK_LIMIT , GL_STACK_OVERFLOW ) ;
2021-01-06 08:58:01 -03:00
m_model_view_matrix_stack . append ( m_model_view_matrix ) ;
break ;
2021-12-20 20:52:14 -03:00
case GL_TEXTURE :
RETURN_WITH_ERROR_IF ( m_texture_matrix_stack . size ( ) > = TEXTURE_MATRIX_STACK_LIMIT , GL_STACK_OVERFLOW ) ;
m_texture_matrix_stack . append ( m_texture_matrix ) ;
break ;
2021-01-06 08:58:01 -03:00
default :
2021-12-20 20:52:14 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-01-06 08:58:01 -03:00
}
}
void SoftwareGLContext : : gl_pop_matrix ( )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_pop_matrix ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
switch ( m_current_matrix_mode ) {
case GL_PROJECTION :
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_projection_matrix_stack . size ( ) = = 0 , GL_STACK_UNDERFLOW ) ;
2021-01-06 08:58:01 -03:00
m_projection_matrix = m_projection_matrix_stack . take_last ( ) ;
break ;
case GL_MODELVIEW :
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_model_view_matrix_stack . size ( ) = = 0 , GL_STACK_UNDERFLOW ) ;
2021-01-06 08:58:01 -03:00
m_model_view_matrix = m_model_view_matrix_stack . take_last ( ) ;
break ;
2021-12-20 20:52:14 -03:00
case GL_TEXTURE :
RETURN_WITH_ERROR_IF ( m_texture_matrix_stack . size ( ) = = 0 , GL_STACK_UNDERFLOW ) ;
m_texture_matrix = m_texture_matrix_stack . take_last ( ) ;
break ;
2021-01-06 08:58:01 -03:00
default :
2021-12-20 20:52:14 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-01-06 08:58:01 -03:00
}
}
2021-12-01 09:27:24 -03:00
void SoftwareGLContext : : gl_mult_matrix ( FloatMatrix4x4 const & matrix )
{
APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED ( gl_mult_matrix , matrix ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-20 20:52:14 -03:00
if ( m_current_matrix_mode = = GL_MODELVIEW )
2021-12-01 09:27:24 -03:00
m_model_view_matrix = m_model_view_matrix * matrix ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = m_projection_matrix * matrix ;
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * matrix ;
else
VERIFY_NOT_REACHED ( ) ;
2021-12-01 09:27:24 -03:00
}
2021-01-06 08:58:01 -03:00
void SoftwareGLContext : : gl_rotate ( GLdouble angle , GLdouble x , GLdouble y , GLdouble z )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_rotate , angle , x , y , z ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-01-06 08:58:01 -03:00
FloatVector3 axis = { ( float ) x , ( float ) y , ( float ) z } ;
axis . normalize ( ) ;
2021-08-16 09:01:48 -03:00
auto rotation_mat = Gfx : : rotation_matrix ( axis , static_cast < float > ( angle * M_PI * 2 / 360 ) ) ;
2021-01-06 08:58:01 -03:00
if ( m_current_matrix_mode = = GL_MODELVIEW )
m_model_view_matrix = m_model_view_matrix * rotation_mat ;
else if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = m_projection_matrix * rotation_mat ;
2021-12-20 20:52:14 -03:00
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * rotation_mat ;
else
VERIFY_NOT_REACHED ( ) ;
2021-01-06 08:58:01 -03:00
}
2021-04-24 09:11:48 -03:00
void SoftwareGLContext : : gl_scale ( GLdouble x , GLdouble y , GLdouble z )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_scale , x , y , z ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-24 09:11:48 -03:00
2021-12-20 20:52:14 -03:00
auto scale_matrix = Gfx : : scale_matrix ( FloatVector3 { static_cast < float > ( x ) , static_cast < float > ( y ) , static_cast < float > ( z ) } ) ;
if ( m_current_matrix_mode = = GL_MODELVIEW )
m_model_view_matrix = m_model_view_matrix * scale_matrix ;
else if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = m_projection_matrix * scale_matrix ;
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * scale_matrix ;
else
VERIFY_NOT_REACHED ( ) ;
2021-04-24 09:11:48 -03:00
}
2021-01-06 08:58:01 -03:00
void SoftwareGLContext : : gl_translate ( GLdouble x , GLdouble y , GLdouble z )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_translate , x , y , z ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-23 12:45:09 -03:00
2021-12-20 20:52:14 -03:00
auto translation_matrix = Gfx : : translation_matrix ( FloatVector3 { static_cast < float > ( x ) , static_cast < float > ( y ) , static_cast < float > ( z ) } ) ;
if ( m_current_matrix_mode = = GL_MODELVIEW )
m_model_view_matrix = m_model_view_matrix * translation_matrix ;
else if ( m_current_matrix_mode = = GL_PROJECTION )
m_projection_matrix = m_projection_matrix * translation_matrix ;
else if ( m_current_matrix_mode = = GL_TEXTURE )
m_texture_matrix = m_texture_matrix * translation_matrix ;
else
VERIFY_NOT_REACHED ( ) ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_vertex ( GLdouble x , GLdouble y , GLdouble z , GLdouble w )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_vertex , x , y , z , w ) ;
2021-12-16 18:43:39 -03:00
SoftGPU : : Vertex vertex ;
2021-01-06 08:58:01 -03:00
2021-08-16 14:22:08 -03:00
vertex . position = { static_cast < float > ( x ) , static_cast < float > ( y ) , static_cast < float > ( z ) , static_cast < float > ( w ) } ;
vertex . color = m_current_vertex_color ;
2021-12-20 20:46:21 -03:00
vertex . tex_coord = m_current_vertex_tex_coord ;
2021-12-01 11:33:23 -03:00
vertex . normal = m_current_vertex_normal ;
2021-01-06 08:58:01 -03:00
2021-12-16 17:26:15 -03:00
m_vertex_list . append ( vertex ) ;
2021-05-23 07:38:18 -03:00
}
// FIXME: We need to add `r` and `q` to our GLVertex?!
2021-08-16 10:13:19 -03:00
void SoftwareGLContext : : gl_tex_coord ( GLfloat s , GLfloat t , GLfloat r , GLfloat q )
2021-05-23 07:38:18 -03:00
{
2021-08-16 10:13:19 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_tex_coord , s , t , r , q ) ;
2021-05-23 07:38:18 -03:00
2021-08-16 10:13:19 -03:00
m_current_vertex_tex_coord = { s , t , r , q } ;
2021-01-06 08:58:01 -03:00
}
void SoftwareGLContext : : gl_viewport ( GLint x , GLint y , GLsizei width , GLsizei height )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_viewport , x , y , width , height ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-08 10:43:03 -03:00
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 , GL_INVALID_VALUE ) ;
m_viewport = { x , y , width , height } ;
2021-04-23 12:45:09 -03:00
2022-01-08 10:43:03 -03:00
auto rasterizer_options = m_rasterizer . options ( ) ;
rasterizer_options . viewport = m_viewport ;
m_rasterizer . set_options ( rasterizer_options ) ;
2021-01-06 08:58:01 -03:00
}
2021-04-24 07:09:56 -03:00
void SoftwareGLContext : : gl_enable ( GLenum capability )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_enable , capability ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-24 07:09:56 -03:00
2021-05-06 18:17:35 -03:00
auto rasterizer_options = m_rasterizer . options ( ) ;
bool update_rasterizer_options = false ;
2021-04-24 07:09:56 -03:00
switch ( capability ) {
2022-01-12 23:03:35 -03:00
case GL_COLOR_MATERIAL :
m_color_material_enabled = true ;
break ;
2021-04-24 07:09:56 -03:00
case GL_CULL_FACE :
m_cull_faces = true ;
2021-12-16 17:10:53 -03:00
rasterizer_options . enable_culling = true ;
update_rasterizer_options = true ;
2021-04-24 07:09:56 -03:00
break ;
2021-05-06 18:17:35 -03:00
case GL_DEPTH_TEST :
m_depth_test_enabled = true ;
rasterizer_options . enable_depth_test = true ;
update_rasterizer_options = true ;
break ;
2021-05-15 17:55:40 -03:00
case GL_BLEND :
m_blend_enabled = true ;
rasterizer_options . enable_blending = true ;
update_rasterizer_options = true ;
break ;
2021-05-16 11:43:09 -03:00
case GL_ALPHA_TEST :
m_alpha_test_enabled = true ;
rasterizer_options . enable_alpha_test = true ;
update_rasterizer_options = true ;
break ;
2021-12-01 18:27:48 -03:00
case GL_DITHER :
m_dither_enabled = true ;
break ;
2021-08-25 05:10:44 -03:00
case GL_FOG :
rasterizer_options . fog_enabled = true ;
update_rasterizer_options = true ;
break ;
2021-12-01 18:18:40 -03:00
case GL_LIGHTING :
m_lighting_enabled = true ;
2022-01-08 04:52:12 -03:00
rasterizer_options . lighting_enabled = true ;
update_rasterizer_options = true ;
2021-12-01 18:18:40 -03:00
break ;
2021-12-29 20:27:17 -03:00
case GL_NORMALIZE :
m_normalize = true ;
rasterizer_options . normalization_enabled = true ;
update_rasterizer_options = true ;
break ;
2021-11-27 15:00:16 -03:00
case GL_SCISSOR_TEST :
rasterizer_options . scissor_enabled = true ;
update_rasterizer_options = true ;
break ;
2021-12-01 10:57:54 -03:00
case GL_STENCIL_TEST :
m_stencil_test_enabled = true ;
2022-01-16 18:48:46 -03:00
rasterizer_options . enable_stencil_test = true ;
update_rasterizer_options = true ;
2021-12-01 10:57:54 -03:00
break ;
2021-11-29 16:57:27 -03:00
case GL_TEXTURE_1D :
m_active_texture_unit - > set_texture_1d_enabled ( true ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_2D :
m_active_texture_unit - > set_texture_2d_enabled ( true ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_3D :
m_active_texture_unit - > set_texture_3d_enabled ( true ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_CUBE_MAP :
m_active_texture_unit - > set_texture_cube_map_enabled ( true ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
2022-01-07 11:54:50 -03:00
case GL_LIGHT0 :
case GL_LIGHT1 :
case GL_LIGHT2 :
case GL_LIGHT3 :
case GL_LIGHT4 :
case GL_LIGHT5 :
case GL_LIGHT6 :
case GL_LIGHT7 :
m_light_states . at ( capability - GL_LIGHT0 ) . is_enabled = true ;
m_light_state_is_dirty = true ;
break ;
2021-12-29 20:56:41 -03:00
case GL_TEXTURE_GEN_Q :
case GL_TEXTURE_GEN_R :
case GL_TEXTURE_GEN_S :
case GL_TEXTURE_GEN_T :
texture_coordinate_generation ( capability ) . enabled = true ;
m_texcoord_generation_dirty = true ;
break ;
2021-04-24 07:09:56 -03:00
default :
2022-01-12 23:00:26 -03:00
dbgln_if ( GL_DEBUG , " gl_enable({:#x}): unknown parameter " , capability ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
2021-04-24 07:09:56 -03:00
}
2021-05-06 18:17:35 -03:00
if ( update_rasterizer_options )
m_rasterizer . set_options ( rasterizer_options ) ;
2021-04-24 07:09:56 -03:00
}
void SoftwareGLContext : : gl_disable ( GLenum capability )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_disable , capability ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-04-24 07:09:56 -03:00
2021-05-06 18:17:35 -03:00
auto rasterizer_options = m_rasterizer . options ( ) ;
bool update_rasterizer_options = false ;
2021-04-24 07:09:56 -03:00
switch ( capability ) {
2022-01-12 23:03:35 -03:00
case GL_COLOR_MATERIAL :
m_color_material_enabled = false ;
break ;
2021-04-24 07:09:56 -03:00
case GL_CULL_FACE :
m_cull_faces = false ;
2021-12-16 17:10:53 -03:00
rasterizer_options . enable_culling = false ;
update_rasterizer_options = true ;
2021-04-24 07:09:56 -03:00
break ;
2021-05-06 18:17:35 -03:00
case GL_DEPTH_TEST :
m_depth_test_enabled = false ;
rasterizer_options . enable_depth_test = false ;
update_rasterizer_options = true ;
break ;
2021-05-15 17:55:40 -03:00
case GL_BLEND :
m_blend_enabled = false ;
rasterizer_options . enable_blending = false ;
2021-05-29 12:54:40 -03:00
update_rasterizer_options = true ;
2021-05-15 17:55:40 -03:00
break ;
2021-05-16 11:43:09 -03:00
case GL_ALPHA_TEST :
m_alpha_test_enabled = false ;
rasterizer_options . enable_alpha_test = false ;
2021-05-29 12:54:40 -03:00
update_rasterizer_options = true ;
2021-05-16 11:43:09 -03:00
break ;
2021-12-01 18:27:48 -03:00
case GL_DITHER :
m_dither_enabled = false ;
break ;
2021-10-17 20:30:47 -03:00
case GL_FOG :
rasterizer_options . fog_enabled = false ;
update_rasterizer_options = true ;
break ;
2021-12-01 18:18:40 -03:00
case GL_LIGHTING :
m_lighting_enabled = false ;
2022-01-08 04:52:12 -03:00
rasterizer_options . lighting_enabled = false ;
update_rasterizer_options = true ;
2021-12-01 18:18:40 -03:00
break ;
2022-01-07 11:54:50 -03:00
case GL_LIGHT0 :
case GL_LIGHT1 :
case GL_LIGHT2 :
case GL_LIGHT3 :
case GL_LIGHT4 :
case GL_LIGHT5 :
case GL_LIGHT6 :
case GL_LIGHT7 :
m_light_states . at ( capability - GL_LIGHT0 ) . is_enabled = false ;
m_light_state_is_dirty = true ;
break ;
2021-12-29 20:27:17 -03:00
case GL_NORMALIZE :
m_normalize = false ;
rasterizer_options . normalization_enabled = false ;
update_rasterizer_options = true ;
break ;
2021-11-27 15:00:16 -03:00
case GL_SCISSOR_TEST :
rasterizer_options . scissor_enabled = false ;
update_rasterizer_options = true ;
break ;
2021-12-01 10:57:54 -03:00
case GL_STENCIL_TEST :
m_stencil_test_enabled = false ;
2022-01-16 18:48:46 -03:00
rasterizer_options . enable_stencil_test = false ;
update_rasterizer_options = true ;
2021-12-01 10:57:54 -03:00
break ;
2021-11-29 16:57:27 -03:00
case GL_TEXTURE_1D :
m_active_texture_unit - > set_texture_1d_enabled ( false ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_2D :
m_active_texture_unit - > set_texture_2d_enabled ( false ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_3D :
m_active_texture_unit - > set_texture_3d_enabled ( false ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
case GL_TEXTURE_CUBE_MAP :
m_active_texture_unit - > set_texture_cube_map_enabled ( false ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-11-29 16:57:27 -03:00
break ;
2021-12-29 20:56:41 -03:00
case GL_TEXTURE_GEN_Q :
case GL_TEXTURE_GEN_R :
case GL_TEXTURE_GEN_S :
case GL_TEXTURE_GEN_T :
texture_coordinate_generation ( capability ) . enabled = false ;
m_texcoord_generation_dirty = true ;
break ;
2021-04-24 07:09:56 -03:00
default :
2022-01-12 23:00:26 -03:00
dbgln_if ( GL_DEBUG , " gl_disable({:#x}): unknown parameter " , capability ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
2021-04-24 07:09:56 -03:00
}
2021-05-06 18:17:35 -03:00
if ( update_rasterizer_options )
m_rasterizer . set_options ( rasterizer_options ) ;
2021-04-24 07:09:56 -03:00
}
2021-10-17 20:31:21 -03:00
GLboolean SoftwareGLContext : : gl_is_enabled ( GLenum capability )
{
RETURN_VALUE_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION , 0 ) ;
2022-01-08 13:10:56 -03:00
auto optional_parameter = get_context_parameter ( capability ) ;
RETURN_VALUE_WITH_ERROR_IF ( ! optional_parameter . has_value ( ) , GL_INVALID_ENUM , 0 ) ;
2021-10-17 20:31:21 -03:00
2022-01-08 13:10:56 -03:00
auto parameter = optional_parameter . release_value ( ) ;
RETURN_VALUE_WITH_ERROR_IF ( ! parameter . is_capability , GL_INVALID_ENUM , 0 ) ;
2021-10-17 20:31:21 -03:00
2022-01-08 13:10:56 -03:00
return parameter . value . boolean_value ;
2021-10-17 20:31:21 -03:00
}
2021-05-09 18:52:39 -03:00
void SoftwareGLContext : : gl_gen_textures ( GLsizei n , GLuint * textures )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( n < 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-09 18:52:39 -03:00
m_name_allocator . allocate ( n , textures ) ;
2021-05-13 09:18:17 -03:00
2021-05-29 19:15:51 -03:00
// Initialize all texture names with a nullptr
2021-05-13 09:18:17 -03:00
for ( auto i = 0 ; i < n ; i + + ) {
GLuint name = textures [ i ] ;
2021-05-29 19:15:51 -03:00
m_allocated_textures . set ( name , nullptr ) ;
2021-05-13 09:18:17 -03:00
}
2021-05-09 18:52:39 -03:00
}
void SoftwareGLContext : : gl_delete_textures ( GLsizei n , const GLuint * textures )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( n < 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-09 18:52:39 -03:00
2021-05-13 09:18:17 -03:00
for ( auto i = 0 ; i < n ; i + + ) {
GLuint name = textures [ i ] ;
2021-11-27 22:45:08 -03:00
if ( name = = 0 )
continue ;
m_name_allocator . free ( name ) ;
2021-05-13 09:18:17 -03:00
2021-05-30 08:39:31 -03:00
auto texture_object = m_allocated_textures . find ( name ) ;
if ( texture_object = = m_allocated_textures . end ( ) | | texture_object - > value . is_null ( ) )
continue ;
// Check all texture units
for ( auto & texture_unit : m_texture_units ) {
if ( texture_object - > value = = texture_unit . bound_texture ( ) )
2021-11-29 22:03:45 -03:00
texture_unit . bind_texture_to_target ( GL_TEXTURE_2D , nullptr ) ;
2021-05-30 08:39:31 -03:00
}
2021-05-29 19:15:51 -03:00
2021-05-13 09:18:17 -03:00
m_allocated_textures . remove ( name ) ;
}
}
void SoftwareGLContext : : gl_tex_image_2d ( GLenum target , GLint level , GLint internal_format , GLsizei width , GLsizei height , GLint border , GLenum format , GLenum type , const GLvoid * data )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-13 09:18:17 -03:00
// We only support GL_TEXTURE_2D for now
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_2D , GL_INVALID_ENUM ) ;
2021-05-13 09:18:17 -03:00
2021-05-29 19:15:51 -03:00
// Check if there is actually a texture bound
2021-05-30 08:39:31 -03:00
RETURN_WITH_ERROR_IF ( target = = GL_TEXTURE_2D & & m_active_texture_unit - > currently_bound_target ( ) ! = GL_TEXTURE_2D , GL_INVALID_OPERATION ) ;
2021-05-29 19:15:51 -03:00
2021-08-31 17:08:01 -03:00
// Internal format can also be a number between 1 and 4. Symbolic formats were only added with EXT_texture, promoted to core in OpenGL 1.1
if ( internal_format = = 1 )
internal_format = GL_ALPHA ;
else if ( internal_format = = 2 )
internal_format = GL_LUMINANCE_ALPHA ;
else if ( internal_format = = 3 )
internal_format = GL_RGB ;
else if ( internal_format = = 4 )
internal_format = GL_RGBA ;
2021-05-13 09:18:17 -03:00
// We only support symbolic constants for now
2022-01-15 07:11:27 -03:00
RETURN_WITH_ERROR_IF ( ! ( internal_format = = GL_RGB | | internal_format = = GL_RGBA | | internal_format = = GL_LUMINANCE8 | | internal_format = = GL_LUMINANCE8_ALPHA8 ) , GL_INVALID_ENUM ) ;
2021-12-01 22:17:05 -03:00
RETURN_WITH_ERROR_IF ( ! ( type = = GL_UNSIGNED_BYTE | | type = = GL_UNSIGNED_SHORT_5_6_5 ) , GL_INVALID_VALUE ) ;
2021-05-29 18:34:40 -03:00
RETURN_WITH_ERROR_IF ( level < 0 | | level > Texture2D : : LOG2_MAX_TEXTURE_SIZE , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 | | width > ( 2 + Texture2D : : MAX_TEXTURE_SIZE ) | | height > ( 2 + Texture2D : : MAX_TEXTURE_SIZE ) , GL_INVALID_VALUE ) ;
2021-08-31 17:17:04 -03:00
// Check if width and height are a power of 2
RETURN_WITH_ERROR_IF ( ( width & ( width - 1 ) ) ! = 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( ( height & ( height - 1 ) ) ! = 0 , GL_INVALID_VALUE ) ;
2021-12-26 11:30:20 -03:00
RETURN_WITH_ERROR_IF ( border ! = 0 , GL_INVALID_VALUE ) ;
2021-05-13 09:18:17 -03:00
2021-12-22 18:00:08 -03:00
if ( level = = 0 ) {
// FIXME: OpenGL has the concept of texture and mipmap completeness. A texture has to fulfill certain criteria to be considered complete.
// Trying to render while an incomplete texture is bound will result in an error.
// Here we simply create a complete device image when mipmap level 0 is attached to the texture object. This has the unfortunate side effect
// that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized.
// To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time.
// All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete.
SoftGPU : : ImageFormat device_format ;
switch ( internal_format ) {
case GL_RGB :
device_format = SoftGPU : : ImageFormat : : RGB888 ;
break ;
case GL_RGBA :
device_format = SoftGPU : : ImageFormat : : RGBA8888 ;
break ;
2022-01-15 07:11:27 -03:00
case GL_LUMINANCE8 :
device_format = SoftGPU : : ImageFormat : : L8 ;
break ;
case GL_LUMINANCE8_ALPHA8 :
device_format = SoftGPU : : ImageFormat : : L8A8 ;
break ;
2021-12-22 18:00:08 -03:00
default :
VERIFY_NOT_REACHED ( ) ;
}
m_active_texture_unit - > bound_texture_2d ( ) - > set_device_image ( m_rasterizer . create_image ( device_format , width , height , 1 , 999 , 1 ) ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-12-22 18:00:08 -03:00
}
2021-12-26 11:30:20 -03:00
m_active_texture_unit - > bound_texture_2d ( ) - > upload_texture_data ( level , internal_format , width , height , format , type , data , m_unpack_row_length , m_unpack_alignment ) ;
2021-08-31 17:35:52 -03:00
}
void SoftwareGLContext : : gl_tex_sub_image_2d ( GLenum target , GLint level , GLint xoffset , GLint yoffset , GLsizei width , GLsizei height , GLenum format , GLenum type , const GLvoid * data )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// We only support GL_TEXTURE_2D for now
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_2D , GL_INVALID_ENUM ) ;
// Check if there is actually a texture bound
RETURN_WITH_ERROR_IF ( target = = GL_TEXTURE_2D & & m_active_texture_unit - > currently_bound_target ( ) ! = GL_TEXTURE_2D , GL_INVALID_OPERATION ) ;
// We only support symbolic constants for now
2021-12-01 22:17:05 -03:00
RETURN_WITH_ERROR_IF ( ! ( format = = GL_RGBA | | format = = GL_RGB ) , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( ! ( type = = GL_UNSIGNED_BYTE | | type = = GL_UNSIGNED_SHORT_5_6_5 ) , GL_INVALID_VALUE ) ;
2021-08-31 17:35:52 -03:00
RETURN_WITH_ERROR_IF ( level < 0 | | level > Texture2D : : LOG2_MAX_TEXTURE_SIZE , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 | | width > ( 2 + Texture2D : : MAX_TEXTURE_SIZE ) | | height > ( 2 + Texture2D : : MAX_TEXTURE_SIZE ) , GL_INVALID_VALUE ) ;
auto texture = m_active_texture_unit - > bound_texture_2d ( ) ;
RETURN_WITH_ERROR_IF ( xoffset < 0 | | yoffset < 0 | | xoffset + width > texture - > width_at_lod ( level ) | | yoffset + height > texture - > height_at_lod ( level ) , GL_INVALID_VALUE ) ;
2021-11-27 21:48:21 -03:00
texture - > replace_sub_texture_data ( level , xoffset , yoffset , width , height , format , type , data , m_unpack_row_length , m_unpack_alignment ) ;
2021-05-09 18:52:39 -03:00
}
2021-08-11 17:09:18 -03:00
void SoftwareGLContext : : gl_tex_parameter ( GLenum target , GLenum pname , GLfloat param )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_tex_parameter , target , pname , param ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_2D , GL_INVALID_ENUM ) ;
// FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
RETURN_WITH_ERROR_IF ( ! ( pname = = GL_TEXTURE_MIN_FILTER
| | pname = = GL_TEXTURE_MAG_FILTER
| | pname = = GL_TEXTURE_WRAP_S
| | pname = = GL_TEXTURE_WRAP_T ) ,
GL_INVALID_ENUM ) ;
if ( target = = GL_TEXTURE_2D ) {
auto texture2d = m_active_texture_unit - > bound_texture_2d ( ) ;
if ( texture2d . is_null ( ) )
return ;
switch ( pname ) {
case GL_TEXTURE_MIN_FILTER :
RETURN_WITH_ERROR_IF ( ! ( param = = GL_NEAREST
| | param = = GL_LINEAR
| | param = = GL_NEAREST_MIPMAP_NEAREST
| | param = = GL_LINEAR_MIPMAP_NEAREST
| | param = = GL_NEAREST_MIPMAP_LINEAR
| | param = = GL_LINEAR_MIPMAP_LINEAR ) ,
GL_INVALID_ENUM ) ;
texture2d - > sampler ( ) . set_min_filter ( param ) ;
break ;
case GL_TEXTURE_MAG_FILTER :
RETURN_WITH_ERROR_IF ( ! ( param = = GL_NEAREST
| | param = = GL_LINEAR ) ,
GL_INVALID_ENUM ) ;
texture2d - > sampler ( ) . set_mag_filter ( param ) ;
break ;
case GL_TEXTURE_WRAP_S :
RETURN_WITH_ERROR_IF ( ! ( param = = GL_CLAMP
| | param = = GL_CLAMP_TO_BORDER
| | param = = GL_CLAMP_TO_EDGE
| | param = = GL_MIRRORED_REPEAT
| | param = = GL_REPEAT ) ,
GL_INVALID_ENUM ) ;
texture2d - > sampler ( ) . set_wrap_s_mode ( param ) ;
break ;
case GL_TEXTURE_WRAP_T :
RETURN_WITH_ERROR_IF ( ! ( param = = GL_CLAMP
| | param = = GL_CLAMP_TO_BORDER
| | param = = GL_CLAMP_TO_EDGE
| | param = = GL_MIRRORED_REPEAT
| | param = = GL_REPEAT ) ,
GL_INVALID_ENUM ) ;
texture2d - > sampler ( ) . set_wrap_t_mode ( param ) ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-08-11 17:09:18 -03:00
}
2021-04-24 07:09:56 -03:00
void SoftwareGLContext : : gl_front_face ( GLenum face )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_front_face , face ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( face < GL_CW | | face > GL_CCW , GL_INVALID_ENUM ) ;
2021-04-24 07:09:56 -03:00
m_front_face = face ;
2021-12-16 17:10:53 -03:00
auto rasterizer_options = m_rasterizer . options ( ) ;
2021-12-22 19:16:58 -03:00
rasterizer_options . front_face = ( face = = GL_CW ) ? SoftGPU : : WindingOrder : : Clockwise : SoftGPU : : WindingOrder : : CounterClockwise ;
2021-12-16 17:10:53 -03:00
m_rasterizer . set_options ( rasterizer_options ) ;
2021-04-24 07:09:56 -03:00
}
void SoftwareGLContext : : gl_cull_face ( GLenum cull_mode )
{
2021-05-09 00:55:15 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_cull_face , cull_mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( cull_mode < GL_FRONT | | cull_mode > GL_FRONT_AND_BACK , GL_INVALID_ENUM ) ;
2021-04-24 07:09:56 -03:00
m_culled_sides = cull_mode ;
2021-12-16 17:10:53 -03:00
auto rasterizer_options = m_rasterizer . options ( ) ;
2021-12-22 19:23:15 -03:00
rasterizer_options . cull_back = cull_mode = = GL_BACK | | cull_mode = = GL_FRONT_AND_BACK ;
rasterizer_options . cull_front = cull_mode = = GL_FRONT | | cull_mode = = GL_FRONT_AND_BACK ;
2021-12-16 17:10:53 -03:00
m_rasterizer . set_options ( rasterizer_options ) ;
2021-04-24 07:09:56 -03:00
}
2021-05-09 00:55:15 -03:00
GLuint SoftwareGLContext : : gl_gen_lists ( GLsizei range )
{
2021-05-29 12:40:33 -03:00
RETURN_VALUE_WITH_ERROR_IF ( range < = 0 , GL_INVALID_VALUE , 0 ) ;
RETURN_VALUE_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION , 0 ) ;
2021-05-09 00:55:15 -03:00
auto initial_entry = m_listings . size ( ) ;
m_listings . resize ( range + initial_entry ) ;
return initial_entry + 1 ;
}
2021-12-01 10:28:26 -03:00
void SoftwareGLContext : : invoke_list ( size_t list_index )
2021-05-09 00:55:15 -03:00
{
2021-12-01 10:28:26 -03:00
auto & listing = m_listings [ list_index - 1 ] ;
2021-05-09 00:55:15 -03:00
for ( auto & entry : listing . entries ) {
entry . function . visit ( [ & ] ( auto & function ) {
entry . arguments . visit ( [ & ] ( auto & arguments ) {
auto apply = [ & ] < typename . . . Args > ( Args & & . . . args )
{
if constexpr ( requires { ( this - > * function ) ( forward < Args > ( args ) . . . ) ; } )
( this - > * function ) ( forward < Args > ( args ) . . . ) ;
} ;
arguments . apply_as_args ( apply ) ;
} ) ;
} ) ;
}
}
2021-12-01 10:28:26 -03:00
void SoftwareGLContext : : gl_call_list ( GLuint list )
{
if ( m_gl_call_depth > max_allowed_gl_call_depth )
return ;
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_call_list , list ) ;
if ( m_listings . size ( ) < list )
return ;
TemporaryChange change { m_gl_call_depth , m_gl_call_depth + 1 } ;
invoke_list ( list ) ;
}
void SoftwareGLContext : : gl_call_lists ( GLsizei n , GLenum type , void const * lists )
{
if ( m_gl_call_depth > max_allowed_gl_call_depth )
return ;
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_call_lists , n , type , lists ) ;
RETURN_WITH_ERROR_IF ( n < 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( ! ( type = = GL_BYTE
| | type = = GL_UNSIGNED_BYTE
| | type = = GL_SHORT
| | type = = GL_UNSIGNED_SHORT
| | type = = GL_INT
| | type = = GL_UNSIGNED_INT
| | type = = GL_FLOAT
| | type = = GL_2_BYTES
| | type = = GL_3_BYTES
| | type = = GL_4_BYTES ) ,
GL_INVALID_ENUM ) ;
TemporaryChange change { m_gl_call_depth , m_gl_call_depth + 1 } ;
auto invoke_all_lists = [ & ] < typename T > ( T const * lists ) {
for ( int i = 0 ; i < n ; + + i ) {
auto list = static_cast < size_t > ( lists [ i ] ) ;
2021-12-01 10:34:26 -03:00
invoke_list ( m_list_base + list ) ;
2021-12-01 10:28:26 -03:00
}
} ;
switch ( type ) {
case GL_BYTE :
invoke_all_lists ( static_cast < GLbyte const * > ( lists ) ) ;
break ;
case GL_UNSIGNED_BYTE :
invoke_all_lists ( static_cast < GLubyte const * > ( lists ) ) ;
break ;
case GL_SHORT :
invoke_all_lists ( static_cast < GLshort const * > ( lists ) ) ;
break ;
case GL_UNSIGNED_SHORT :
invoke_all_lists ( static_cast < GLushort const * > ( lists ) ) ;
break ;
case GL_INT :
invoke_all_lists ( static_cast < GLint const * > ( lists ) ) ;
break ;
case GL_UNSIGNED_INT :
invoke_all_lists ( static_cast < GLuint const * > ( lists ) ) ;
break ;
case GL_FLOAT :
invoke_all_lists ( static_cast < GLfloat const * > ( lists ) ) ;
break ;
case GL_2_BYTES :
case GL_3_BYTES :
case GL_4_BYTES :
dbgln ( " SoftwareGLContext FIXME: unimplemented glCallLists() with type {} " , type ) ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2021-05-09 00:55:15 -03:00
void SoftwareGLContext : : gl_delete_lists ( GLuint list , GLsizei range )
{
if ( m_listings . size ( ) < list | | m_listings . size ( ) < = list + range )
return ;
for ( auto & entry : m_listings . span ( ) . slice ( list - 1 , range ) )
2021-08-16 14:22:08 -03:00
entry . entries . clear_with_capacity ( ) ;
2021-05-09 00:55:15 -03:00
}
2021-12-01 10:34:26 -03:00
void SoftwareGLContext : : gl_list_base ( GLuint base )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_list_base , base ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
m_list_base = base ;
}
2021-05-09 00:55:15 -03:00
void SoftwareGLContext : : gl_end_list ( )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! m_current_listing_index . has_value ( ) , GL_INVALID_OPERATION ) ;
2021-05-09 00:55:15 -03:00
m_listings [ m_current_listing_index - > index ] = move ( m_current_listing_index - > listing ) ;
m_current_listing_index . clear ( ) ;
}
void SoftwareGLContext : : gl_new_list ( GLuint list , GLenum mode )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( list = = 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( mode ! = GL_COMPILE & & mode ! = GL_COMPILE_AND_EXECUTE , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( m_current_listing_index . has_value ( ) , GL_INVALID_OPERATION ) ;
2021-05-09 00:55:15 -03:00
if ( m_listings . size ( ) < list )
return ;
m_current_listing_index = CurrentListing { { } , static_cast < size_t > ( list - 1 ) , mode } ;
}
2021-12-01 12:27:08 -03:00
GLboolean SoftwareGLContext : : gl_is_list ( GLuint list )
{
RETURN_VALUE_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION , GL_FALSE ) ;
return list < m_listings . size ( ) ? GL_TRUE : GL_FALSE ;
}
2021-05-14 16:47:25 -03:00
void SoftwareGLContext : : gl_flush ( )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-14 16:47:25 -03:00
// No-op since SoftwareGLContext is completely synchronous at the moment
}
void SoftwareGLContext : : gl_finish ( )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-14 16:47:25 -03:00
// No-op since SoftwareGLContext is completely synchronous at the moment
}
2021-05-15 17:45:05 -03:00
void SoftwareGLContext : : gl_blend_func ( GLenum src_factor , GLenum dst_factor )
{
2021-05-16 09:42:08 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_blend_func , src_factor , dst_factor ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-15 17:45:05 -03:00
// FIXME: The list of allowed enums differs between API versions
// This was taken from the 2.0 spec on https://docs.gl/gl2/glBlendFunc
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( ! ( src_factor = = GL_ZERO
| | src_factor = = GL_ONE
| | src_factor = = GL_SRC_COLOR
| | src_factor = = GL_ONE_MINUS_SRC_COLOR
| | src_factor = = GL_DST_COLOR
| | src_factor = = GL_ONE_MINUS_DST_COLOR
| | src_factor = = GL_SRC_ALPHA
| | src_factor = = GL_ONE_MINUS_SRC_ALPHA
| | src_factor = = GL_DST_ALPHA
| | src_factor = = GL_ONE_MINUS_DST_ALPHA
| | src_factor = = GL_CONSTANT_COLOR
| | src_factor = = GL_ONE_MINUS_CONSTANT_COLOR
| | src_factor = = GL_CONSTANT_ALPHA
| | src_factor = = GL_ONE_MINUS_CONSTANT_ALPHA
| | src_factor = = GL_SRC_ALPHA_SATURATE ) ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! ( dst_factor = = GL_ZERO
| | dst_factor = = GL_ONE
| | dst_factor = = GL_SRC_COLOR
| | dst_factor = = GL_ONE_MINUS_SRC_COLOR
| | dst_factor = = GL_DST_COLOR
| | dst_factor = = GL_ONE_MINUS_DST_COLOR
| | dst_factor = = GL_SRC_ALPHA
| | dst_factor = = GL_ONE_MINUS_SRC_ALPHA
| | dst_factor = = GL_DST_ALPHA
| | dst_factor = = GL_ONE_MINUS_DST_ALPHA
| | dst_factor = = GL_CONSTANT_COLOR
| | dst_factor = = GL_ONE_MINUS_CONSTANT_COLOR
| | dst_factor = = GL_CONSTANT_ALPHA
| | dst_factor = = GL_ONE_MINUS_CONSTANT_ALPHA ) ,
GL_INVALID_ENUM ) ;
2021-05-15 17:45:05 -03:00
m_blend_source_factor = src_factor ;
m_blend_destination_factor = dst_factor ;
2021-05-15 17:55:40 -03:00
2021-12-22 19:11:35 -03:00
auto map_gl_blend_factor_to_device = [ ] ( GLenum factor ) constexpr
{
switch ( factor ) {
case GL_ZERO :
return SoftGPU : : BlendFactor : : Zero ;
case GL_ONE :
return SoftGPU : : BlendFactor : : One ;
case GL_SRC_ALPHA :
return SoftGPU : : BlendFactor : : SrcAlpha ;
case GL_ONE_MINUS_SRC_ALPHA :
return SoftGPU : : BlendFactor : : OneMinusSrcAlpha ;
case GL_SRC_COLOR :
return SoftGPU : : BlendFactor : : SrcColor ;
case GL_ONE_MINUS_SRC_COLOR :
return SoftGPU : : BlendFactor : : OneMinusSrcColor ;
case GL_DST_ALPHA :
return SoftGPU : : BlendFactor : : DstAlpha ;
case GL_ONE_MINUS_DST_ALPHA :
return SoftGPU : : BlendFactor : : OneMinusDstAlpha ;
case GL_DST_COLOR :
return SoftGPU : : BlendFactor : : DstColor ;
case GL_ONE_MINUS_DST_COLOR :
return SoftGPU : : BlendFactor : : OneMinusDstColor ;
case GL_SRC_ALPHA_SATURATE :
return SoftGPU : : BlendFactor : : SrcAlphaSaturate ;
default :
VERIFY_NOT_REACHED ( ) ;
}
} ;
2021-05-15 17:55:40 -03:00
auto options = m_rasterizer . options ( ) ;
2021-12-22 19:11:35 -03:00
options . blend_source_factor = map_gl_blend_factor_to_device ( m_blend_source_factor ) ;
options . blend_destination_factor = map_gl_blend_factor_to_device ( m_blend_destination_factor ) ;
2021-05-15 17:55:40 -03:00
m_rasterizer . set_options ( options ) ;
2021-05-15 17:45:05 -03:00
}
2021-05-16 09:05:13 -03:00
void SoftwareGLContext : : gl_shade_model ( GLenum mode )
{
2021-05-16 09:42:08 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_shade_model , mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( mode ! = GL_FLAT & & mode ! = GL_SMOOTH , GL_INVALID_ENUM ) ;
2021-05-16 09:05:13 -03:00
auto options = m_rasterizer . options ( ) ;
options . shade_smooth = ( mode = = GL_SMOOTH ) ;
m_rasterizer . set_options ( options ) ;
}
2021-05-16 11:43:09 -03:00
void SoftwareGLContext : : gl_alpha_func ( GLenum func , GLclampf ref )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_alpha_func , func , ref ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( func < GL_NEVER | | func > GL_ALWAYS , GL_INVALID_ENUM ) ;
2021-05-16 11:43:09 -03:00
m_alpha_test_func = func ;
m_alpha_test_ref_value = ref ;
auto options = m_rasterizer . options ( ) ;
2021-12-22 18:59:07 -03:00
switch ( func ) {
case GL_NEVER :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : Never ;
break ;
case GL_ALWAYS :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : Always ;
break ;
case GL_LESS :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : Less ;
break ;
case GL_LEQUAL :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : LessOrEqual ;
break ;
case GL_EQUAL :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : Equal ;
break ;
case GL_NOTEQUAL :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : NotEqual ;
break ;
case GL_GEQUAL :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : GreaterOrEqual ;
break ;
case GL_GREATER :
options . alpha_test_func = SoftGPU : : AlphaTestFunction : : Greater ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2021-05-16 11:43:09 -03:00
options . alpha_test_ref_value = m_alpha_test_ref_value ;
m_rasterizer . set_options ( options ) ;
}
2021-05-20 18:20:23 -03:00
void SoftwareGLContext : : gl_hint ( GLenum target , GLenum mode )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_hint , target , mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-20 18:20:23 -03:00
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( target ! = GL_PERSPECTIVE_CORRECTION_HINT
& & target ! = GL_POINT_SMOOTH_HINT
& & target ! = GL_LINE_SMOOTH_HINT
& & target ! = GL_POLYGON_SMOOTH_HINT
& & target ! = GL_FOG_HINT
& & target ! = GL_GENERATE_MIPMAP_HINT
& & target ! = GL_TEXTURE_COMPRESSION_HINT ,
GL_INVALID_ENUM ) ;
2021-05-20 18:20:23 -03:00
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( mode ! = GL_DONT_CARE
& & mode ! = GL_FASTEST
& & mode ! = GL_NICEST ,
GL_INVALID_ENUM ) ;
2021-05-20 18:20:23 -03:00
// According to the spec implementors are free to ignore glHint. So we do.
}
2021-05-24 10:24:49 -03:00
void SoftwareGLContext : : gl_read_buffer ( GLenum mode )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_read_buffer , mode ) ;
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-05-24 10:24:49 -03:00
// FIXME: Also allow aux buffers GL_AUX0 through GL_AUX3 here
// plus any aux buffer between 0 and GL_AUX_BUFFERS
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( mode ! = GL_FRONT_LEFT
& & mode ! = GL_FRONT_RIGHT
& & mode ! = GL_BACK_LEFT
& & mode ! = GL_BACK_RIGHT
& & mode ! = GL_FRONT
& & mode ! = GL_BACK
& & mode ! = GL_LEFT
& & mode ! = GL_RIGHT ,
GL_INVALID_ENUM ) ;
2021-05-24 10:24:49 -03:00
// FIXME: We do not currently have aux buffers, so make it an invalid
// operation to select anything but front or back buffers. Also we do
// not allow selecting the stereoscopic RIGHT buffers since we do not
// have them configured.
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( mode ! = GL_FRONT_LEFT
& & mode ! = GL_FRONT
& & mode ! = GL_BACK_LEFT
& & mode ! = GL_BACK
& & mode ! = GL_FRONT
& & mode ! = GL_BACK
& & mode ! = GL_LEFT ,
GL_INVALID_OPERATION ) ;
2021-05-24 10:24:49 -03:00
m_current_read_buffer = mode ;
}
2021-08-31 15:23:29 -03:00
void SoftwareGLContext : : gl_draw_buffer ( GLenum buffer )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_draw_buffer , buffer ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: Also allow aux buffers GL_AUX0 through GL_AUX3 here
// plus any aux buffer between 0 and GL_AUX_BUFFERS
RETURN_WITH_ERROR_IF ( buffer ! = GL_NONE
& & buffer ! = GL_FRONT_LEFT
& & buffer ! = GL_FRONT_RIGHT
& & buffer ! = GL_BACK_LEFT
& & buffer ! = GL_BACK_RIGHT
& & buffer ! = GL_FRONT
& & buffer ! = GL_BACK
& & buffer ! = GL_LEFT
& & buffer ! = GL_RIGHT ,
GL_INVALID_ENUM ) ;
// FIXME: We do not currently have aux buffers, so make it an invalid
// operation to select anything but front or back buffers. Also we do
// not allow selecting the stereoscopic RIGHT buffers since we do not
// have them configured.
RETURN_WITH_ERROR_IF ( buffer ! = GL_NONE
& & buffer ! = GL_FRONT_LEFT
& & buffer ! = GL_FRONT
& & buffer ! = GL_BACK_LEFT
& & buffer ! = GL_BACK
& & buffer ! = GL_FRONT
& & buffer ! = GL_BACK
& & buffer ! = GL_LEFT ,
GL_INVALID_OPERATION ) ;
m_current_draw_buffer = buffer ;
auto rasterizer_options = m_rasterizer . options ( ) ;
2021-12-22 19:31:44 -03:00
// FIXME: We only have a single draw buffer in SoftGPU at the moment,
// so we simply disable color writes if GL_NONE is selected
rasterizer_options . enable_color_write = m_current_draw_buffer ! = GL_NONE ;
2021-08-31 15:23:29 -03:00
m_rasterizer . set_options ( rasterizer_options ) ;
}
2021-05-24 11:11:06 -03:00
void SoftwareGLContext : : gl_read_pixels ( GLint x , GLint y , GLsizei width , GLsizei height , GLenum format , GLenum type , GLvoid * pixels )
{
2021-05-29 12:40:33 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( format ! = GL_COLOR_INDEX
& & format ! = GL_STENCIL_INDEX
& & format ! = GL_DEPTH_COMPONENT
& & format ! = GL_RED
& & format ! = GL_GREEN
& & format ! = GL_BLUE
& & format ! = GL_ALPHA
& & format ! = GL_RGB
& & format ! = GL_RGBA
& & format ! = GL_LUMINANCE
& & format ! = GL_LUMINANCE_ALPHA ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( type ! = GL_UNSIGNED_BYTE
& & type ! = GL_BYTE
& & type ! = GL_BITMAP
& & type ! = GL_UNSIGNED_SHORT
& & type ! = GL_SHORT
& & type ! = GL_BLUE
& & type ! = GL_UNSIGNED_INT
& & type ! = GL_INT
& & type ! = GL_FLOAT ,
GL_INVALID_ENUM ) ;
// FIXME: We only support RGBA buffers for now.
// Once we add support for indexed color modes do the correct check here
RETURN_WITH_ERROR_IF ( format = = GL_COLOR_INDEX , GL_INVALID_OPERATION ) ;
// FIXME: We do not have stencil buffers yet
// Once we add support for stencil buffers do the correct check here
RETURN_WITH_ERROR_IF ( format = = GL_STENCIL_INDEX , GL_INVALID_OPERATION ) ;
2021-05-24 11:11:06 -03:00
if ( format = = GL_DEPTH_COMPONENT ) {
// FIXME: This check needs to be a bit more sophisticated. Currently the buffers
// are hardcoded. Once we add proper structures for them we need to correct this check
2021-05-29 12:40:33 -03:00
// Error because only back buffer has a depth buffer
RETURN_WITH_ERROR_IF ( m_current_read_buffer = = GL_FRONT
| | m_current_read_buffer = = GL_FRONT_LEFT
| | m_current_read_buffer = = GL_FRONT_RIGHT ,
GL_INVALID_OPERATION ) ;
2021-05-24 11:11:06 -03:00
}
2021-05-24 12:52:24 -03:00
// Some helper functions for converting float values to integer types
auto float_to_i8 = [ ] ( float f ) - > GLchar {
return static_cast < GLchar > ( ( 0x7f * min ( max ( f , 0.0f ) , 1.0f ) - 1 ) / 2 ) ;
} ;
auto float_to_i16 = [ ] ( float f ) - > GLshort {
return static_cast < GLshort > ( ( 0x7fff * min ( max ( f , 0.0f ) , 1.0f ) - 1 ) / 2 ) ;
} ;
auto float_to_i32 = [ ] ( float f ) - > GLint {
return static_cast < GLint > ( ( 0x7fffffff * min ( max ( f , 0.0f ) , 1.0f ) - 1 ) / 2 ) ;
} ;
auto float_to_u8 = [ ] ( float f ) - > GLubyte {
return static_cast < GLubyte > ( 0xff * min ( max ( f , 0.0f ) , 1.0f ) ) ;
} ;
auto float_to_u16 = [ ] ( float f ) - > GLushort {
return static_cast < GLushort > ( 0xffff * min ( max ( f , 0.0f ) , 1.0f ) ) ;
} ;
auto float_to_u32 = [ ] ( float f ) - > GLuint {
return static_cast < GLuint > ( 0xffffffff * min ( max ( f , 0.0f ) , 1.0f ) ) ;
} ;
2021-11-27 21:48:21 -03:00
u8 component_size = 0 ;
switch ( type ) {
case GL_BYTE :
case GL_UNSIGNED_BYTE :
component_size = 1 ;
break ;
case GL_SHORT :
case GL_UNSIGNED_SHORT :
component_size = 2 ;
break ;
case GL_INT :
case GL_UNSIGNED_INT :
case GL_FLOAT :
component_size = 4 ;
break ;
}
2021-05-24 11:11:06 -03:00
if ( format = = GL_DEPTH_COMPONENT ) {
2021-11-27 21:48:21 -03:00
auto const row_stride = ( width * component_size + m_pack_alignment - 1 ) / m_pack_alignment * m_pack_alignment ;
2021-05-24 11:11:06 -03:00
// Read from depth buffer
2021-05-25 10:21:31 -03:00
for ( GLsizei i = 0 ; i < height ; + + i ) {
for ( GLsizei j = 0 ; j < width ; + + j ) {
2021-05-24 12:52:24 -03:00
float depth = m_rasterizer . get_depthbuffer_value ( x + j , y + i ) ;
2021-11-27 21:48:21 -03:00
auto char_ptr = reinterpret_cast < char * > ( pixels ) + i * row_stride + j * component_size ;
2021-05-24 12:52:24 -03:00
switch ( type ) {
case GL_BYTE :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLchar * > ( char_ptr ) = float_to_i8 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_SHORT :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLshort * > ( char_ptr ) = float_to_i16 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_INT :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLint * > ( char_ptr ) = float_to_i32 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_UNSIGNED_BYTE :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLubyte * > ( char_ptr ) = float_to_u8 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_UNSIGNED_SHORT :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLushort * > ( char_ptr ) = float_to_u16 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_UNSIGNED_INT :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLuint * > ( char_ptr ) = float_to_u32 ( depth ) ;
2021-05-24 12:52:24 -03:00
break ;
case GL_FLOAT :
2021-11-27 21:48:21 -03:00
* reinterpret_cast < GLfloat * > ( char_ptr ) = min ( max ( depth , 0.0f ) , 1.0f ) ;
2021-05-24 12:52:24 -03:00
break ;
}
}
}
2021-05-24 11:11:06 -03:00
return ;
}
2021-05-24 12:52:24 -03:00
bool write_red = false ;
bool write_green = false ;
bool write_blue = false ;
bool write_alpha = false ;
size_t component_count = 0 ;
size_t red_offset = 0 ;
size_t green_offset = 0 ;
size_t blue_offset = 0 ;
size_t alpha_offset = 0 ;
char * red_ptr = nullptr ;
char * green_ptr = nullptr ;
char * blue_ptr = nullptr ;
char * alpha_ptr = nullptr ;
switch ( format ) {
case GL_RGB :
write_red = true ;
write_green = true ;
write_blue = true ;
component_count = 3 ;
red_offset = 2 ;
green_offset = 1 ;
blue_offset = 0 ;
break ;
case GL_RGBA :
write_red = true ;
write_green = true ;
write_blue = true ;
write_alpha = true ;
component_count = 4 ;
red_offset = 3 ;
green_offset = 2 ;
blue_offset = 1 ;
alpha_offset = 0 ;
break ;
case GL_RED :
write_red = true ;
component_count = 1 ;
red_offset = 0 ;
break ;
case GL_GREEN :
write_green = true ;
component_count = 1 ;
green_offset = 0 ;
break ;
case GL_BLUE :
write_blue = true ;
component_count = 1 ;
blue_offset = 0 ;
break ;
case GL_ALPHA :
write_alpha = true ;
component_count = 1 ;
alpha_offset = 0 ;
break ;
}
2021-11-27 21:48:21 -03:00
auto const pixel_bytes = component_size * component_count ;
auto const row_alignment_bytes = ( m_pack_alignment - ( ( width * pixel_bytes ) % m_pack_alignment ) ) % m_pack_alignment ;
2021-05-24 12:52:24 -03:00
char * out_ptr = reinterpret_cast < char * > ( pixels ) ;
for ( int i = 0 ; i < ( int ) height ; + + i ) {
for ( int j = 0 ; j < ( int ) width ; + + j ) {
Gfx : : RGBA32 color { } ;
if ( m_current_read_buffer = = GL_FRONT | | m_current_read_buffer = = GL_LEFT | | m_current_read_buffer = = GL_FRONT_LEFT ) {
if ( y + i > = m_frontbuffer - > width ( ) | | x + j > = m_frontbuffer - > height ( ) )
color = 0 ;
else
color = m_frontbuffer - > scanline ( y + i ) [ x + j ] ;
} else {
color = m_rasterizer . get_backbuffer_pixel ( x + j , y + i ) ;
}
float red = ( ( color > > 24 ) & 0xff ) / 255.0f ;
float green = ( ( color > > 16 ) & 0xff ) / 255.0f ;
float blue = ( ( color > > 8 ) & 0xff ) / 255.0f ;
float alpha = ( color & 0xff ) / 255.0f ;
// FIXME: Set up write pointers based on selected endianness (glPixelStore)
red_ptr = out_ptr + ( component_size * red_offset ) ;
green_ptr = out_ptr + ( component_size * green_offset ) ;
blue_ptr = out_ptr + ( component_size * blue_offset ) ;
alpha_ptr = out_ptr + ( component_size * alpha_offset ) ;
switch ( type ) {
case GL_BYTE :
if ( write_red )
* reinterpret_cast < GLchar * > ( red_ptr ) = float_to_i8 ( red ) ;
if ( write_green )
* reinterpret_cast < GLchar * > ( green_ptr ) = float_to_i8 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLchar * > ( blue_ptr ) = float_to_i8 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLchar * > ( alpha_ptr ) = float_to_i8 ( alpha ) ;
break ;
case GL_UNSIGNED_BYTE :
if ( write_red )
* reinterpret_cast < GLubyte * > ( red_ptr ) = float_to_u8 ( red ) ;
if ( write_green )
* reinterpret_cast < GLubyte * > ( green_ptr ) = float_to_u8 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLubyte * > ( blue_ptr ) = float_to_u8 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLubyte * > ( alpha_ptr ) = float_to_u8 ( alpha ) ;
break ;
case GL_SHORT :
if ( write_red )
* reinterpret_cast < GLshort * > ( red_ptr ) = float_to_i16 ( red ) ;
if ( write_green )
* reinterpret_cast < GLshort * > ( green_ptr ) = float_to_i16 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLshort * > ( blue_ptr ) = float_to_i16 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLshort * > ( alpha_ptr ) = float_to_i16 ( alpha ) ;
break ;
case GL_UNSIGNED_SHORT :
if ( write_red )
* reinterpret_cast < GLushort * > ( red_ptr ) = float_to_u16 ( red ) ;
if ( write_green )
* reinterpret_cast < GLushort * > ( green_ptr ) = float_to_u16 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLushort * > ( blue_ptr ) = float_to_u16 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLushort * > ( alpha_ptr ) = float_to_u16 ( alpha ) ;
break ;
case GL_INT :
if ( write_red )
* reinterpret_cast < GLint * > ( red_ptr ) = float_to_i32 ( red ) ;
if ( write_green )
* reinterpret_cast < GLint * > ( green_ptr ) = float_to_i32 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLint * > ( blue_ptr ) = float_to_i32 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLint * > ( alpha_ptr ) = float_to_i32 ( alpha ) ;
break ;
case GL_UNSIGNED_INT :
if ( write_red )
* reinterpret_cast < GLuint * > ( red_ptr ) = float_to_u32 ( red ) ;
if ( write_green )
* reinterpret_cast < GLuint * > ( green_ptr ) = float_to_u32 ( green ) ;
if ( write_blue )
* reinterpret_cast < GLuint * > ( blue_ptr ) = float_to_u32 ( blue ) ;
if ( write_alpha )
* reinterpret_cast < GLuint * > ( alpha_ptr ) = float_to_u32 ( alpha ) ;
break ;
case GL_FLOAT :
if ( write_red )
* reinterpret_cast < GLfloat * > ( red_ptr ) = min ( max ( red , 0.0f ) , 1.0f ) ;
if ( write_green )
* reinterpret_cast < GLfloat * > ( green_ptr ) = min ( max ( green , 0.0f ) , 1.0f ) ;
if ( write_blue )
* reinterpret_cast < GLfloat * > ( blue_ptr ) = min ( max ( blue , 0.0f ) , 1.0f ) ;
if ( write_alpha )
* reinterpret_cast < GLfloat * > ( alpha_ptr ) = min ( max ( alpha , 0.0f ) , 1.0f ) ;
break ;
}
2021-11-27 21:48:21 -03:00
out_ptr + = pixel_bytes ;
2021-05-24 12:52:24 -03:00
}
2021-11-27 21:48:21 -03:00
out_ptr + = row_alignment_bytes ;
2021-05-24 12:52:24 -03:00
}
2021-05-24 11:11:06 -03:00
}
2021-05-29 19:15:51 -03:00
void SoftwareGLContext : : gl_bind_texture ( GLenum target , GLuint texture )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: We only support GL_TEXTURE_2D for now
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_2D , GL_INVALID_ENUM ) ;
if ( texture = = 0 ) {
switch ( target ) {
case GL_TEXTURE_2D :
2021-11-29 22:03:45 -03:00
m_active_texture_unit - > bind_texture_to_target ( target , nullptr ) ;
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-05-29 19:15:51 -03:00
return ;
default :
VERIFY_NOT_REACHED ( ) ;
return ;
}
}
auto it = m_allocated_textures . find ( texture ) ;
2022-01-12 07:05:50 -03:00
RefPtr < Texture > texture_object ;
// OpenGL 1.x supports binding texture names that were not previously generated by glGenTextures.
// If there is not an allocated texture, meaning it was not previously generated by glGenTextures,
// we can keep texture_object null to both allocate and bind the texture with the passed in texture name.
// FIXME: Later OpenGL versions such as 4.x enforce that texture names being bound were previously generated
// by glGenTextures.
if ( it ! = m_allocated_textures . end ( ) )
texture_object = it - > value ;
2021-05-29 19:15:51 -03:00
// Binding a texture to a different target than it was first bound is an invalid operation
// FIXME: We only support GL_TEXTURE_2D for now
RETURN_WITH_ERROR_IF ( target = = GL_TEXTURE_2D & & ! texture_object . is_null ( ) & & ! texture_object - > is_texture_2d ( ) , GL_INVALID_OPERATION ) ;
if ( ! texture_object ) {
// This is the first time the texture is bound. Allocate an actual texture object
switch ( target ) {
case GL_TEXTURE_2D :
texture_object = adopt_ref ( * new Texture2D ( ) ) ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
m_allocated_textures . set ( texture , texture_object ) ;
}
switch ( target ) {
case GL_TEXTURE_2D :
2021-05-30 08:39:31 -03:00
m_active_texture_unit - > bind_texture_to_target ( target , texture_object ) ;
2021-05-29 19:15:51 -03:00
break ;
}
2021-12-22 18:02:57 -03:00
m_sampler_config_is_dirty = true ;
2021-05-29 19:15:51 -03:00
}
2022-01-13 03:02:33 -03:00
GLboolean SoftwareGLContext : : gl_is_texture ( GLuint texture )
{
RETURN_VALUE_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION , GL_FALSE ) ;
if ( texture = = 0 )
return GL_FALSE ;
auto it = m_allocated_textures . find ( texture ) ;
if ( it = = m_allocated_textures . end ( ) )
return GL_FALSE ;
return it - > value . is_null ( ) ? GL_FALSE : GL_TRUE ;
}
2021-05-30 03:56:56 -03:00
void SoftwareGLContext : : gl_active_texture ( GLenum texture )
{
RETURN_WITH_ERROR_IF ( texture < GL_TEXTURE0 | | texture > GL_TEXTURE31 , GL_INVALID_ENUM ) ;
m_active_texture_unit = & m_texture_units . at ( texture - GL_TEXTURE0 ) ;
}
2021-08-17 05:38:34 -03:00
void SoftwareGLContext : : gl_get_booleanv ( GLenum pname , GLboolean * data )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-01 18:06:27 -03:00
auto optional_parameter = get_context_parameter ( pname ) ;
RETURN_WITH_ERROR_IF ( ! optional_parameter . has_value ( ) , GL_INVALID_ENUM ) ;
auto parameter = optional_parameter . release_value ( ) ;
switch ( parameter . type ) {
case GL_BOOL :
* data = parameter . value . boolean_value ? GL_TRUE : GL_FALSE ;
2021-11-29 16:57:27 -03:00
break ;
2021-12-01 18:06:27 -03:00
case GL_DOUBLE :
* data = ( parameter . value . double_value = = 0.0 ) ? GL_FALSE : GL_TRUE ;
2021-11-29 16:57:27 -03:00
break ;
2021-12-01 18:06:27 -03:00
case GL_INT :
* data = ( parameter . value . integer_value = = 0 ) ? GL_FALSE : GL_TRUE ;
2021-11-29 16:57:27 -03:00
break ;
2021-08-17 05:38:34 -03:00
default :
2021-12-01 18:06:27 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-08-17 05:38:34 -03:00
}
}
2021-12-01 09:07:30 -03:00
void SoftwareGLContext : : gl_get_doublev ( GLenum pname , GLdouble * params )
{
get_floating_point ( pname , params ) ;
}
void SoftwareGLContext : : gl_get_floatv ( GLenum pname , GLfloat * params )
{
get_floating_point ( pname , params ) ;
}
template < typename T >
void SoftwareGLContext : : get_floating_point ( GLenum pname , T * params )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-01 18:06:27 -03:00
// Handle special matrix cases first
2021-12-01 09:07:30 -03:00
auto flatten_and_assign_matrix = [ & params ] ( const FloatMatrix4x4 & matrix ) {
auto elements = matrix . elements ( ) ;
2021-12-01 18:06:27 -03:00
for ( size_t i = 0 ; i < 4 ; + + i )
for ( size_t j = 0 ; j < 4 ; + + j )
2021-12-01 09:07:30 -03:00
params [ i * 4 + j ] = static_cast < T > ( elements [ i ] [ j ] ) ;
} ;
switch ( pname ) {
case GL_MODELVIEW_MATRIX :
if ( m_current_matrix_mode = = GL_MODELVIEW )
flatten_and_assign_matrix ( m_model_view_matrix ) ;
2021-12-01 18:06:27 -03:00
else if ( m_model_view_matrix_stack . is_empty ( ) )
flatten_and_assign_matrix ( FloatMatrix4x4 : : identity ( ) ) ;
else
flatten_and_assign_matrix ( m_model_view_matrix_stack . last ( ) ) ;
return ;
case GL_PROJECTION_MATRIX :
if ( m_current_matrix_mode = = GL_PROJECTION )
flatten_and_assign_matrix ( m_projection_matrix ) ;
else if ( m_projection_matrix_stack . is_empty ( ) )
flatten_and_assign_matrix ( FloatMatrix4x4 : : identity ( ) ) ;
else
flatten_and_assign_matrix ( m_projection_matrix_stack . last ( ) ) ;
return ;
}
// Regular parameters
auto optional_parameter = get_context_parameter ( pname ) ;
RETURN_WITH_ERROR_IF ( ! optional_parameter . has_value ( ) , GL_INVALID_ENUM ) ;
auto parameter = optional_parameter . release_value ( ) ;
switch ( parameter . type ) {
case GL_BOOL :
* params = parameter . value . boolean_value ? GL_TRUE : GL_FALSE ;
break ;
case GL_DOUBLE :
for ( size_t i = 0 ; i < parameter . count ; + + i ) {
params [ i ] = parameter . value . double_list [ i ] ;
}
break ;
case GL_INT :
for ( size_t i = 0 ; i < parameter . count ; + + i ) {
params [ i ] = parameter . value . integer_list [ i ] ;
2021-12-01 09:07:30 -03:00
}
break ;
default :
2021-12-01 18:06:27 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-12-01 09:07:30 -03:00
}
}
2021-08-17 05:58:21 -03:00
void SoftwareGLContext : : gl_get_integerv ( GLenum pname , GLint * data )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-01 18:06:27 -03:00
auto optional_parameter = get_context_parameter ( pname ) ;
RETURN_WITH_ERROR_IF ( ! optional_parameter . has_value ( ) , GL_INVALID_ENUM ) ;
auto parameter = optional_parameter . release_value ( ) ;
switch ( parameter . type ) {
case GL_BOOL :
* data = parameter . value . boolean_value ? GL_TRUE : GL_FALSE ;
2021-11-27 21:48:21 -03:00
break ;
2021-12-01 18:06:27 -03:00
case GL_DOUBLE : {
double const int_range = static_cast < double > ( NumericLimits < GLint > : : max ( ) ) - NumericLimits < GLint > : : min ( ) ;
for ( size_t i = 0 ; i < parameter . count ; + + i ) {
double const result_factor = ( clamp ( parameter . value . double_list [ i ] , - 1.0 , 1.0 ) + 1.0 ) / 2.0 ;
data [ i ] = static_cast < GLint > ( NumericLimits < GLint > : : min ( ) + result_factor * int_range ) ;
}
2021-11-27 15:00:16 -03:00
break ;
}
2021-12-01 18:06:27 -03:00
case GL_INT :
for ( size_t i = 0 ; i < parameter . count ; + + i ) {
data [ i ] = parameter . value . integer_list [ i ] ;
}
2021-12-01 16:42:45 -03:00
break ;
2021-08-17 05:58:21 -03:00
default :
2021-12-01 18:06:27 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-08-17 05:58:21 -03:00
}
}
2021-08-12 20:06:26 -03:00
void SoftwareGLContext : : gl_depth_mask ( GLboolean flag )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_depth_mask , flag ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
auto options = m_rasterizer . options ( ) ;
options . enable_depth_write = ( flag ! = GL_FALSE ) ;
m_rasterizer . set_options ( options ) ;
}
2021-08-12 20:36:22 -03:00
void SoftwareGLContext : : gl_enable_client_state ( GLenum cap )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
switch ( cap ) {
case GL_VERTEX_ARRAY :
m_client_side_vertex_array_enabled = true ;
break ;
case GL_COLOR_ARRAY :
m_client_side_color_array_enabled = true ;
break ;
case GL_TEXTURE_COORD_ARRAY :
m_client_side_texture_coord_array_enabled = true ;
break ;
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
}
void SoftwareGLContext : : gl_disable_client_state ( GLenum cap )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
switch ( cap ) {
case GL_VERTEX_ARRAY :
m_client_side_vertex_array_enabled = false ;
break ;
case GL_COLOR_ARRAY :
m_client_side_color_array_enabled = false ;
break ;
case GL_TEXTURE_COORD_ARRAY :
m_client_side_texture_coord_array_enabled = false ;
break ;
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
}
2021-08-13 08:03:06 -03:00
void SoftwareGLContext : : gl_vertex_pointer ( GLint size , GLenum type , GLsizei stride , const void * pointer )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-08-16 10:15:30 -03:00
RETURN_WITH_ERROR_IF ( ! ( size = = 2 | | size = = 3 | | size = = 4 ) , GL_INVALID_VALUE ) ;
2021-08-13 08:03:06 -03:00
RETURN_WITH_ERROR_IF ( ! ( type = = GL_SHORT | | type = = GL_INT | | type = = GL_FLOAT | | type = = GL_DOUBLE ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( stride < 0 , GL_INVALID_VALUE ) ;
m_client_vertex_pointer . size = size ;
m_client_vertex_pointer . type = type ;
m_client_vertex_pointer . stride = stride ;
m_client_vertex_pointer . pointer = pointer ;
}
2021-08-13 08:16:00 -03:00
void SoftwareGLContext : : gl_color_pointer ( GLint size , GLenum type , GLsizei stride , const void * pointer )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( size = = 3 | | size = = 4 ) , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( ! ( type = = GL_BYTE
| | type = = GL_UNSIGNED_BYTE
| | type = = GL_SHORT
| | type = = GL_UNSIGNED_SHORT
| | type = = GL_INT
| | type = = GL_UNSIGNED_INT
| | type = = GL_FLOAT
| | type = = GL_DOUBLE ) ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( stride < 0 , GL_INVALID_VALUE ) ;
m_client_color_pointer . size = size ;
m_client_color_pointer . type = type ;
m_client_color_pointer . stride = stride ;
m_client_color_pointer . pointer = pointer ;
}
2021-08-13 08:54:51 -03:00
void SoftwareGLContext : : gl_tex_coord_pointer ( GLint size , GLenum type , GLsizei stride , const void * pointer )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( size = = 1 | | size = = 2 | | size = = 3 | | size = = 4 ) , GL_INVALID_VALUE ) ;
RETURN_WITH_ERROR_IF ( ! ( type = = GL_SHORT | | type = = GL_INT | | type = = GL_FLOAT | | type = = GL_DOUBLE ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( stride < 0 , GL_INVALID_VALUE ) ;
m_client_tex_coord_pointer . size = size ;
m_client_tex_coord_pointer . type = type ;
m_client_tex_coord_pointer . stride = stride ;
m_client_tex_coord_pointer . pointer = pointer ;
}
2021-08-21 08:59:13 -03:00
void SoftwareGLContext : : gl_tex_env ( GLenum target , GLenum pname , GLfloat param )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_tex_env , target , pname , param ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-29 20:40:42 -03:00
// FIXME: We currently only support a subset of possible target values. Implement the rest!
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_ENV , GL_INVALID_ENUM ) ;
// FIXME: We currently only support a subset of possible pname values. Implement the rest!
RETURN_WITH_ERROR_IF ( pname ! = GL_TEXTURE_ENV_MODE , GL_INVALID_ENUM ) ;
auto param_enum = static_cast < GLenum > ( param ) ;
switch ( param_enum ) {
case GL_MODULATE :
case GL_REPLACE :
case GL_DECAL :
m_active_texture_unit - > set_env_mode ( param_enum ) ;
break ;
default :
// FIXME: We currently only support a subset of possible param values. Implement the rest!
dbgln_if ( GL_DEBUG , " gl_tex_env({:#x}, {:#x}, {}): param unimplemented " , target , pname , param ) ;
2021-08-21 08:59:13 -03:00
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
}
2021-08-13 10:22:29 -03:00
void SoftwareGLContext : : gl_draw_arrays ( GLenum mode , GLint first , GLsizei count )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_draw_arrays , mode , first , count ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-26 21:23:44 -03:00
// FIXME: Some modes are still missing (GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES)
2021-08-13 10:22:29 -03:00
RETURN_WITH_ERROR_IF ( ! ( mode = = GL_TRIANGLE_STRIP
| | mode = = GL_TRIANGLE_FAN
| | mode = = GL_TRIANGLES
| | mode = = GL_QUADS
2021-12-26 21:23:44 -03:00
| | mode = = GL_QUAD_STRIP
2021-08-13 10:22:29 -03:00
| | mode = = GL_POLYGON ) ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( count < 0 , GL_INVALID_VALUE ) ;
// At least the vertex array needs to be enabled
if ( ! m_client_side_vertex_array_enabled )
return ;
auto last = first + count ;
2021-12-01 19:24:31 -03:00
gl_begin ( mode ) ;
2021-08-13 10:22:29 -03:00
for ( int i = first ; i < last ; i + + ) {
if ( m_client_side_texture_coord_array_enabled ) {
float tex_coords [ 4 ] { 0 , 0 , 0 , 0 } ;
read_from_vertex_attribute_pointer ( m_client_tex_coord_pointer , i , tex_coords , false ) ;
2021-12-01 19:24:31 -03:00
gl_tex_coord ( tex_coords [ 0 ] , tex_coords [ 1 ] , tex_coords [ 2 ] , tex_coords [ 3 ] ) ;
2021-08-13 10:22:29 -03:00
}
if ( m_client_side_color_array_enabled ) {
float color [ 4 ] { 0 , 0 , 0 , 1 } ;
read_from_vertex_attribute_pointer ( m_client_color_pointer , i , color , true ) ;
glColor4fv ( color ) ;
}
float vertex [ 4 ] { 0 , 0 , 0 , 1 } ;
read_from_vertex_attribute_pointer ( m_client_vertex_pointer , i , vertex , false ) ;
glVertex4fv ( vertex ) ;
}
2021-12-01 19:24:31 -03:00
gl_end ( ) ;
2021-08-13 10:37:38 -03:00
}
void SoftwareGLContext : : gl_draw_elements ( GLenum mode , GLsizei count , GLenum type , const void * indices )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_draw_elements , mode , count , type , indices ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-26 21:23:44 -03:00
// FIXME: Some modes are still missing (GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES)
2021-08-13 10:37:38 -03:00
RETURN_WITH_ERROR_IF ( ! ( mode = = GL_TRIANGLE_STRIP
| | mode = = GL_TRIANGLE_FAN
| | mode = = GL_TRIANGLES
| | mode = = GL_QUADS
2021-12-26 21:23:44 -03:00
| | mode = = GL_QUAD_STRIP
2021-08-13 10:37:38 -03:00
| | mode = = GL_POLYGON ) ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! ( type = = GL_UNSIGNED_BYTE
| | type = = GL_UNSIGNED_SHORT
| | type = = GL_UNSIGNED_INT ) ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( count < 0 , GL_INVALID_VALUE ) ;
// At least the vertex array needs to be enabled
if ( ! m_client_side_vertex_array_enabled )
return ;
2021-12-01 19:24:31 -03:00
gl_begin ( mode ) ;
2021-08-13 10:37:38 -03:00
for ( int index = 0 ; index < count ; index + + ) {
int i = 0 ;
switch ( type ) {
case GL_UNSIGNED_BYTE :
i = reinterpret_cast < const GLubyte * > ( indices ) [ index ] ;
break ;
case GL_UNSIGNED_SHORT :
i = reinterpret_cast < const GLushort * > ( indices ) [ index ] ;
break ;
case GL_UNSIGNED_INT :
i = reinterpret_cast < const GLuint * > ( indices ) [ index ] ;
break ;
}
if ( m_client_side_texture_coord_array_enabled ) {
float tex_coords [ 4 ] { 0 , 0 , 0 , 0 } ;
read_from_vertex_attribute_pointer ( m_client_tex_coord_pointer , i , tex_coords , false ) ;
2021-12-01 19:24:31 -03:00
gl_tex_coord ( tex_coords [ 0 ] , tex_coords [ 1 ] , tex_coords [ 2 ] , tex_coords [ 3 ] ) ;
2021-08-13 10:37:38 -03:00
}
if ( m_client_side_color_array_enabled ) {
float color [ 4 ] { 0 , 0 , 0 , 1 } ;
read_from_vertex_attribute_pointer ( m_client_color_pointer , i , color , true ) ;
glColor4fv ( color ) ;
}
float vertex [ 4 ] { 0 , 0 , 0 , 1 } ;
read_from_vertex_attribute_pointer ( m_client_vertex_pointer , i , vertex , false ) ;
glVertex4fv ( vertex ) ;
}
2021-12-01 19:24:31 -03:00
gl_end ( ) ;
2021-08-13 10:22:29 -03:00
}
2021-12-01 13:19:22 -03:00
void SoftwareGLContext : : gl_draw_pixels ( GLsizei width , GLsizei height , GLenum format , GLenum type , const void * data )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_draw_pixels , width , height , format , type , data ) ;
2021-12-21 10:42:25 -03:00
RETURN_WITH_ERROR_IF ( format < GL_COLOR_INDEX | | format > GL_BGRA , GL_INVALID_ENUM ) ;
2021-12-01 13:19:22 -03:00
2021-12-21 10:42:25 -03:00
RETURN_WITH_ERROR_IF ( ( type < GL_BYTE | | type > GL_FLOAT )
& & ( type < GL_UNSIGNED_BYTE_3_3_2 | | type > GL_UNSIGNED_INT_10_10_10_2 )
& & ( type < GL_UNSIGNED_BYTE_2_3_3_REV | | type > GL_UNSIGNED_INT_2_10_10_10_REV ) ,
2021-12-01 13:19:22 -03:00
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( type = = GL_BITMAP & & ! ( format = = GL_COLOR_INDEX | | format = = GL_STENCIL_INDEX ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 , GL_INVALID_VALUE ) ;
// FIXME: GL_INVALID_OPERATION is generated if format is GL_STENCIL_INDEX and there is no stencil buffer
// FIXME: GL_INVALID_OPERATION is generated if format is GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA,
// GL_BGR, GL_BGRA, GL_LUMINANCE, or GL_LUMINANCE_ALPHA, and the GL is in color index mode
RETURN_WITH_ERROR_IF ( format ! = GL_RGB
& & ( type = = GL_UNSIGNED_BYTE_3_3_2
| | type = = GL_UNSIGNED_BYTE_2_3_3_REV
| | type = = GL_UNSIGNED_SHORT_5_6_5
| | type = = GL_UNSIGNED_SHORT_5_6_5_REV ) ,
GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( format = = GL_RGBA | | format = = GL_BGRA )
& & ( type = = GL_UNSIGNED_SHORT_4_4_4_4
| | type = = GL_UNSIGNED_SHORT_4_4_4_4_REV
| | type = = GL_UNSIGNED_SHORT_5_5_5_1
| | type = = GL_UNSIGNED_SHORT_1_5_5_5_REV
| | type = = GL_UNSIGNED_INT_8_8_8_8
| | type = = GL_UNSIGNED_INT_8_8_8_8_REV
| | type = = GL_UNSIGNED_INT_10_10_10_2
| | type = = GL_UNSIGNED_INT_2_10_10_10_REV ) ,
GL_INVALID_OPERATION ) ;
// FIXME: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
// target and the buffer object's data store is currently mapped.
// FIXME: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
// target and the data would be unpacked from the buffer object such that the memory reads required would
// exceed the data store size.
// FIXME: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
// target and data is not evenly divisible into the number of bytes needed to store in memory a datum
// indicated by type.
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-12 13:08:59 -03:00
// FIXME: we only support RGBA + UNSIGNED_BYTE and DEPTH_COMPONENT + UNSIGNED_SHORT, implement all combinations!
if ( ! ( ( format = = GL_RGBA & & type = = GL_UNSIGNED_BYTE ) | | ( format = = GL_DEPTH_COMPONENT & & type = = GL_UNSIGNED_SHORT ) ) ) {
dbgln_if ( GL_DEBUG , " gl_draw_pixels(): support for format {:#x} and/or type {:#x} not implemented " , format , type ) ;
2021-12-01 13:19:22 -03:00
return ;
}
2022-01-12 13:08:59 -03:00
// FIXME: implement support for pixel parameters such as GL_UNPACK_ALIGNMENT
if ( format = = GL_RGBA ) {
auto bitmap_or_error = Gfx : : Bitmap : : try_create ( Gfx : : BitmapFormat : : BGRA8888 , { width , height } ) ;
RETURN_WITH_ERROR_IF ( bitmap_or_error . is_error ( ) , GL_OUT_OF_MEMORY ) ;
auto bitmap = bitmap_or_error . release_value ( ) ;
auto pixel_data = static_cast < u32 const * > ( data ) ;
for ( int y = 0 ; y < height ; + + y )
for ( int x = 0 ; x < width ; + + x )
bitmap - > set_pixel ( x , y , Color : : from_rgba ( * ( pixel_data + + ) ) ) ;
m_rasterizer . blit_to_color_buffer_at_raster_position ( bitmap ) ;
} else if ( format = = GL_DEPTH_COMPONENT ) {
Vector < float > depth_values ;
depth_values . ensure_capacity ( width * height ) ;
auto depth_data = static_cast < u16 const * > ( data ) ;
for ( int y = 0 ; y < height ; + + y ) {
for ( int x = 0 ; x < width ; + + x ) {
auto u16_value = * ( depth_data + + ) ;
auto float_value = static_cast < float > ( u16_value ) / NumericLimits < u16 > : : max ( ) ;
depth_values . append ( float_value ) ;
}
}
2021-12-01 13:19:22 -03:00
2022-01-12 13:08:59 -03:00
m_rasterizer . blit_to_depth_buffer_at_raster_position ( depth_values , width , height ) ;
} else {
VERIFY_NOT_REACHED ( ) ;
}
2021-12-01 13:19:22 -03:00
}
2021-08-16 12:52:12 -03:00
void SoftwareGLContext : : gl_depth_range ( GLdouble min , GLdouble max )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_depth_range , min , max ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
auto options = m_rasterizer . options ( ) ;
options . depth_min = clamp ( min , 0.f , 1.f ) ;
options . depth_max = clamp ( max , 0.f , 1.f ) ;
m_rasterizer . set_options ( options ) ;
}
2021-08-16 12:59:45 -03:00
void SoftwareGLContext : : gl_depth_func ( GLenum func )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_depth_func , func ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( func = = GL_NEVER
| | func = = GL_LESS
| | func = = GL_EQUAL
| | func = = GL_LEQUAL
| | func = = GL_GREATER
| | func = = GL_NOTEQUAL
| | func = = GL_GEQUAL
| | func = = GL_ALWAYS ) ,
GL_INVALID_ENUM ) ;
auto options = m_rasterizer . options ( ) ;
2021-12-22 19:38:13 -03:00
switch ( func ) {
case GL_NEVER :
options . depth_func = SoftGPU : : DepthTestFunction : : Never ;
break ;
case GL_ALWAYS :
options . depth_func = SoftGPU : : DepthTestFunction : : Always ;
break ;
case GL_LESS :
options . depth_func = SoftGPU : : DepthTestFunction : : Less ;
break ;
case GL_LEQUAL :
options . depth_func = SoftGPU : : DepthTestFunction : : LessOrEqual ;
break ;
case GL_EQUAL :
options . depth_func = SoftGPU : : DepthTestFunction : : Equal ;
break ;
case GL_NOTEQUAL :
options . depth_func = SoftGPU : : DepthTestFunction : : NotEqual ;
break ;
case GL_GEQUAL :
options . depth_func = SoftGPU : : DepthTestFunction : : GreaterOrEqual ;
break ;
case GL_GREATER :
options . depth_func = SoftGPU : : DepthTestFunction : : Greater ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2021-08-16 12:59:45 -03:00
m_rasterizer . set_options ( options ) ;
}
2021-08-13 10:22:29 -03:00
// General helper function to read arbitrary vertex attribute data into a float array
void SoftwareGLContext : : read_from_vertex_attribute_pointer ( VertexAttribPointer const & attrib , int index , float * elements , bool normalize )
{
auto byte_ptr = reinterpret_cast < const char * > ( attrib . pointer ) ;
size_t stride = attrib . stride ;
switch ( attrib . type ) {
case GL_BYTE : {
if ( stride = = 0 )
stride = sizeof ( GLbyte ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLbyte * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0x80 ;
}
break ;
}
case GL_UNSIGNED_BYTE : {
if ( stride = = 0 )
stride = sizeof ( GLubyte ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLubyte * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0xff ;
}
break ;
}
case GL_SHORT : {
if ( stride = = 0 )
stride = sizeof ( GLshort ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLshort * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0x8000 ;
}
break ;
}
case GL_UNSIGNED_SHORT : {
if ( stride = = 0 )
stride = sizeof ( GLushort ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLushort * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0xffff ;
}
break ;
}
case GL_INT : {
if ( stride = = 0 )
stride = sizeof ( GLint ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLint * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0x80000000 ;
}
break ;
}
case GL_UNSIGNED_INT : {
if ( stride = = 0 )
stride = sizeof ( GLuint ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLuint * > ( byte_ptr + stride * index ) + i ) ;
if ( normalize )
elements [ i ] / = 0xffffffff ;
}
break ;
}
case GL_FLOAT : {
if ( stride = = 0 )
stride = sizeof ( GLfloat ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = * ( reinterpret_cast < const GLfloat * > ( byte_ptr + stride * index ) + i ) ;
}
break ;
}
case GL_DOUBLE : {
if ( stride = = 0 )
stride = sizeof ( GLdouble ) * attrib . size ;
for ( int i = 0 ; i < attrib . size ; i + + ) {
elements [ i ] = static_cast < float > ( * ( reinterpret_cast < const GLdouble * > ( byte_ptr + stride * index ) + i ) ) ;
}
break ;
}
}
}
2021-08-13 09:03:39 -03:00
void SoftwareGLContext : : gl_color_mask ( GLboolean red , GLboolean green , GLboolean blue , GLboolean alpha )
{
auto options = m_rasterizer . options ( ) ;
auto mask = options . color_mask ;
if ( ! red )
mask & = ~ 0x000000ff ;
else
mask | = 0x000000ff ;
if ( ! green )
mask & = ~ 0x0000ff00 ;
else
mask | = 0x0000ff00 ;
if ( ! blue )
mask & = ~ 0x00ff0000 ;
else
mask | = 0x00ff0000 ;
if ( ! alpha )
mask & = ~ 0xff000000 ;
else
mask | = 0xff000000 ;
options . color_mask = mask ;
m_rasterizer . set_options ( options ) ;
}
2021-08-17 09:00:39 -03:00
void SoftwareGLContext : : gl_polygon_mode ( GLenum face , GLenum mode )
{
RETURN_WITH_ERROR_IF ( ! ( face = = GL_BACK | | face = = GL_FRONT | | face = = GL_FRONT_AND_BACK ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! ( mode = = GL_POINT | | mode = = GL_LINE | | mode = = GL_FILL ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
auto options = m_rasterizer . options ( ) ;
2021-12-22 19:51:28 -03:00
// FIXME: This must support different polygon modes for front- and backside
switch ( mode ) {
case GL_POINT :
options . polygon_mode = SoftGPU : : PolygonMode : : Point ;
break ;
case GL_LINE :
options . polygon_mode = SoftGPU : : PolygonMode : : Line ;
break ;
case GL_FILL :
options . polygon_mode = SoftGPU : : PolygonMode : : Fill ;
break ;
}
2021-08-17 09:00:39 -03:00
m_rasterizer . set_options ( options ) ;
}
2021-08-31 15:50:01 -03:00
void SoftwareGLContext : : gl_polygon_offset ( GLfloat factor , GLfloat units )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_polygon_offset , factor , units ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
auto rasterizer_options = m_rasterizer . options ( ) ;
rasterizer_options . depth_offset_factor = factor ;
rasterizer_options . depth_offset_constant = units ;
m_rasterizer . set_options ( rasterizer_options ) ;
}
2021-08-24 12:10:19 -03:00
void SoftwareGLContext : : gl_fogfv ( GLenum pname , GLfloat * params )
{
2021-12-29 19:57:29 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_fogfv , pname , params ) ;
2021-08-24 12:10:19 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
auto options = m_rasterizer . options ( ) ;
switch ( pname ) {
case GL_FOG_COLOR :
2021-12-29 19:57:29 -03:00
options . fog_color = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
2021-08-24 12:10:19 -03:00
break ;
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
m_rasterizer . set_options ( options ) ;
}
2021-08-24 12:17:28 -03:00
void SoftwareGLContext : : gl_fogf ( GLenum pname , GLfloat param )
{
2021-12-29 19:57:29 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_fogf , pname , param ) ;
2021-08-24 12:17:28 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( param < 0.0f , GL_INVALID_VALUE ) ;
auto options = m_rasterizer . options ( ) ;
switch ( pname ) {
case GL_FOG_DENSITY :
options . fog_density = param ;
break ;
2021-12-29 19:57:29 -03:00
case GL_FOG_END :
options . fog_end = param ;
break ;
case GL_FOG_START :
options . fog_start = param ;
break ;
2021-08-24 12:17:28 -03:00
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
m_rasterizer . set_options ( options ) ;
}
2021-08-24 12:21:54 -03:00
void SoftwareGLContext : : gl_fogi ( GLenum pname , GLint param )
{
2021-12-29 19:57:29 -03:00
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_fogi , pname , param ) ;
2021-08-24 12:21:54 -03:00
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2021-12-29 19:57:29 -03:00
RETURN_WITH_ERROR_IF ( param ! = GL_LINEAR & & param ! = GL_EXP & & param ! = GL_EXP2 , GL_INVALID_ENUM ) ;
2021-08-24 12:21:54 -03:00
auto options = m_rasterizer . options ( ) ;
switch ( pname ) {
case GL_FOG_MODE :
2021-12-22 19:44:29 -03:00
switch ( param ) {
case GL_LINEAR :
options . fog_mode = SoftGPU : : FogMode : : Linear ;
break ;
case GL_EXP :
options . fog_mode = SoftGPU : : FogMode : : Exp ;
break ;
case GL_EXP2 :
options . fog_mode = SoftGPU : : FogMode : : Exp2 ;
break ;
}
2021-08-24 12:21:54 -03:00
break ;
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
}
m_rasterizer . set_options ( options ) ;
}
2021-11-27 21:48:21 -03:00
void SoftwareGLContext : : gl_pixel_storei ( GLenum pname , GLint param )
2021-08-21 09:49:27 -03:00
{
// FIXME: Implement missing parameters
switch ( pname ) {
2021-11-27 21:48:21 -03:00
case GL_PACK_ALIGNMENT :
RETURN_WITH_ERROR_IF ( param ! = 1 & & param ! = 2 & & param ! = 4 & & param ! = 8 , GL_INVALID_VALUE ) ;
m_pack_alignment = param ;
break ;
2021-08-21 09:49:27 -03:00
case GL_UNPACK_ROW_LENGTH :
RETURN_WITH_ERROR_IF ( param < 0 , GL_INVALID_VALUE ) ;
m_unpack_row_length = static_cast < size_t > ( param ) ;
break ;
2021-11-27 21:48:21 -03:00
case GL_UNPACK_ALIGNMENT :
RETURN_WITH_ERROR_IF ( param ! = 1 & & param ! = 2 & & param ! = 4 & & param ! = 8 , GL_INVALID_VALUE ) ;
m_unpack_alignment = param ;
break ;
2021-08-21 09:49:27 -03:00
default :
RETURN_WITH_ERROR_IF ( true , GL_INVALID_ENUM ) ;
break ;
}
}
2021-11-27 15:00:16 -03:00
void SoftwareGLContext : : gl_scissor ( GLint x , GLint y , GLsizei width , GLsizei height )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_scissor , x , y , width , height ) ;
RETURN_WITH_ERROR_IF ( width < 0 | | height < 0 , GL_INVALID_VALUE ) ;
auto options = m_rasterizer . options ( ) ;
options . scissor_box = { x , y , width , height } ;
m_rasterizer . set_options ( options ) ;
}
2021-12-01 11:21:00 -03:00
void SoftwareGLContext : : gl_stencil_func_separate ( GLenum face , GLenum func , GLint ref , GLuint mask )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_stencil_func_separate , face , func , ref , mask ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( face = = GL_FRONT | | face = = GL_BACK | | face = = GL_FRONT_AND_BACK ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! ( func = = GL_NEVER
| | func = = GL_LESS
| | func = = GL_LEQUAL
| | func = = GL_GREATER
| | func = = GL_GEQUAL
| | func = = GL_EQUAL
| | func = = GL_NOTEQUAL
| | func = = GL_ALWAYS ) ,
GL_INVALID_ENUM ) ;
2022-01-16 18:48:46 -03:00
ref = clamp ( ref , 0 , ( 1 < < m_device_info . stencil_bits ) - 1 ) ;
2021-12-01 11:21:00 -03:00
StencilFunctionOptions new_options = { func , ref , mask } ;
if ( face = = GL_FRONT | | face = = GL_FRONT_AND_BACK )
2022-01-15 22:13:07 -03:00
m_stencil_function [ Face : : Front ] = new_options ;
2021-12-01 11:21:00 -03:00
if ( face = = GL_BACK | | face = = GL_FRONT_AND_BACK )
2022-01-15 22:13:07 -03:00
m_stencil_function [ Face : : Back ] = new_options ;
2022-01-16 18:48:46 -03:00
m_stencil_configuration_dirty = true ;
}
void SoftwareGLContext : : gl_stencil_mask_separate ( GLenum face , GLuint mask )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_stencil_mask_separate , face , mask ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
if ( face = = GL_FRONT | | face = = GL_FRONT_AND_BACK )
m_stencil_operation [ Face : : Front ] . write_mask = mask ;
if ( face = = GL_BACK | | face = = GL_FRONT_AND_BACK )
m_stencil_operation [ Face : : Back ] . write_mask = mask ;
m_stencil_configuration_dirty = true ;
2021-12-01 11:21:00 -03:00
}
void SoftwareGLContext : : gl_stencil_op_separate ( GLenum face , GLenum sfail , GLenum dpfail , GLenum dppass )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_stencil_op_separate , face , sfail , dpfail , dppass ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( ! ( face = = GL_FRONT | | face = = GL_BACK | | face = = GL_FRONT_AND_BACK ) , GL_INVALID_ENUM ) ;
2022-01-16 18:48:46 -03:00
auto is_valid_op = [ ] ( GLenum op ) - > bool {
return op = = GL_KEEP | | op = = GL_ZERO | | op = = GL_REPLACE | | op = = GL_INCR | | op = = GL_INCR_WRAP
| | op = = GL_DECR | | op = = GL_DECR_WRAP | | op = = GL_INVERT ;
} ;
RETURN_WITH_ERROR_IF ( ! is_valid_op ( sfail ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! is_valid_op ( dpfail ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! is_valid_op ( dppass ) , GL_INVALID_ENUM ) ;
auto update_stencil_operation = [ & ] ( Face face , GLenum sfail , GLenum dpfail , GLenum dppass ) {
auto & stencil_operation = m_stencil_operation [ face ] ;
stencil_operation . op_fail = sfail ;
stencil_operation . op_depth_fail = dpfail ;
stencil_operation . op_pass = dppass ;
} ;
2021-12-01 11:21:00 -03:00
if ( face = = GL_FRONT | | face = = GL_FRONT_AND_BACK )
2022-01-16 18:48:46 -03:00
update_stencil_operation ( Face : : Front , sfail , dpfail , dppass ) ;
2021-12-01 11:21:00 -03:00
if ( face = = GL_BACK | | face = = GL_FRONT_AND_BACK )
2022-01-16 18:48:46 -03:00
update_stencil_operation ( Face : : Back , sfail , dpfail , dppass ) ;
m_stencil_configuration_dirty = true ;
2021-12-01 11:21:00 -03:00
}
2021-12-01 11:33:23 -03:00
void SoftwareGLContext : : gl_normal ( GLfloat nx , GLfloat ny , GLfloat nz )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_normal , nx , ny , nz ) ;
m_current_vertex_normal = { nx , ny , nz } ;
}
2021-12-24 10:59:24 -03:00
void SoftwareGLContext : : gl_normal_pointer ( GLenum type , GLsizei stride , void const * pointer )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( type ! = GL_BYTE
& & type ! = GL_SHORT
& & type ! = GL_INT
& & type ! = GL_FLOAT
& & type ! = GL_DOUBLE ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( stride < 0 , GL_INVALID_VALUE ) ;
dbgln_if ( GL_DEBUG , " gl_normal_pointer({:#x}, {}, {:p}): unimplemented " , type , stride , pointer ) ;
}
2021-12-01 11:53:55 -03:00
void SoftwareGLContext : : gl_raster_pos ( GLfloat x , GLfloat y , GLfloat z , GLfloat w )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_raster_pos , x , y , z , w ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-12 13:07:53 -03:00
m_rasterizer . set_raster_position ( { x , y , z , w } , m_model_view_matrix , m_projection_matrix ) ;
2021-12-01 11:53:55 -03:00
}
2021-12-01 12:51:40 -03:00
void SoftwareGLContext : : gl_line_width ( GLfloat width )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_line_width , width ) ;
RETURN_WITH_ERROR_IF ( width < = 0 , GL_INVALID_VALUE ) ;
m_line_width = width ;
}
2021-12-01 12:59:30 -03:00
void SoftwareGLContext : : gl_push_attrib ( GLbitfield mask )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_push_attrib , mask ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: implement
dbgln_if ( GL_DEBUG , " SoftwareGLContext FIXME: implement gl_push_attrib({}) " , mask ) ;
}
void SoftwareGLContext : : gl_pop_attrib ( )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_pop_attrib ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: implement
dbgln_if ( GL_DEBUG , " SoftwareGLContext FIXME: implement gl_pop_attrib() " ) ;
}
2021-12-01 13:09:22 -03:00
void SoftwareGLContext : : gl_light_model ( GLenum pname , GLfloat x , GLfloat y , GLfloat z , GLfloat w )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_light_model , pname , x , y , z , w ) ;
RETURN_WITH_ERROR_IF ( ! ( pname = = GL_LIGHT_MODEL_AMBIENT
| | pname = = GL_LIGHT_MODEL_TWO_SIDE ) ,
GL_INVALID_ENUM ) ;
2022-01-08 10:40:39 -03:00
auto lighting_params = m_rasterizer . light_model ( ) ;
bool update_lighting_model = false ;
2021-12-01 13:09:22 -03:00
switch ( pname ) {
case GL_LIGHT_MODEL_AMBIENT :
2022-01-08 10:40:39 -03:00
lighting_params . scene_ambient_color = { x , y , z , w } ;
update_lighting_model = true ;
2021-12-01 13:09:22 -03:00
break ;
case GL_LIGHT_MODEL_TWO_SIDE :
VERIFY ( y = = 0.0f & & z = = 0.0f & & w = = 0.0f ) ;
2022-01-08 10:40:39 -03:00
lighting_params . two_sided_lighting = x ;
update_lighting_model = true ;
2021-12-01 13:09:22 -03:00
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2022-01-08 10:40:39 -03:00
if ( update_lighting_model )
m_rasterizer . set_light_model_params ( lighting_params ) ;
2021-12-01 13:09:22 -03:00
}
2021-12-01 13:19:22 -03:00
void SoftwareGLContext : : gl_bitmap ( GLsizei width , GLsizei height , GLfloat xorig , GLfloat yorig , GLfloat xmove , GLfloat ymove , GLubyte const * bitmap )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_bitmap , width , height , xorig , yorig , xmove , ymove , bitmap ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-12 13:07:53 -03:00
if ( bitmap ! = nullptr ) {
// FIXME: implement
dbgln_if ( GL_DEBUG , " gl_bitmap({}, {}, {}, {}, {}, {}, {}): unimplemented " , width , height , xorig , yorig , xmove , ymove , bitmap ) ;
}
auto raster_position = m_rasterizer . raster_position ( ) ;
raster_position . window_coordinates + = { xmove , ymove , 0.f , 0.f } ;
m_rasterizer . set_raster_position ( raster_position ) ;
2021-12-01 13:19:22 -03:00
}
2021-12-01 13:58:49 -03:00
void SoftwareGLContext : : gl_copy_tex_image_2d ( GLenum target , GLint level , GLenum internalformat , GLint x , GLint y , GLsizei width , GLsizei height , GLint border )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_copy_tex_image_2d , target , level , internalformat , x , y , width , height , border ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: implement
dbgln_if ( GL_DEBUG , " SoftwareGLContext FIXME: implement gl_copy_tex_image_2d({:#x}, {}, {:#x}, {}, {}, {}, {}, {}) " ,
target , level , internalformat , x , y , width , height , border ) ;
}
2021-12-24 10:55:14 -03:00
void SoftwareGLContext : : gl_get_tex_parameter_integerv ( GLenum target , GLint level , GLenum pname , GLint * params )
{
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
// FIXME: support targets other than GL_TEXTURE_2D
RETURN_WITH_ERROR_IF ( target ! = GL_TEXTURE_2D , GL_INVALID_ENUM ) ;
// FIXME: support other parameter names
RETURN_WITH_ERROR_IF ( pname < GL_TEXTURE_WIDTH | | pname > GL_TEXTURE_HEIGHT , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( level < 0 | | level > Texture2D : : LOG2_MAX_TEXTURE_SIZE , GL_INVALID_VALUE ) ;
// FIXME: GL_INVALID_VALUE is generated if target is GL_TEXTURE_BUFFER and level is not zero
// FIXME: GL_INVALID_OPERATION is generated if GL_TEXTURE_COMPRESSED_IMAGE_SIZE is queried on texture images with an uncompressed internal format or on proxy targets
switch ( pname ) {
case GL_TEXTURE_HEIGHT :
* params = m_active_texture_unit - > bound_texture_2d ( ) - > height_at_lod ( level ) ;
break ;
case GL_TEXTURE_WIDTH :
* params = m_active_texture_unit - > bound_texture_2d ( ) - > width_at_lod ( level ) ;
break ;
}
}
2021-12-24 10:53:32 -03:00
void SoftwareGLContext : : gl_rect ( GLdouble x1 , GLdouble y1 , GLdouble x2 , GLdouble y2 )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_rect , x1 , y1 , x2 , y2 ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
gl_begin ( GL_POLYGON ) ;
gl_vertex ( x1 , y1 , 0.0 , 0.0 ) ;
gl_vertex ( x2 , y1 , 0.0 , 0.0 ) ;
gl_vertex ( x2 , y2 , 0.0 , 0.0 ) ;
gl_vertex ( x1 , y2 , 0.0 , 0.0 ) ;
gl_end ( ) ;
}
2021-12-29 20:38:22 -03:00
void SoftwareGLContext : : gl_tex_gen ( GLenum coord , GLenum pname , GLint param )
2021-12-24 10:59:24 -03:00
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_tex_gen , coord , pname , param ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( coord < GL_S | | coord > GL_Q , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( pname ! = GL_TEXTURE_GEN_MODE , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( param ! = GL_EYE_LINEAR
& & param ! = GL_OBJECT_LINEAR
& & param ! = GL_SPHERE_MAP
& & param ! = GL_NORMAL_MAP
& & param ! = GL_REFLECTION_MAP ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ( coord = = GL_R | | coord = = GL_Q ) & & param = = GL_SPHERE_MAP , GL_INVALID_ENUM ) ;
2021-12-29 20:56:41 -03:00
RETURN_WITH_ERROR_IF ( coord = = GL_Q & & ( param = = GL_REFLECTION_MAP | | param = = GL_NORMAL_MAP ) , GL_INVALID_ENUM ) ;
2021-12-24 10:59:24 -03:00
2021-12-29 20:56:41 -03:00
GLenum const capability = GL_TEXTURE_GEN_S + ( coord - GL_S ) ;
texture_coordinate_generation ( capability ) . generation_mode = param ;
m_texcoord_generation_dirty = true ;
2021-12-24 10:59:24 -03:00
}
void SoftwareGLContext : : gl_tex_gen_floatv ( GLenum coord , GLenum pname , GLfloat const * params )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_tex_gen_floatv , coord , pname , params ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( coord < GL_S | | coord > GL_Q , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( pname ! = GL_TEXTURE_GEN_MODE
& & pname ! = GL_OBJECT_PLANE
& & pname ! = GL_EYE_PLANE ,
GL_INVALID_ENUM ) ;
2021-12-29 20:56:41 -03:00
GLenum const capability = GL_TEXTURE_GEN_S + ( coord - GL_S ) ;
2021-12-24 10:59:24 -03:00
switch ( pname ) {
case GL_TEXTURE_GEN_MODE : {
auto param = static_cast < GLenum > ( params [ 0 ] ) ;
RETURN_WITH_ERROR_IF ( param ! = GL_EYE_LINEAR
& & param ! = GL_OBJECT_LINEAR
& & param ! = GL_SPHERE_MAP
& & param ! = GL_NORMAL_MAP
& & param ! = GL_REFLECTION_MAP ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ( coord = = GL_R | | coord = = GL_Q ) & & param = = GL_SPHERE_MAP , GL_INVALID_ENUM ) ;
2021-12-29 20:56:41 -03:00
RETURN_WITH_ERROR_IF ( coord = = GL_Q & & ( param = = GL_REFLECTION_MAP | | param = = GL_NORMAL_MAP ) , GL_INVALID_ENUM ) ;
2021-12-24 10:59:24 -03:00
2021-12-29 20:56:41 -03:00
texture_coordinate_generation ( capability ) . generation_mode = param ;
2021-12-24 10:59:24 -03:00
break ;
}
case GL_OBJECT_PLANE :
2021-12-29 20:56:41 -03:00
texture_coordinate_generation ( capability ) . object_plane_coefficients = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
2021-12-24 10:59:24 -03:00
case GL_EYE_PLANE : {
2022-01-11 20:54:14 -03:00
auto const & inverse_model_view = m_model_view_matrix . inverse ( ) ;
2021-12-29 20:56:41 -03:00
auto input_coefficients = FloatVector4 { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
2021-12-24 10:59:24 -03:00
2021-12-29 20:56:41 -03:00
// Note: we are allowed to store transformed coefficients here, according to the documentation on
// `glGetTexGen`:
//
// "The returned values are those maintained in eye coordinates. They are not equal to the values
// specified using glTexGen, unless the modelview matrix was identity when glTexGen was called."
2022-01-11 20:54:14 -03:00
texture_coordinate_generation ( capability ) . eye_plane_coefficients = inverse_model_view * input_coefficients ;
2021-12-24 10:59:24 -03:00
break ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
2021-12-29 20:56:41 -03:00
m_texcoord_generation_dirty = true ;
2021-12-24 10:59:24 -03:00
}
2021-04-23 20:57:01 -03:00
void SoftwareGLContext : : present ( )
{
m_rasterizer . blit_to ( * m_frontbuffer ) ;
}
2021-12-22 18:02:57 -03:00
void SoftwareGLContext : : sync_device_config ( )
{
sync_device_sampler_config ( ) ;
2021-12-29 20:56:41 -03:00
sync_device_texcoord_config ( ) ;
2022-01-07 11:41:46 -03:00
sync_light_state ( ) ;
2022-01-16 18:48:46 -03:00
sync_stencil_configuration ( ) ;
2021-12-22 18:02:57 -03:00
}
void SoftwareGLContext : : sync_device_sampler_config ( )
{
if ( ! m_sampler_config_is_dirty )
return ;
m_sampler_config_is_dirty = false ;
for ( unsigned i = 0 ; i < m_texture_units . size ( ) ; + + i ) {
SoftGPU : : SamplerConfig config ;
if ( ! m_texture_units [ i ] . texture_2d_enabled ( ) )
continue ;
auto texture = m_texture_units [ i ] . bound_texture_2d ( ) ;
config . bound_image = texture . is_null ( ) ? nullptr : texture - > device_image ( ) ;
auto const & sampler = texture - > sampler ( ) ;
switch ( sampler . min_filter ( ) ) {
case GL_NEAREST :
config . texture_min_filter = SoftGPU : : TextureFilter : : Nearest ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : None ;
break ;
case GL_LINEAR :
config . texture_min_filter = SoftGPU : : TextureFilter : : Linear ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : None ;
break ;
case GL_NEAREST_MIPMAP_NEAREST :
config . texture_min_filter = SoftGPU : : TextureFilter : : Nearest ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : Nearest ;
break ;
2021-12-25 08:30:36 -03:00
case GL_LINEAR_MIPMAP_NEAREST :
config . texture_min_filter = SoftGPU : : TextureFilter : : Nearest ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : Linear ;
break ;
2021-12-22 18:02:57 -03:00
case GL_NEAREST_MIPMAP_LINEAR :
config . texture_min_filter = SoftGPU : : TextureFilter : : Linear ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : Nearest ;
break ;
case GL_LINEAR_MIPMAP_LINEAR :
config . texture_min_filter = SoftGPU : : TextureFilter : : Linear ;
config . mipmap_filter = SoftGPU : : MipMapFilter : : Linear ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( sampler . mag_filter ( ) ) {
case GL_NEAREST :
config . texture_mag_filter = SoftGPU : : TextureFilter : : Nearest ;
break ;
case GL_LINEAR :
config . texture_mag_filter = SoftGPU : : TextureFilter : : Linear ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( sampler . wrap_s_mode ( ) ) {
case GL_CLAMP :
config . texture_wrap_u = SoftGPU : : TextureWrapMode : : Clamp ;
break ;
case GL_CLAMP_TO_BORDER :
config . texture_wrap_u = SoftGPU : : TextureWrapMode : : ClampToBorder ;
break ;
case GL_CLAMP_TO_EDGE :
config . texture_wrap_u = SoftGPU : : TextureWrapMode : : ClampToEdge ;
break ;
case GL_REPEAT :
config . texture_wrap_u = SoftGPU : : TextureWrapMode : : Repeat ;
break ;
case GL_MIRRORED_REPEAT :
config . texture_wrap_u = SoftGPU : : TextureWrapMode : : MirroredRepeat ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( sampler . wrap_t_mode ( ) ) {
case GL_CLAMP :
config . texture_wrap_v = SoftGPU : : TextureWrapMode : : Clamp ;
break ;
case GL_CLAMP_TO_BORDER :
config . texture_wrap_v = SoftGPU : : TextureWrapMode : : ClampToBorder ;
break ;
case GL_CLAMP_TO_EDGE :
config . texture_wrap_v = SoftGPU : : TextureWrapMode : : ClampToEdge ;
break ;
case GL_REPEAT :
config . texture_wrap_v = SoftGPU : : TextureWrapMode : : Repeat ;
break ;
case GL_MIRRORED_REPEAT :
config . texture_wrap_v = SoftGPU : : TextureWrapMode : : MirroredRepeat ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( m_texture_units [ i ] . env_mode ( ) ) {
case GL_MODULATE :
config . fixed_function_texture_env_mode = SoftGPU : : TextureEnvMode : : Modulate ;
break ;
case GL_REPLACE :
config . fixed_function_texture_env_mode = SoftGPU : : TextureEnvMode : : Replace ;
break ;
case GL_DECAL :
config . fixed_function_texture_env_mode = SoftGPU : : TextureEnvMode : : Decal ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
m_rasterizer . set_sampler_config ( i , config ) ;
}
}
2022-01-07 11:41:46 -03:00
void SoftwareGLContext : : sync_light_state ( )
{
if ( ! m_light_state_is_dirty )
return ;
m_light_state_is_dirty = false ;
2022-01-12 23:03:35 -03:00
auto options = m_rasterizer . options ( ) ;
options . color_material_enabled = m_color_material_enabled ;
switch ( m_color_material_face ) {
case GL_BACK :
options . color_material_face = SoftGPU : : ColorMaterialFace : : Back ;
break ;
case GL_FRONT :
options . color_material_face = SoftGPU : : ColorMaterialFace : : Front ;
break ;
case GL_FRONT_AND_BACK :
options . color_material_face = SoftGPU : : ColorMaterialFace : : FrontAndBack ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( m_color_material_mode ) {
case GL_AMBIENT :
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Ambient ;
break ;
case GL_AMBIENT_AND_DIFFUSE :
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Ambient ;
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Diffuse ;
break ;
case GL_DIFFUSE :
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Diffuse ;
break ;
case GL_EMISSION :
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Emissive ;
break ;
case GL_SPECULAR :
options . color_material_mode = SoftGPU : : ColorMaterialMode : : Specular ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
m_rasterizer . set_options ( options ) ;
2022-01-07 11:41:46 -03:00
for ( auto light_id = 0u ; light_id < SoftGPU : : NUM_LIGHTS ; light_id + + ) {
SoftGPU : : Light light ;
auto const & current_light_state = m_light_states . at ( light_id ) ;
light . is_enabled = current_light_state . is_enabled ;
light . ambient_intensity = current_light_state . ambient_intensity ;
light . diffuse_intensity = current_light_state . diffuse_intensity ;
light . specular_intensity = current_light_state . specular_intensity ;
light . position = current_light_state . position ;
light . spotlight_direction = current_light_state . spotlight_direction ;
light . spotlight_exponent = current_light_state . spotlight_exponent ;
light . spotlight_cutoff_angle = current_light_state . spotlight_cutoff_angle ;
light . constant_attenuation = current_light_state . constant_attenuation ;
light . linear_attenuation = current_light_state . linear_attenuation ;
light . quadratic_attenuation = current_light_state . quadratic_attenuation ;
m_rasterizer . set_light_state ( light_id , light ) ;
}
2022-01-08 03:46:58 -03:00
2022-01-15 22:09:27 -03:00
auto update_material_state = [ & ] ( SoftGPU : : Face face , SoftGPU : : Material const & current_material_state ) {
2022-01-08 03:46:58 -03:00
SoftGPU : : Material material ;
material . ambient = current_material_state . ambient ;
material . diffuse = current_material_state . diffuse ;
material . specular = current_material_state . specular ;
material . emissive = current_material_state . emissive ;
material . shininess = current_material_state . shininess ;
material . specular_exponent = current_material_state . specular_exponent ;
material . ambient_color_index = current_material_state . ambient_color_index ;
material . diffuse_color_index = current_material_state . diffuse_color_index ;
material . specular_color_index = current_material_state . specular_color_index ;
2022-01-15 22:09:27 -03:00
m_rasterizer . set_material_state ( face , material ) ;
} ;
update_material_state ( SoftGPU : : Face : : Front , m_material_states [ Face : : Front ] ) ;
update_material_state ( SoftGPU : : Face : : Back , m_material_states [ Face : : Back ] ) ;
2022-01-07 11:41:46 -03:00
}
2021-12-29 20:56:41 -03:00
void SoftwareGLContext : : sync_device_texcoord_config ( )
{
if ( ! m_texcoord_generation_dirty )
return ;
m_texcoord_generation_dirty = false ;
auto options = m_rasterizer . options ( ) ;
u8 enabled_coordinates = SoftGPU : : TexCoordGenerationCoordinate : : None ;
for ( GLenum capability = GL_TEXTURE_GEN_S ; capability < = GL_TEXTURE_GEN_Q ; + + capability ) {
auto const context_coordinate_config = texture_coordinate_generation ( capability ) ;
if ( ! context_coordinate_config . enabled )
continue ;
SoftGPU : : TexCoordGenerationConfig * texcoord_generation_config ;
switch ( capability ) {
case GL_TEXTURE_GEN_S :
enabled_coordinates | = SoftGPU : : TexCoordGenerationCoordinate : : S ;
texcoord_generation_config = & options . texcoord_generation_config [ 0 ] ;
break ;
case GL_TEXTURE_GEN_T :
enabled_coordinates | = SoftGPU : : TexCoordGenerationCoordinate : : T ;
texcoord_generation_config = & options . texcoord_generation_config [ 1 ] ;
break ;
case GL_TEXTURE_GEN_R :
enabled_coordinates | = SoftGPU : : TexCoordGenerationCoordinate : : R ;
texcoord_generation_config = & options . texcoord_generation_config [ 2 ] ;
break ;
case GL_TEXTURE_GEN_Q :
enabled_coordinates | = SoftGPU : : TexCoordGenerationCoordinate : : Q ;
texcoord_generation_config = & options . texcoord_generation_config [ 3 ] ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
switch ( context_coordinate_config . generation_mode ) {
case GL_OBJECT_LINEAR :
texcoord_generation_config - > mode = SoftGPU : : TexCoordGenerationMode : : ObjectLinear ;
texcoord_generation_config - > coefficients = context_coordinate_config . object_plane_coefficients ;
break ;
case GL_EYE_LINEAR :
texcoord_generation_config - > mode = SoftGPU : : TexCoordGenerationMode : : EyeLinear ;
texcoord_generation_config - > coefficients = context_coordinate_config . eye_plane_coefficients ;
break ;
case GL_SPHERE_MAP :
texcoord_generation_config - > mode = SoftGPU : : TexCoordGenerationMode : : SphereMap ;
break ;
case GL_REFLECTION_MAP :
texcoord_generation_config - > mode = SoftGPU : : TexCoordGenerationMode : : ReflectionMap ;
break ;
case GL_NORMAL_MAP :
texcoord_generation_config - > mode = SoftGPU : : TexCoordGenerationMode : : NormalMap ;
break ;
}
}
options . texcoord_generation_enabled_coordinates = enabled_coordinates ;
m_rasterizer . set_options ( options ) ;
}
2022-01-16 18:48:46 -03:00
void SoftwareGLContext : : sync_stencil_configuration ( )
{
if ( ! m_stencil_configuration_dirty )
return ;
m_stencil_configuration_dirty = false ;
auto set_device_stencil = [ & ] ( SoftGPU : : Face face , StencilFunctionOptions func , StencilOperationOptions op ) {
SoftGPU : : StencilConfiguration device_configuration ;
// Stencil test function
auto map_func = [ ] ( GLenum func ) - > SoftGPU : : StencilTestFunction {
switch ( func ) {
case GL_ALWAYS :
return SoftGPU : : StencilTestFunction : : Always ;
case GL_EQUAL :
return SoftGPU : : StencilTestFunction : : Equal ;
case GL_GEQUAL :
return SoftGPU : : StencilTestFunction : : GreaterOrEqual ;
case GL_GREATER :
return SoftGPU : : StencilTestFunction : : Greater ;
case GL_LESS :
return SoftGPU : : StencilTestFunction : : Less ;
case GL_LEQUAL :
return SoftGPU : : StencilTestFunction : : LessOrEqual ;
case GL_NEVER :
return SoftGPU : : StencilTestFunction : : Never ;
case GL_NOTEQUAL :
return SoftGPU : : StencilTestFunction : : NotEqual ;
}
VERIFY_NOT_REACHED ( ) ;
} ;
device_configuration . test_function = map_func ( func . func ) ;
device_configuration . reference_value = func . reference_value ;
device_configuration . test_mask = func . mask ;
// Stencil operation
auto map_operation = [ ] ( GLenum operation ) - > SoftGPU : : StencilOperation {
switch ( operation ) {
case GL_DECR :
return SoftGPU : : StencilOperation : : Decrement ;
case GL_DECR_WRAP :
return SoftGPU : : StencilOperation : : DecrementWrap ;
case GL_INCR :
return SoftGPU : : StencilOperation : : Increment ;
case GL_INCR_WRAP :
return SoftGPU : : StencilOperation : : IncrementWrap ;
case GL_INVERT :
return SoftGPU : : StencilOperation : : Invert ;
case GL_KEEP :
return SoftGPU : : StencilOperation : : Keep ;
case GL_REPLACE :
return SoftGPU : : StencilOperation : : Replace ;
case GL_ZERO :
return SoftGPU : : StencilOperation : : Zero ;
}
VERIFY_NOT_REACHED ( ) ;
} ;
device_configuration . on_stencil_test_fail = map_operation ( op . op_fail ) ;
device_configuration . on_depth_test_fail = map_operation ( op . op_depth_fail ) ;
device_configuration . on_pass = map_operation ( op . op_pass ) ;
device_configuration . write_mask = op . write_mask ;
m_rasterizer . set_stencil_configuration ( face , device_configuration ) ;
} ;
set_device_stencil ( SoftGPU : : Face : : Front , m_stencil_function [ Face : : Front ] , m_stencil_operation [ Face : : Front ] ) ;
set_device_stencil ( SoftGPU : : Face : : Back , m_stencil_function [ Face : : Back ] , m_stencil_operation [ Face : : Back ] ) ;
}
2022-01-07 11:41:46 -03:00
void SoftwareGLContext : : gl_lightf ( GLenum light , GLenum pname , GLfloat param )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_lightf , light , pname , param ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-16 18:51:53 -03:00
RETURN_WITH_ERROR_IF ( light < GL_LIGHT0 | | light > = ( GL_LIGHT0 + m_device_info . num_lights ) , GL_INVALID_ENUM ) ;
2022-01-07 11:41:46 -03:00
RETURN_WITH_ERROR_IF ( ! ( pname = = GL_CONSTANT_ATTENUATION | | pname = = GL_LINEAR_ATTENUATION | | pname = = GL_QUADRATIC_ATTENUATION | | pname ! = GL_SPOT_EXPONENT | | pname ! = GL_SPOT_CUTOFF ) , GL_INVALID_ENUM ) ;
auto & light_state = m_light_states . at ( light - GL_LIGHT0 ) ;
switch ( pname ) {
case GL_CONSTANT_ATTENUATION :
light_state . constant_attenuation = param ;
break ;
case GL_LINEAR_ATTENUATION :
light_state . linear_attenuation = param ;
break ;
case GL_QUADRATIC_ATTENUATION :
light_state . quadratic_attenuation = param ;
break ;
case GL_SPOT_EXPONENT :
light_state . spotlight_exponent = param ;
break ;
case GL_SPOT_CUTOFF :
light_state . spotlight_cutoff_angle = param ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
m_light_state_is_dirty = true ;
}
void SoftwareGLContext : : gl_lightfv ( GLenum light , GLenum pname , GLfloat const * params )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_lightfv , light , pname , params ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
2022-01-16 18:51:53 -03:00
RETURN_WITH_ERROR_IF ( light < GL_LIGHT0 | | light > = ( GL_LIGHT0 + m_device_info . num_lights ) , GL_INVALID_ENUM ) ;
2022-01-07 11:41:46 -03:00
RETURN_WITH_ERROR_IF ( ! ( pname = = GL_AMBIENT | | pname = = GL_DIFFUSE | | pname = = GL_SPECULAR | | pname = = GL_POSITION | | pname = = GL_CONSTANT_ATTENUATION | | pname = = GL_LINEAR_ATTENUATION | | pname = = GL_QUADRATIC_ATTENUATION | | pname = = GL_SPOT_CUTOFF | | pname = = GL_SPOT_EXPONENT | | pname = = GL_SPOT_DIRECTION ) , GL_INVALID_ENUM ) ;
auto & light_state = m_light_states . at ( light - GL_LIGHT0 ) ;
switch ( pname ) {
case GL_AMBIENT :
light_state . ambient_intensity = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_DIFFUSE :
light_state . diffuse_intensity = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_SPECULAR :
light_state . specular_intensity = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_POSITION :
light_state . position = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
light_state . position = m_model_view_matrix * light_state . position ;
break ;
case GL_CONSTANT_ATTENUATION :
light_state . constant_attenuation = * params ;
break ;
case GL_LINEAR_ATTENUATION :
light_state . linear_attenuation = * params ;
break ;
case GL_QUADRATIC_ATTENUATION :
light_state . quadratic_attenuation = * params ;
break ;
case GL_SPOT_EXPONENT :
light_state . spotlight_exponent = * params ;
break ;
case GL_SPOT_CUTOFF :
light_state . spotlight_cutoff_angle = * params ;
break ;
case GL_SPOT_DIRECTION : {
FloatVector4 direction_vector = { params [ 0 ] , params [ 1 ] , params [ 2 ] , 0.0f } ;
direction_vector = m_model_view_matrix * direction_vector ;
light_state . spotlight_direction = { direction_vector . x ( ) , direction_vector . y ( ) , direction_vector . z ( ) } ;
break ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
m_light_state_is_dirty = true ;
}
2022-01-08 03:46:58 -03:00
void SoftwareGLContext : : gl_materialf ( GLenum face , GLenum pname , GLfloat param )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_materialf , face , pname , param ) ;
RETURN_WITH_ERROR_IF ( ! ( face = = GL_FRONT | | face = = GL_BACK | | face = = GL_FRONT_AND_BACK ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( pname ! = GL_SHININESS , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( param > 128.0f , GL_INVALID_VALUE ) ;
switch ( face ) {
case GL_FRONT :
2022-01-15 22:09:27 -03:00
m_material_states [ Face : : Front ] . shininess = param ;
2022-01-08 03:46:58 -03:00
break ;
case GL_BACK :
2022-01-15 22:09:27 -03:00
m_material_states [ Face : : Back ] . shininess = param ;
2022-01-08 03:46:58 -03:00
break ;
case GL_FRONT_AND_BACK :
2022-01-15 22:09:27 -03:00
m_material_states [ Face : : Front ] . shininess = param ;
m_material_states [ Face : : Back ] . shininess = param ;
2022-01-08 03:46:58 -03:00
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
m_light_state_is_dirty = true ;
}
void SoftwareGLContext : : gl_materialfv ( GLenum face , GLenum pname , GLfloat const * params )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_materialfv , face , pname , params ) ;
RETURN_WITH_ERROR_IF ( ! ( face = = GL_FRONT | | face = = GL_BACK | | face = = GL_FRONT_AND_BACK ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ! ( pname = = GL_AMBIENT | | pname = = GL_DIFFUSE | | pname = = GL_SPECULAR | | pname = = GL_EMISSION | | pname = = GL_SHININESS | | pname = = GL_AMBIENT_AND_DIFFUSE ) , GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( ( pname = = GL_SHININESS & & * params > 128.0f ) , GL_INVALID_VALUE ) ;
auto update_material = [ ] ( SoftGPU : : Material & material , GLenum pname , GLfloat const * params ) {
switch ( pname ) {
case GL_AMBIENT :
material . ambient = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_DIFFUSE :
material . diffuse = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_SPECULAR :
material . specular = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_EMISSION :
material . emissive = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
case GL_SHININESS :
material . shininess = * params ;
break ;
case GL_AMBIENT_AND_DIFFUSE :
material . ambient = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
material . diffuse = { params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] } ;
break ;
}
} ;
switch ( face ) {
case GL_FRONT :
2022-01-15 22:09:27 -03:00
update_material ( m_material_states [ Face : : Front ] , pname , params ) ;
2022-01-08 03:46:58 -03:00
break ;
case GL_BACK :
2022-01-15 22:09:27 -03:00
update_material ( m_material_states [ Face : : Back ] , pname , params ) ;
2022-01-08 03:46:58 -03:00
break ;
case GL_FRONT_AND_BACK :
2022-01-15 22:09:27 -03:00
update_material ( m_material_states [ Face : : Front ] , pname , params ) ;
update_material ( m_material_states [ Face : : Back ] , pname , params ) ;
2022-01-08 03:46:58 -03:00
break ;
}
m_light_state_is_dirty = true ;
}
2022-01-12 23:03:35 -03:00
void SoftwareGLContext : : gl_color_material ( GLenum face , GLenum mode )
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED ( gl_color_material , face , mode ) ;
RETURN_WITH_ERROR_IF ( m_in_draw_state , GL_INVALID_OPERATION ) ;
RETURN_WITH_ERROR_IF ( face ! = GL_FRONT
& & face ! = GL_BACK
& & face ! = GL_FRONT_AND_BACK ,
GL_INVALID_ENUM ) ;
RETURN_WITH_ERROR_IF ( mode ! = GL_EMISSION
& & mode ! = GL_AMBIENT
& & mode ! = GL_DIFFUSE
& & mode ! = GL_SPECULAR
& & mode ! = GL_AMBIENT_AND_DIFFUSE ,
GL_INVALID_ENUM ) ;
m_color_material_face = face ;
m_color_material_mode = mode ;
m_light_state_is_dirty = true ;
}
2021-01-06 08:58:01 -03:00
}