GPU: Add GL_ARB_clip_control support to the GL backend

This adds support for the extension and always
set the clip state value to 0..1 to align with vulkan
and metal. Moreover this is needed for the Reverse Z
implementation.

Note that this is a OpenGL 4.5 feature and is not
required to start Blender. So there must still be
a fallback path for now.

Rel #138898

Pull Request: https://projects.blender.org/blender/blender/pulls/138941
This commit is contained in:
Clément Foucault
2025-05-16 13:53:36 +02:00
committed by Clément Foucault
parent 45240026a2
commit 8ac5940e33
12 changed files with 67 additions and 0 deletions

View File

@@ -639,6 +639,13 @@ void ShaderModule::material_create_info_amend(GPUMaterial *gpumat, GPUCodegenOut
}
}
#if 0 /* Waiting for using DRW_STATE_CLIP_CONTROL_UNIT_RANGE where these are used. */
if (geometry_type != MAT_GEOM_WORLD) {
/* Allow to use Reverse-Z on OpenGL. Does nothing in other backend. */
info.builtins(BuiltinBits::CLIP_CONTROL);
}
#endif
std::stringstream global_vars;
switch (geometry_type) {
case MAT_GEOM_MESH:

View File

@@ -305,6 +305,13 @@ void StateSet::execute(RecordingState &recording_state) const
to_stencil_op(new_state),
to_provoking_vertex(new_state));
if (new_state & DRW_STATE_CLIP_CONTROL_UNIT_RANGE) {
GPU_clip_control_unit_range(true);
}
else {
GPU_clip_control_unit_range(false);
}
if (new_state & DRW_STATE_SHADOW_OFFSET) {
GPU_shadow_offset(true);
}

View File

@@ -64,6 +64,8 @@ typedef enum {
DRW_STATE_LOGIC_INVERT = (10 << 11),
DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11),
/* See GPU_clip_control_unit_range. */
DRW_STATE_CLIP_CONTROL_UNIT_RANGE = (1 << 26),
DRW_STATE_IN_FRONT_SELECT = (1 << 27),
DRW_STATE_SHADOW_OFFSET = (1 << 28),
DRW_STATE_CLIP_PLANES = (1 << 29),

View File

@@ -194,6 +194,11 @@ void GPU_stencil_reference_set(uint reference);
void GPU_stencil_write_mask_set(uint write_mask);
void GPU_stencil_compare_mask_set(uint compare_mask);
/* Sets the depth range to be 0..1. Only have effect with the OpenGL backend. Have no effect if
* glClipControl is not supported. Shaders used for drawing with this state must use
* BuiltinBits::CLIP_CONTROL for their vertex shader to be patched. */
void GPU_clip_control_unit_range(bool enable);
eGPUFaceCullTest GPU_face_culling_get();
eGPUBlend GPU_blend_get();
eGPUDepthTest GPU_depth_test_get();

View File

@@ -422,6 +422,10 @@ enum class BuiltinBits {
/* Texture atomics requires usage options to alter compilation flag. */
TEXTURE_ATOMIC = (1 << 18),
/* Enable shader patching on GL to remap clip range to 0..1.
* Will do nothing if ClipControl is unsupporteds. */
CLIP_CONTROL = (1 << 19),
/* Not a builtin but a flag we use to tag shaders that use the debug features. */
USE_PRINTF = (1 << 28),
USE_DEBUG_DRAW = (1 << 29),

View File

@@ -145,6 +145,11 @@ void GPU_state_set(eGPUWriteMask write_mask,
state.provoking_vert = uint32_t(provoking_vert);
}
void GPU_clip_control_unit_range(bool enable)
{
SET_IMMUTABLE_STATE(clip_control, enable);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -423,6 +428,7 @@ StateManager::StateManager()
state.invert_facing = false;
state.shadow_bias = false;
state.clip_distances = 0;
state.clip_control = false;
state.polygon_smooth = false;
state.line_smooth = false;

View File

@@ -40,6 +40,8 @@ union GPUState {
uint32_t logic_op_xor : 1;
uint32_t invert_facing : 1;
uint32_t shadow_bias : 1;
/** Clip range of 0..1 on OpenGL. */
uint32_t clip_control : 1;
/** Number of clip distances enabled. */
/* TODO(fclem): This should be a shader property. */
uint32_t clip_distances : 3;

View File

@@ -413,6 +413,7 @@ static void detect_workarounds()
GLContext::multi_bind_support = false;
GLContext::multi_bind_image_support = false;
/* Turn off OpenGL 4.5 features. */
GLContext::clip_control_support = false;
GLContext::direct_state_access_support = false;
/* Turn off OpenGL 4.6 features. */
GLContext::texture_filter_anisotropic_support = false;
@@ -612,6 +613,7 @@ GLint GLContext::max_ssbo_binds = 0;
/** Extensions. */
bool GLContext::clip_control_support = false;
bool GLContext::debug_layer_support = false;
bool GLContext::direct_state_access_support = false;
bool GLContext::explicit_location_support = false;
@@ -710,6 +712,7 @@ void GLBackend::capabilities_init()
GLContext::stencil_texturing_support = epoxy_gl_version() >= 43;
GLContext::texture_filter_anisotropic_support = epoxy_has_gl_extension(
"GL_EXT_texture_filter_anisotropic");
GLContext::clip_control_support = epoxy_has_gl_extension("GL_ARB_clip_control");
/* Disabled until it is proven to work. */
GLContext::framebuffer_fetch_support = false;

View File

@@ -48,6 +48,7 @@ class GLContext : public Context {
/** Extensions. */
static bool clip_control_support;
static bool debug_layer_support;
static bool direct_state_access_support;
static bool explicit_location_support;

View File

@@ -711,6 +711,13 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con
ss << "#define gpu_ViewportIndex gl_ViewportIndex\n";
}
}
if (bool(info.builtins_ & BuiltinBits::CLIP_CONTROL)) {
if (GLContext::clip_control_support && !has_geometry_stage) {
/* Assume clip range is set to 0..1 and remap the range just like Vulkan and Metal.
* If geometry stage is needed, do that remapping inside the geometry shader stage. */
post_main += "gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n";
}
}
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
if (!GLContext::native_barycentric_support) {
/* Disabled or unsupported. */
@@ -983,6 +990,12 @@ std::string GLShader::workaround_geometry_shader_source_create(
ss << " vec3(" << int(i == 0) << ", " << int(i == 1) << ", " << int(i == 2) << ");\n";
}
ss << " gl_Position = gl_in[" << i << "].gl_Position;\n";
if (bool(info.builtins_ & BuiltinBits::CLIP_CONTROL)) {
if (GLContext::clip_control_support) {
/* Assume clip range is set to 0..1 and remap the range just like Vulkan and Metal. */
ss << "gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n";
}
}
if (do_layer_output) {
ss << " gl_Layer = gpu_Layer[" << i << "];\n";
}

View File

@@ -109,6 +109,9 @@ void GLStateManager::set_state(const GPUState &state)
if (changed.shadow_bias != 0) {
set_shadow_bias(state.shadow_bias);
}
if (changed.clip_control != 0) {
set_clip_control(state.clip_control);
}
/* TODO: remove. */
if (changed.polygon_smooth) {
@@ -330,6 +333,19 @@ void GLStateManager::set_shadow_bias(const bool enable)
}
}
void GLStateManager::set_clip_control(const bool enable)
{
if (GLContext::clip_control_support) {
if (enable) {
/* Match Vulkan and Metal by default. */
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
else {
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
}
}
}
void GLStateManager::set_blend(const eGPUBlend value)
{
/**

View File

@@ -94,6 +94,7 @@ class GLStateManager : public StateManager {
static void set_backface_culling(eGPUFaceCullTest test);
static void set_provoking_vert(eGPUProvokingVertex vert);
static void set_shadow_bias(bool enable);
static void set_clip_control(bool enable);
static void set_blend(eGPUBlend value);
void set_state(const GPUState &state);