1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 03:57:43 +00:00

LibGL+LibSoftGPU: Implement the stencil buffer

This implements an 8-bit front stencil buffer. Stencil operations are
SIMD optimized. LibGL changes include:

* New `glStencilMask` and `glStencilMaskSeparate` functions
* New context parameter `GL_STENCIL_CLEAR_VALUE`
This commit is contained in:
Jelle Raaijmakers 2022-01-16 22:48:46 +01:00 committed by Andreas Kling
parent 6386671944
commit 11c807ebd1
13 changed files with 430 additions and 77 deletions

View file

@ -165,7 +165,9 @@ Optional<ContextParameter> SoftwareGLContext::get_context_parameter(GLenum name)
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = scissor_enabled } };
}
case GL_STENCIL_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } };
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 } };
case GL_STENCIL_TEST:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_stencil_test_enabled } };
case GL_TEXTURE_1D:
@ -239,9 +241,8 @@ void SoftwareGLContext::gl_clear(GLbitfield mask)
if (mask & GL_DEPTH_BUFFER_BIT)
m_rasterizer.clear_depth(static_cast<float>(m_clear_depth));
// FIXME: implement GL_STENCIL_BUFFER_BIT
if (mask & GL_STENCIL_BUFFER_BIT)
dbgln_if(GL_DEBUG, "gl_clear(): GL_STENCIL_BUFFER_BIT is unimplemented");
m_rasterizer.clear_stencil(m_clear_stencil);
}
void SoftwareGLContext::gl_clear_color(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
@ -268,9 +269,7 @@ void SoftwareGLContext::gl_clear_stencil(GLint s)
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
// FIXME: "s is masked with 2^m - 1 , where m is the number of bits in the stencil buffer"
m_clear_stencil = s;
m_clear_stencil = static_cast<u8>(s & ((1 << m_device_info.stencil_bits) - 1));
}
void SoftwareGLContext::gl_color(GLdouble r, GLdouble g, GLdouble b, GLdouble a)
@ -697,6 +696,8 @@ void SoftwareGLContext::gl_enable(GLenum capability)
break;
case GL_STENCIL_TEST:
m_stencil_test_enabled = true;
rasterizer_options.enable_stencil_test = true;
update_rasterizer_options = true;
break;
case GL_TEXTURE_1D:
m_active_texture_unit->set_texture_1d_enabled(true);
@ -808,6 +809,8 @@ void SoftwareGLContext::gl_disable(GLenum capability)
break;
case GL_STENCIL_TEST:
m_stencil_test_enabled = false;
rasterizer_options.enable_stencil_test = false;
update_rasterizer_options = true;
break;
case GL_TEXTURE_1D:
m_active_texture_unit->set_texture_1d_enabled(false);
@ -2631,13 +2634,28 @@ void SoftwareGLContext::gl_stencil_func_separate(GLenum face, GLenum func, GLint
|| func == GL_ALWAYS),
GL_INVALID_ENUM);
// FIXME: "ref is clamped to the range 02^n - 1 , where n is the number of bitplanes in the stencil buffer"
ref = clamp(ref, 0, (1 << m_device_info.stencil_bits) - 1);
StencilFunctionOptions new_options = { func, ref, mask };
if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
m_stencil_function[Face::Front] = new_options;
if (face == GL_BACK || face == GL_FRONT_AND_BACK)
m_stencil_function[Face::Back] = new_options;
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;
}
void SoftwareGLContext::gl_stencil_op_separate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
@ -2647,39 +2665,26 @@ void SoftwareGLContext::gl_stencil_op_separate(GLenum face, GLenum sfail, GLenum
RETURN_WITH_ERROR_IF(!(face == GL_FRONT || face == GL_BACK || face == GL_FRONT_AND_BACK), GL_INVALID_ENUM);
RETURN_WITH_ERROR_IF(!(sfail == GL_KEEP
|| sfail == GL_ZERO
|| sfail == GL_REPLACE
|| sfail == GL_INCR
|| sfail == GL_INCR_WRAP
|| sfail == GL_DECR
|| sfail == GL_DECR_WRAP
|| sfail == GL_INVERT),
GL_INVALID_ENUM);
RETURN_WITH_ERROR_IF(!(dpfail == GL_KEEP
|| dpfail == GL_ZERO
|| dpfail == GL_REPLACE
|| dpfail == GL_INCR
|| dpfail == GL_INCR_WRAP
|| dpfail == GL_DECR
|| dpfail == GL_DECR_WRAP
|| dpfail == GL_INVERT),
GL_INVALID_ENUM);
RETURN_WITH_ERROR_IF(!(dppass == GL_KEEP
|| dppass == GL_ZERO
|| dppass == GL_REPLACE
|| dppass == GL_INCR
|| dppass == GL_INCR_WRAP
|| dppass == GL_DECR
|| dppass == GL_DECR_WRAP
|| dppass == GL_INVERT),
GL_INVALID_ENUM);
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);
StencilOperationOptions new_options = { sfail, dpfail, dppass };
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;
};
if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
m_stencil_operation[Face::Front] = new_options;
update_stencil_operation(Face::Front, sfail, dpfail, dppass);
if (face == GL_BACK || face == GL_FRONT_AND_BACK)
m_stencil_operation[Face::Back] = new_options;
update_stencil_operation(Face::Back, sfail, dpfail, dppass);
m_stencil_configuration_dirty = true;
}
void SoftwareGLContext::gl_normal(GLfloat nx, GLfloat ny, GLfloat nz)
@ -2908,6 +2913,7 @@ void SoftwareGLContext::sync_device_config()
sync_device_sampler_config();
sync_device_texcoord_config();
sync_light_state();
sync_stencil_configuration();
}
void SoftwareGLContext::sync_device_sampler_config()
@ -3170,6 +3176,74 @@ void SoftwareGLContext::sync_device_texcoord_config()
m_rasterizer.set_options(options);
}
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]);
}
void SoftwareGLContext::gl_lightf(GLenum light, GLenum pname, GLfloat param)
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_lightf, light, pname, param);