diff options
author | Hans Goudey <h.goudey@me.com> | 2020-09-01 20:35:14 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2020-09-01 20:38:05 +0300 |
commit | baca8611e5fe4b3dcd6f5065fb125bc0a9d65934 (patch) | |
tree | bb1230387cd53b15f9621f10c4d0e5e2050b5580 /source/blender/gpu/opengl/gl_state.cc | |
parent | 31705201dddebf7e3be5c4533b89f380aad1ede1 (diff) | |
parent | 2930d4fcea405985f2212c5f28c061af7c4849f8 (diff) |
Merge branch 'master' into active-fcurve-keyframeactive-fcurve-keyframe
Diffstat (limited to 'source/blender/gpu/opengl/gl_state.cc')
-rw-r--r-- | source/blender/gpu/opengl/gl_state.cc | 421 |
1 files changed, 421 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..8f01ff13486 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.cc @@ -0,0 +1,421 @@ +/* + * 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 "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gl_context.hh" +#include "gl_framebuffer.hh" +#include "gl_state.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name GLStateManager + * \{ */ + +GLStateManager::GLStateManager(void) : GPUStateManager() +{ + /* Set other states that never change. */ + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_MULTISAMPLE); + glEnable(GL_PRIMITIVE_RESTART); + + glDisable(GL_DITHER); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); + /* TODO: Should become default. But needs at least GL 4.3 */ + if (GLEW_ARB_ES3_compatibility) { + /* Takes predecence over GL_PRIMITIVE_RESTART */ + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + + /* Limits. */ + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void GLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + active_fb->apply_state(); +}; + +void GLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling((eGPUFaceCullTest)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((eGPUProvokingVertex)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 GLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + + /* TODO remove, should be uniform. */ + if (changed.point_size != 0) { + 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, line_width_range_[0], line_width_range_[1])); + } + + 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((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State set functions + * \{ */ + +void GLStateManager::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); + + if (value == GPU_WRITE_NONE) { + glEnable(GL_RASTERIZER_DISCARD); + } + else { + glDisable(GL_RASTERIZER_DISCARD); + } +} + +void GLStateManager::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 GLStateManager::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 GLStateManager::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 GLStateManager::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 GLStateManager::set_logic_op(const bool enable) +{ + if (enable) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } +} + +void GLStateManager::set_facing(const bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + if (test != GPU_CULL_NONE) { + glEnable(GL_CULL_FACE); + glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } + else { + glDisable(GL_CULL_FACE); + } +} + +void GLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION; + glProvokingVertex(value); +} + +void GLStateManager::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 GLStateManager::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; + } + } + + /* Always set the blend function. This avoid a rendering error when blending is disabled but + * GPU_BLEND_CUSTOM was used just before and the framebuffer is using more than 1 color targe */ + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + if (value != GPU_BLEND_NONE) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } +} + +/** \} */ |