diff options
Diffstat (limited to 'source/blender/gpu/opengl/gl_state.cc')
-rw-r--r-- | source/blender/gpu/opengl/gl_state.cc | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc new file mode 100644 index 00000000000..c64e4471087 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.cc @@ -0,0 +1,389 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" +#include "BLI_utildefines.h" + +#include "BKE_global.h" + +#include "GPU_extensions.h" +#include "GPU_glew.h" +#include "GPU_state.h" + +#include "gl_state.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name GLStateStack + * \{ */ + +void GLStateStack::set_state(GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.write_mask != 0) { + set_write_mask(state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test(state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test(state.stencil_test, state.stencil_op); + set_stencil_mask(state.stencil_test, mutable_stack_top_get()); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling(state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert(state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove */ + if (changed.polygon_smooth) { + if (state.polygon_smooth) { + glEnable(GL_POLYGON_SMOOTH); + } + else { + glDisable(GL_POLYGON_SMOOTH); + } + } + if (changed.line_smooth) { + if (state.line_smooth) { + glEnable(GL_LINE_SMOOTH); + } + else { + glDisable(GL_LINE_SMOOTH); + } + } + + current_ = state; +} + +void GLStateStack::set_mutable_state(GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + + glViewport(UNPACK4(state.viewport_rect)); + + if ((changed.scissor_rect[0] != 0) || (changed.scissor_rect[1] != 0) || + (changed.scissor_rect[2] != 0) || (changed.scissor_rect[3] != 0)) { + glScissor(UNPACK4(state.scissor_rect)); + + if ((state.scissor_rect[0] != state.viewport_rect[0]) || + (state.scissor_rect[1] != state.viewport_rect[1]) || + (state.scissor_rect[2] != state.viewport_rect[2]) || + (state.scissor_rect[3] != state.viewport_rect[3])) { + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } + } + + /* TODO remove, should be uniform. */ + if (state.point_size > 0.0f) { + glEnable(GL_PROGRAM_POINT_SIZE); + glPointSize(state.point_size); + } + else { + glDisable(GL_PROGRAM_POINT_SIZE); + } + + if (changed.line_width != 0) { + /* TODO remove, should use wide line shader. */ + glLineWidth(clamp_f(state.line_width, 1.0f, GPU_max_line_width())); + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + glDepthRange(UNPACK2(state.depth_range)); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask(current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State set functions + * \{ */ + +void GLStateStack::set_write_mask(const eGPUWriteMask value) +{ + glDepthMask((value & GPU_WRITE_DEPTH) != 0); + glColorMask((value & GPU_WRITE_RED) != 0, + (value & GPU_WRITE_GREEN) != 0, + (value & GPU_WRITE_BLUE) != 0, + (value & GPU_WRITE_ALPHA) != 0); +} + +void GLStateStack::set_depth_test(const eGPUDepthTest value) +{ + GLenum func; + switch (value) { + case GPU_DEPTH_LESS: + func = GL_LESS; + break; + case GPU_DEPTH_LESS_EQUAL: + func = GL_LEQUAL; + break; + case GPU_DEPTH_EQUAL: + func = GL_EQUAL; + break; + case GPU_DEPTH_GREATER: + func = GL_GREATER; + break; + case GPU_DEPTH_GREATER_EQUAL: + func = GL_GEQUAL; + break; + case GPU_DEPTH_ALWAYS: + default: + func = GL_ALWAYS; + break; + } + + if (value != GPU_DEPTH_NONE) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(func); + } + else { + glDisable(GL_DEPTH_TEST); + } +} + +void GLStateStack::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP); + break; + case GPU_STENCIL_OP_NONE: + default: + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + + if (test != GPU_STENCIL_NONE) { + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } +} + +void GLStateStack::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + GLenum func; + switch (test) { + case GPU_STENCIL_NEQUAL: + func = GL_NOTEQUAL; + break; + case GPU_STENCIL_EQUAL: + func = GL_EQUAL; + break; + case GPU_STENCIL_ALWAYS: + func = GL_ALWAYS; + break; + case GPU_STENCIL_NONE: + default: + glStencilMask(0x00); + glStencilFunc(GL_ALWAYS, 0x00, 0x00); + return; + } + + glStencilMask(state.stencil_write_mask); + glStencilFunc(func, state.stencil_reference, state.stencil_compare_mask); +} + +void GLStateStack::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + for (int i = 0; i < new_dist_len; i++) { + glEnable(GL_CLIP_DISTANCE0 + i); + } + for (int i = new_dist_len; i < old_dist_len; i++) { + glDisable(GL_CLIP_DISTANCE0 + i); + } +} + +void GLStateStack::set_logic_op(const bool enable) +{ + if (enable) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } +} + +void GLStateStack::set_facing(const bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GLStateStack::set_backface_culling(const eGPUFaceCullTest test) +{ + if (test != GPU_CULL_NONE) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } +} + +void GLStateStack::set_provoking_vert(const eGPUProvokingVertex vert) +{ + GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION; + glProvokingVertex(value); +} + +void GLStateStack::set_shadow_bias(const bool enable) +{ + if (enable) { + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + /* 2.0 Seems to be the lowest possible slope bias that works in every case. */ + glPolygonOffset(2.0f, 1.0f); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_POLYGON_OFFSET_LINE); + } +} + +void GLStateStack::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + GLenum src_rgb, src_alpha, dst_rgb, dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but premult the source RGB by it. */ + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ONE; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = GL_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_DST_ALPHA; + dst_alpha = GL_ZERO; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = GL_ONE_MINUS_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_OIT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = GL_ONE_MINUS_DST_ALPHA; + dst_rgb = GL_SRC_ALPHA; + src_alpha = GL_ZERO; + dst_alpha = GL_SRC_ALPHA; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = GL_ONE; + dst_rgb = GL_SRC1_COLOR; + src_alpha = GL_ONE; + dst_alpha = GL_SRC1_ALPHA; + break; + } + } + + if (value != GPU_BLEND_NONE) { + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } +} + +/** \} */ |