From 2ae1c895a2f7590d0783a27176085da45ef12bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sun, 16 Aug 2020 14:01:07 +0200 Subject: GPUState: Add GL backend and state tracking but do not use it This is just the backend work. It is not plugged in yet because it needs more external cleanup/refactor. --- source/blender/gpu/CMakeLists.txt | 3 + source/blender/gpu/GPU_material.h | 9 - source/blender/gpu/GPU_state.h | 80 ++++- source/blender/gpu/intern/gpu_state.cc | 35 ++- source/blender/gpu/intern/gpu_state_private.hh | 164 +++++++++++ source/blender/gpu/opengl/gl_state.cc | 389 +++++++++++++++++++++++++ source/blender/gpu/opengl/gl_state.hh | 58 ++++ 7 files changed, 717 insertions(+), 21 deletions(-) create mode 100644 source/blender/gpu/intern/gpu_state_private.hh create mode 100644 source/blender/gpu/opengl/gl_state.cc create mode 100644 source/blender/gpu/opengl/gl_state.hh (limited to 'source/blender/gpu') diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index bcf2bc42402..45b379c5e0a 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -93,6 +93,7 @@ set(SRC opengl/gl_context.cc opengl/gl_drawlist.cc opengl/gl_shader.cc + opengl/gl_state.cc opengl/gl_vertex_array.cc GPU_attr_binding.h @@ -139,6 +140,7 @@ set(SRC intern/gpu_private.h intern/gpu_select_private.h intern/gpu_shader_private.hh + intern/gpu_state_private.hh intern/gpu_vertex_format_private.h opengl/gl_backend.hh @@ -146,6 +148,7 @@ set(SRC opengl/gl_context.hh opengl/gl_drawlist.hh opengl/gl_shader.hh + opengl/gl_state.hh opengl/gl_vertex_array.hh ) diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index b8957ff1819..9dcf9b7d5bb 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -112,15 +112,6 @@ typedef enum eGPUMatFlag { GPU_MATFLAG_BARYCENTRIC = (1 << 4), } eGPUMatFlag; -typedef enum eGPUBlendMode { - GPU_BLEND_SOLID = 0, - GPU_BLEND_ADD = 1, - GPU_BLEND_ALPHA = 2, - GPU_BLEND_CLIP = 4, - GPU_BLEND_ALPHA_SORT = 8, - GPU_BLEND_ALPHA_TO_COVERAGE = 16, -} eGPUBlendMode; - typedef struct GPUNodeStack { eGPUType type; float vec[4]; diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 2a940b97104..19bb8001491 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -24,6 +24,70 @@ extern "C" { #endif +typedef enum eGPUWriteMask { + GPU_WRITE_RED = (1 << 0), + GPU_WRITE_GREEN = (1 << 1), + GPU_WRITE_BLUE = (1 << 2), + GPU_WRITE_ALPHA = (1 << 3), + GPU_WRITE_DEPTH = (1 << 4), + GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA), +} eGPUWriteMask; + +/** + * Defines the fixed pipeline blending equation. + * SRC is the output color from the shader. + * DST is the color from the framebuffer. + * The blending equation is : + * (SRC * A) + (DST * B). + * The blend mode will modify the A and B parameters. + */ +typedef enum eGPUBlend { + GPU_BLEND_NONE = 0, + /** Premult variants will _NOT_ multiply rgb output by alpha. */ + GPU_BLEND_ALPHA, + GPU_BLEND_ALPHA_PREMULT, + GPU_BLEND_ADDITIVE, + GPU_BLEND_ADDITIVE_PREMULT, + GPU_BLEND_MULTIPLY, + GPU_BLEND_SUBTRACT, + /** Replace logic op: SRC * (1 - DST) + * NOTE: Does not modify alpha. */ + GPU_BLEND_INVERT, + /** Order independant transparency. + * NOTE: Cannot be used as is. Needs special setup (framebuffer, shader ...). */ + GPU_BLEND_OIT, + /** Special blend to add color under and multiply dst color by src alpha. */ + GPU_BLEND_BACKGROUND, + /** Custom blend parameters using dual source blending : SRC0 + SRC1 * DST + * NOTE: Can only be used with _ONE_ Draw Buffer and shader needs to be specialized. */ + GPU_BLEND_CUSTOM, +} eGPUBlend; + +typedef enum eGPUDepthTest { + GPU_DEPTH_NONE = 0, + GPU_DEPTH_ALWAYS, + GPU_DEPTH_LESS, + GPU_DEPTH_LESS_EQUAL, + GPU_DEPTH_EQUAL, + GPU_DEPTH_GREATER, + GPU_DEPTH_GREATER_EQUAL, +} eGPUDepthTest; + +typedef enum eGPUStencilTest { + GPU_STENCIL_NONE = 0, + GPU_STENCIL_ALWAYS, + GPU_STENCIL_EQUAL, + GPU_STENCIL_NEQUAL, +} eGPUStencilTest; + +typedef enum eGPUStencilOp { + GPU_STENCIL_OP_NONE = 0, + GPU_STENCIL_OP_REPLACE, + /** Special values for stencil shadows. */ + GPU_STENCIL_OP_COUNT_DEPTH_PASS, + GPU_STENCIL_OP_COUNT_DEPTH_FAIL, +} eGPUStencilOp; + /* These map directly to the GL_ blend functions, to minimize API add as needed*/ typedef enum eGPUBlendFunction { GPU_ONE, @@ -33,21 +97,15 @@ typedef enum eGPUBlendFunction { GPU_ZERO, } eGPUBlendFunction; -/* These map directly to the GL_ filter functions, to minimize API add as needed*/ -typedef enum eGPUFilterFunction { - GPU_NEAREST, - GPU_LINEAR, -} eGPUFilterFunction; - -typedef enum eGPUFaceCull { +typedef enum eGPUFaceCullTest { GPU_CULL_NONE = 0, /* Culling disabled. */ GPU_CULL_FRONT, GPU_CULL_BACK, -} eGPUFaceCull; +} eGPUFaceCullTest; typedef enum eGPUProvokingVertex { - GPU_VERTEX_FIRST = 0, - GPU_VERTEX_LAST, /* Default */ + GPU_VERTEX_LAST = 0, /* Default. */ + GPU_VERTEX_FIRST = 1, /* Follow Blender loop order. */ } eGPUProvokingVertex; /* Initialize @@ -62,7 +120,7 @@ void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, eGPUBlendFunction dst_rgb, eGPUBlendFunction src_alpha, eGPUBlendFunction dst_alpha); -void GPU_face_culling(eGPUFaceCull culling); +void GPU_face_culling(eGPUFaceCullTest culling); void GPU_front_facing(bool invert); void GPU_provoking_vertex(eGPUProvokingVertex vert); void GPU_depth_range(float near, float far); diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index d697b465458..485aa3b852e 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -33,6 +33,10 @@ #include "GPU_glew.h" #include "GPU_state.h" +#include "gpu_state_private.hh" + +using namespace blender::gpu; + static GLenum gpu_get_gl_blendfunction(eGPUBlendFunction blend) { switch (blend) { @@ -78,7 +82,7 @@ void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, gpu_get_gl_blendfunction(dst_alpha)); } -void GPU_face_culling(eGPUFaceCull culling) +void GPU_face_culling(eGPUFaceCullTest culling) { if (culling == GPU_CULL_NONE) { glDisable(GL_CULL_FACE); @@ -269,6 +273,35 @@ bool GPU_mipmap_enabled(void) return true; } +/* -------------------------------------------------------------------- */ +/** \name Draw State (DRW_state) + * \{ */ + +void GPUStateStack::push_stack(void) +{ + stack[stack_top + 1] = stack[stack_top]; + stack_top++; +} + +void GPUStateStack::pop_stack(void) +{ + stack_top--; +} + +void GPUStateStack::push_mutable_stack(void) +{ + mutable_stack[mutable_stack_top + 1] = mutable_stack[mutable_stack_top]; + mutable_stack_top++; +} + +void GPUStateStack::pop_mutable_stack(void) +{ + mutable_stack_top--; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name GPU Push/Pop State * \{ */ diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh new file mode 100644 index 00000000000..0ac538d117c --- /dev/null +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -0,0 +1,164 @@ +/* + * 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 + */ + +#pragma once + +#include "BLI_utildefines.h" + +#include "GPU_state.h" + +#include + +namespace blender { +namespace gpu { + +/* Ecapsulate all pipeline state that we need to track. + * Try to keep small to reduce validation time. */ +union GPUState { + struct { + eGPUWriteMask write_mask : 13; + eGPUBlend blend : 4; + eGPUFaceCullTest culling_test : 2; + eGPUDepthTest depth_test : 3; + eGPUStencilTest stencil_test : 3; + eGPUStencilOp stencil_op : 3; + eGPUProvokingVertex provoking_vert : 1; + /** Enable bits. */ + uint32_t logic_op_xor : 1; + uint32_t invert_facing : 1; + uint32_t shadow_bias : 1; + /** Number of clip distances enabled. */ + /* TODO(fclem) This should be a shader property. */ + uint32_t clip_distances : 3; + /* TODO(fclem) remove, old opengl features. */ + uint32_t polygon_smooth : 1; + uint32_t line_smooth : 1; + }; + /* Here to allow fast bitwise ops. */ + uint64_t data; +}; + +BLI_STATIC_ASSERT(sizeof(GPUState) == sizeof(uint64_t), "GPUState is too big."); + +inline bool operator==(const GPUState &a, const GPUState &b) +{ + return a.data == b.data; +} + +inline bool operator!=(const GPUState &a, const GPUState &b) +{ + return !(a == b); +} + +inline GPUState operator^(const GPUState &a, const GPUState &b) +{ + GPUState r; + r.data = a.data ^ b.data; + return r; +} + +/* Mutable state that does not require pipeline change. */ +union GPUStateMutable { + struct { + /** Offset + Extent of the drawable region inside the framebuffer. */ + int viewport_rect[4]; + /** Offset + Extent of the scissor region inside the framebuffer. */ + int scissor_rect[4]; + /** TODO remove */ + float depth_range[2]; + /** TODO remove, use explicit clear calls. */ + float clear_color[4]; + float clear_depth; + /** Negative if using program point size. */ + /* TODO(fclem) should be passed as uniform to all shaders. */ + float point_size; + /** Not supported on every platform. Prefer using wideline shader. */ + float line_width; + /** Mutable stencil states. */ + uint8_t stencil_write_mask; + uint8_t stencil_compare_mask; + uint8_t stencil_reference; + uint8_t _pad0; + /* IMPORTANT: ensure x64 stuct alignment. */ + }; + /* Here to allow fast bitwise ops. */ + uint64_t data[9]; +}; + +BLI_STATIC_ASSERT(sizeof(GPUStateMutable) == sizeof(GPUStateMutable::data), + "GPUStateMutable is too big."); + +inline bool operator==(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return memcmp(&a, &b, sizeof(GPUStateMutable)) == 0; +} + +inline bool operator!=(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return !(a == b); +} + +inline GPUStateMutable operator^(const GPUStateMutable &a, const GPUStateMutable &b) +{ + GPUStateMutable r; + for (int i = 0; i < ARRAY_SIZE(a.data); i++) { + r.data[i] = a.data[i] ^ b.data[i]; + } + return r; +} + +#define GPU_STATE_STACK_LEN 4 + +class GPUStateStack { + private: + /** Stack of state for quick temporary modification of the state. */ + GPUState stack[GPU_STATE_STACK_LEN]; + GPUStateMutable mutable_stack[GPU_STATE_STACK_LEN]; + int stack_top = 0; + int mutable_stack_top = 0; + + public: + virtual void set_state(GPUState &state) = 0; + virtual void set_mutable_state(GPUStateMutable &state) = 0; + + void push_stack(void); + void pop_stack(void); + + void push_mutable_stack(void); + void pop_mutable_stack(void); + + inline GPUState &stack_top_get(void); + inline GPUStateMutable &mutable_stack_top_get(void); +}; + +inline GPUState &GPUStateStack::stack_top_get(void) +{ + return stack[stack_top]; +} + +inline GPUStateMutable &GPUStateStack::mutable_stack_top_get(void) +{ + return mutable_stack[mutable_stack_top]; +} + +} // namespace gpu +} // namespace blender 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); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh new file mode 100644 index 00000000000..ea3ab512431 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.hh @@ -0,0 +1,58 @@ +/* + * 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_utildefines.h" + +#include "gpu_state_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +class GLStateStack : public GPUStateStack { + private: + /** Current state of the GL implementation. Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + + public: + void set_state(GPUState &state) override; + void set_mutable_state(GPUStateMutable &state) override; + + public: + /* TODO to become private */ + static void set_write_mask(const eGPUWriteMask value); + static void set_depth_test(const eGPUDepthTest value); + static void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + static void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + static void set_clip_distances(const int new_dist_len, const int old_dist_len); + static void set_logic_op(const bool enable); + static void set_facing(const bool invert); + static void set_backface_culling(const eGPUFaceCullTest test); + static void set_provoking_vert(const eGPUProvokingVertex vert); + static void set_shadow_bias(const bool enable); + static void set_blend(const eGPUBlend value); +}; + +} // namespace gpu +} // namespace blender -- cgit v1.2.3