Files
test/source/blender/gpu/intern/gpu_state.cc
Clément Foucault 8ac5940e33 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
2025-05-16 13:53:36 +02:00

445 lines
10 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#ifndef GPU_STANDALONE
# include "DNA_userdef_types.h"
# define PIXELSIZE (U.pixelsize)
#else
# define PIXELSIZE (1.0f)
#endif
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "GPU_state.hh"
#include "gpu_backend.hh"
#include "gpu_context_private.hh"
#include "gpu_state_private.hh"
using namespace blender::gpu;
#define SET_STATE(_prefix, _state, _value) \
do { \
StateManager *stack = Context::get()->state_manager; \
auto &state_object = stack->_prefix##state; \
state_object._state = (_value); \
} while (0)
#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value)
#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value)
/* -------------------------------------------------------------------- */
/** \name Immutable state Setters
* \{ */
void GPU_blend(eGPUBlend blend)
{
SET_IMMUTABLE_STATE(blend, blend);
}
void GPU_face_culling(eGPUFaceCullTest culling)
{
SET_IMMUTABLE_STATE(culling_test, culling);
}
eGPUFaceCullTest GPU_face_culling_get()
{
GPUState &state = Context::get()->state_manager->state;
return (eGPUFaceCullTest)state.culling_test;
}
void GPU_front_facing(bool invert)
{
SET_IMMUTABLE_STATE(invert_facing, invert);
}
void GPU_provoking_vertex(eGPUProvokingVertex vert)
{
SET_IMMUTABLE_STATE(provoking_vert, vert);
}
void GPU_depth_test(eGPUDepthTest test)
{
SET_IMMUTABLE_STATE(depth_test, test);
}
void GPU_stencil_test(eGPUStencilTest test)
{
SET_IMMUTABLE_STATE(stencil_test, test);
}
void GPU_line_smooth(bool enable)
{
SET_IMMUTABLE_STATE(line_smooth, enable);
}
void GPU_polygon_smooth(bool enable)
{
SET_IMMUTABLE_STATE(polygon_smooth, enable);
}
void GPU_logic_op_xor_set(bool enable)
{
SET_IMMUTABLE_STATE(logic_op_xor, enable);
}
void GPU_write_mask(eGPUWriteMask mask)
{
SET_IMMUTABLE_STATE(write_mask, mask);
}
void GPU_color_mask(bool r, bool g, bool b, bool a)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->state;
uint32_t write_mask = state.write_mask;
SET_FLAG_FROM_TEST(write_mask, r, uint32_t(GPU_WRITE_RED));
SET_FLAG_FROM_TEST(write_mask, g, uint32_t(GPU_WRITE_GREEN));
SET_FLAG_FROM_TEST(write_mask, b, uint32_t(GPU_WRITE_BLUE));
SET_FLAG_FROM_TEST(write_mask, a, uint32_t(GPU_WRITE_ALPHA));
state.write_mask = write_mask;
}
void GPU_depth_mask(bool depth)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->state;
uint32_t write_mask = state.write_mask;
SET_FLAG_FROM_TEST(write_mask, depth, uint32_t(GPU_WRITE_DEPTH));
state.write_mask = write_mask;
}
void GPU_shadow_offset(bool enable)
{
SET_IMMUTABLE_STATE(shadow_bias, enable);
}
void GPU_clip_distances(int distances_enabled)
{
SET_IMMUTABLE_STATE(clip_distances, distances_enabled);
}
void GPU_state_set(eGPUWriteMask write_mask,
eGPUBlend blend,
eGPUFaceCullTest culling_test,
eGPUDepthTest depth_test,
eGPUStencilTest stencil_test,
eGPUStencilOp stencil_op,
eGPUProvokingVertex provoking_vert)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->state;
state.write_mask = uint32_t(write_mask);
state.blend = uint32_t(blend);
state.culling_test = uint32_t(culling_test);
state.depth_test = uint32_t(depth_test);
state.stencil_test = uint32_t(stencil_test);
state.stencil_op = uint32_t(stencil_op);
state.provoking_vert = uint32_t(provoking_vert);
}
void GPU_clip_control_unit_range(bool enable)
{
SET_IMMUTABLE_STATE(clip_control, enable);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mutable State Setters
* \{ */
void GPU_depth_range(float near, float far)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->mutable_state;
copy_v2_fl2(state.depth_range, near, far);
}
void GPU_line_width(float width)
{
width = max_ff(1.0f, width * PIXELSIZE);
SET_MUTABLE_STATE(line_width, width);
}
void GPU_point_size(float size)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->mutable_state;
/* Keep the sign of point_size since it represents the enable state. */
state.point_size = size * ((state.point_size > 0.0) ? 1.0f : -1.0f);
}
void GPU_program_point_size(bool enable)
{
StateManager *stack = Context::get()->state_manager;
auto &state = stack->mutable_state;
/* Set point size sign negative to disable. */
state.point_size = fabsf(state.point_size) * (enable ? 1 : -1);
}
void GPU_scissor_test(bool enable)
{
Context::get()->active_fb->scissor_test_set(enable);
}
void GPU_scissor(int x, int y, int width, int height)
{
int scissor_rect[4] = {x, y, width, height};
Context::get()->active_fb->scissor_set(scissor_rect);
}
void GPU_viewport(int x, int y, int width, int height)
{
int viewport_rect[4] = {x, y, width, height};
Context::get()->active_fb->viewport_set(viewport_rect);
}
void GPU_stencil_reference_set(uint reference)
{
SET_MUTABLE_STATE(stencil_reference, uint8_t(reference));
}
void GPU_stencil_write_mask_set(uint write_mask)
{
SET_MUTABLE_STATE(stencil_write_mask, uint8_t(write_mask));
}
void GPU_stencil_compare_mask_set(uint compare_mask)
{
SET_MUTABLE_STATE(stencil_compare_mask, uint8_t(compare_mask));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name State Getters
* \{ */
eGPUBlend GPU_blend_get()
{
GPUState &state = Context::get()->state_manager->state;
return (eGPUBlend)state.blend;
}
eGPUWriteMask GPU_write_mask_get()
{
GPUState &state = Context::get()->state_manager->state;
return (eGPUWriteMask)state.write_mask;
}
uint GPU_stencil_mask_get()
{
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.stencil_write_mask;
}
eGPUDepthTest GPU_depth_test_get()
{
GPUState &state = Context::get()->state_manager->state;
return (eGPUDepthTest)state.depth_test;
}
eGPUStencilTest GPU_stencil_test_get()
{
GPUState &state = Context::get()->state_manager->state;
return (eGPUStencilTest)state.stencil_test;
}
float GPU_line_width_get()
{
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.line_width;
}
bool GPU_line_smooth_get()
{
const GPUState &state = Context::get()->state_manager->state;
return bool(state.line_smooth);
}
void GPU_scissor_get(int coords[4])
{
Context::get()->active_fb->scissor_get(coords);
}
void GPU_viewport_size_get_f(float coords[4])
{
int viewport[4];
Context::get()->active_fb->viewport_get(viewport);
for (int i = 0; i < 4; i++) {
coords[i] = viewport[i];
}
}
void GPU_viewport_size_get_i(int coords[4])
{
Context::get()->active_fb->viewport_get(coords);
}
bool GPU_depth_mask_get()
{
const GPUState &state = Context::get()->state_manager->state;
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
}
bool GPU_mipmap_enabled()
{
/* TODO(fclem): this used to be a userdef option. */
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Context Utils
* \{ */
void GPU_flush()
{
Context::get()->flush();
}
void GPU_finish()
{
Context::get()->finish();
}
void GPU_apply_state()
{
Context::get()->state_manager->apply_state();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name BGL workaround
*
* bgl makes direct GL calls that makes our state tracking out of date.
* This flag make it so that the pyGPU calls will not override the state set by
* bgl functions.
* \{ */
void GPU_bgl_start()
{
Context *ctx = Context::get();
if (!(ctx && ctx->state_manager)) {
return;
}
StateManager &state_manager = *(Context::get()->state_manager);
if (state_manager.use_bgl == false) {
/* Expected by many addons (see #80169, #81289).
* This will reset the blend function. */
GPU_blend(GPU_BLEND_NONE);
/* Equivalent of setting the depth func `glDepthFunc(GL_LEQUAL)`
* Needed since Python scripts may enable depth test.
* Without this block the depth test function is undefined. */
{
eGPUDepthTest depth_test_real = GPU_depth_test_get();
eGPUDepthTest depth_test_temp = GPU_DEPTH_LESS_EQUAL;
if (depth_test_real != depth_test_temp) {
GPU_depth_test(depth_test_temp);
state_manager.apply_state();
GPU_depth_test(depth_test_real);
}
}
state_manager.apply_state();
state_manager.use_bgl = true;
}
}
void GPU_bgl_end()
{
Context *ctx = Context::get();
if (!(ctx && ctx->state_manager)) {
return;
}
StateManager &state_manager = *ctx->state_manager;
if (state_manager.use_bgl == true) {
state_manager.use_bgl = false;
/* Resync state tracking. */
state_manager.force_state();
}
}
bool GPU_bgl_get()
{
return Context::get()->state_manager->use_bgl;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Synchronization Utils
* \{ */
void GPU_memory_barrier(eGPUBarrier barrier)
{
Context::get()->state_manager->issue_barrier(barrier);
}
GPUFence *GPU_fence_create()
{
Fence *fence = GPUBackend::get()->fence_alloc();
return wrap(fence);
}
void GPU_fence_free(GPUFence *fence)
{
delete unwrap(fence);
}
void GPU_fence_signal(GPUFence *fence)
{
unwrap(fence)->signal();
}
void GPU_fence_wait(GPUFence *fence)
{
unwrap(fence)->wait();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Default State
* \{ */
StateManager::StateManager()
{
/* Set default state. */
state.write_mask = GPU_WRITE_COLOR;
state.blend = GPU_BLEND_NONE;
state.culling_test = GPU_CULL_NONE;
state.depth_test = GPU_DEPTH_NONE;
state.stencil_test = GPU_STENCIL_NONE;
state.stencil_op = GPU_STENCIL_OP_NONE;
state.provoking_vert = GPU_VERTEX_LAST;
state.logic_op_xor = false;
state.invert_facing = false;
state.shadow_bias = false;
state.clip_distances = 0;
state.clip_control = false;
state.polygon_smooth = false;
state.line_smooth = false;
mutable_state.depth_range[0] = 0.0f;
mutable_state.depth_range[1] = 1.0f;
mutable_state.point_size = -1.0f; /* Negative is not using point size. */
mutable_state.line_width = 1.0f;
mutable_state.stencil_write_mask = 0x00;
mutable_state.stencil_compare_mask = 0x00;
mutable_state.stencil_reference = 0x00;
}
/** \} */