diff options
Diffstat (limited to 'source/blender/gpu/intern/gpu_state.cc')
-rw-r--r-- | source/blender/gpu/intern/gpu_state.cc | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc new file mode 100644 index 00000000000..794c7a3eb97 --- /dev/null +++ b/source/blender/gpu/intern/gpu_state.cc @@ -0,0 +1,436 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#ifndef GPU_STANDALONE +# include "DNA_userdef_types.h" +# define PIXELSIZE (U.pixelsize) +#else +# define PIXELSIZE (1.0f) +#endif + +#include "BLI_utildefines.h" + +#include "BKE_global.h" + +#include "GPU_extensions.h" +#include "GPU_glew.h" +#include "GPU_state.h" + +static GLenum gpu_get_gl_blendfunction(eGPUBlendFunction blend) +{ + switch (blend) { + case GPU_ONE: + return GL_ONE; + case GPU_SRC_ALPHA: + return GL_SRC_ALPHA; + case GPU_ONE_MINUS_SRC_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + case GPU_DST_COLOR: + return GL_DST_COLOR; + case GPU_ZERO: + return GL_ZERO; + default: + BLI_assert(!"Unhandled blend mode"); + return GL_ZERO; + } +} + +void GPU_blend(bool enable) +{ + if (enable) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } +} + +void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor) +{ + glBlendFunc(gpu_get_gl_blendfunction(sfactor), gpu_get_gl_blendfunction(dfactor)); +} + +void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, + eGPUBlendFunction dst_rgb, + eGPUBlendFunction src_alpha, + eGPUBlendFunction dst_alpha) +{ + glBlendFuncSeparate(gpu_get_gl_blendfunction(src_rgb), + gpu_get_gl_blendfunction(dst_rgb), + gpu_get_gl_blendfunction(src_alpha), + gpu_get_gl_blendfunction(dst_alpha)); +} + +void GPU_face_culling(eGPUFaceCull culling) +{ + if (culling == GPU_CULL_NONE) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } +} + +void GPU_front_facing(bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GPU_provoking_vertex(eGPUProvokingVertex vert) +{ + glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION); +} + +void GPU_depth_range(float near, float far) +{ + /* glDepthRangef is only for OpenGL 4.1 or higher */ + glDepthRange(near, far); +} + +void GPU_depth_test(bool enable) +{ + if (enable) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } +} + +bool GPU_depth_test_enabled() +{ + return glIsEnabled(GL_DEPTH_TEST); +} + +void GPU_line_smooth(bool enable) +{ + if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { + glEnable(GL_LINE_SMOOTH); + } + else { + glDisable(GL_LINE_SMOOTH); + } +} + +void GPU_line_width(float width) +{ + float max_size = GPU_max_line_width(); + float final_size = width * PIXELSIZE; + /* Fix opengl errors on certain platform / drivers. */ + CLAMP(final_size, 1.0f, max_size); + glLineWidth(final_size); +} + +void GPU_point_size(float size) +{ + glPointSize(size * PIXELSIZE); +} + +void GPU_polygon_smooth(bool enable) +{ + if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { + glEnable(GL_POLYGON_SMOOTH); + } + else { + glDisable(GL_POLYGON_SMOOTH); + } +} + +/* Programmable point size + * - shaders set their own point size when enabled + * - use glPointSize when disabled */ +void GPU_program_point_size(bool enable) +{ + if (enable) { + glEnable(GL_PROGRAM_POINT_SIZE); + } + else { + glDisable(GL_PROGRAM_POINT_SIZE); + } +} + +void GPU_scissor_test(bool enable) +{ + if (enable) { + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } +} + +void GPU_scissor(int x, int y, int width, int height) +{ + glScissor(x, y, width, height); +} + +void GPU_viewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +void GPU_scissor_get_f(float coords[4]) +{ + glGetFloatv(GL_SCISSOR_BOX, coords); +} + +void GPU_scissor_get_i(int coords[4]) +{ + glGetIntegerv(GL_SCISSOR_BOX, coords); +} + +void GPU_viewport_size_get_f(float coords[4]) +{ + glGetFloatv(GL_VIEWPORT, coords); +} + +void GPU_viewport_size_get_i(int coords[4]) +{ + glGetIntegerv(GL_VIEWPORT, coords); +} + +void GPU_flush(void) +{ + glFlush(); +} + +void GPU_finish(void) +{ + glFinish(); +} + +void GPU_unpack_row_length_set(uint len) +{ + glPixelStorei(GL_UNPACK_ROW_LENGTH, len); +} + +void GPU_logic_op_xor_set(bool enable) +{ + if (enable) { + glLogicOp(GL_XOR); + glEnable(GL_COLOR_LOGIC_OP); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } +} + +void GPU_color_mask(bool r, bool g, bool b, bool a) +{ + glColorMask(r, g, b, a); +} + +void GPU_depth_mask(bool depth) +{ + glDepthMask(depth); +} + +bool GPU_depth_mask_get(void) +{ + GLint mask; + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask); + return mask == GL_TRUE; +} + +void GPU_stencil_mask(uint stencil) +{ + glStencilMask(stencil); +} + +void GPU_clip_distances(int distances_new) +{ + static int distances_enabled = 0; + for (int i = 0; i < distances_new; i++) { + glEnable(GL_CLIP_DISTANCE0 + i); + } + for (int i = distances_new; i < distances_enabled; i++) { + glDisable(GL_CLIP_DISTANCE0 + i); + } + distances_enabled = distances_new; +} + +bool GPU_mipmap_enabled(void) +{ + /* TODO(fclem) this used to be a userdef option. */ + return true; +} + +/** \name GPU Push/Pop State + * \{ */ + +#define STATE_STACK_DEPTH 16 + +typedef struct { + eGPUAttrMask mask; + + /* GL_BLEND_BIT */ + uint is_blend : 1; + + /* GL_DEPTH_BUFFER_BIT */ + uint is_depth_test : 1; + int depth_func; + double depth_clear_value; + bool depth_write_mask; + + /* GL_SCISSOR_BIT */ + int scissor_box[4]; + uint is_scissor_test : 1; + + /* GL_VIEWPORT_BIT */ + int viewport[4]; + double near_far[2]; +} GPUAttrValues; + +typedef struct { + GPUAttrValues attr_stack[STATE_STACK_DEPTH]; + uint top; +} GPUAttrStack; + +static GPUAttrStack state = { + {}, + 0, +}; + +#define AttrStack state +#define Attr state.attr_stack[state.top] + +/** + * Replacement for glPush/PopAttributes + * + * We don't need to cover all the options of legacy OpenGL + * but simply the ones used by Blender. + */ +void gpuPushAttr(eGPUAttrMask mask) +{ + Attr.mask = mask; + + if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { + Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); + glGetIntegerv(GL_DEPTH_FUNC, &Attr.depth_func); + glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attr.depth_clear_value); + glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); + } + + if ((mask & GPU_SCISSOR_BIT) != 0) { + Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); + } + + if ((mask & GPU_VIEWPORT_BIT) != 0) { + glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); + glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); + } + + if ((mask & GPU_BLEND_BIT) != 0) { + Attr.is_blend = glIsEnabled(GL_BLEND); + } + + BLI_assert(AttrStack.top < STATE_STACK_DEPTH); + AttrStack.top++; +} + +static void restore_mask(GLenum cap, const bool value) +{ + if (value) { + glEnable(cap); + } + else { + glDisable(cap); + } +} + +void gpuPopAttr(void) +{ + BLI_assert(AttrStack.top > 0); + AttrStack.top--; + + GLint mask = Attr.mask; + + if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { + restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); + glDepthFunc(Attr.depth_func); + glClearDepth(Attr.depth_clear_value); + glDepthMask(Attr.depth_write_mask); + } + + if ((mask & GPU_VIEWPORT_BIT) != 0) { + glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]); + glDepthRange(Attr.near_far[0], Attr.near_far[1]); + } + + if ((mask & GPU_SCISSOR_BIT) != 0) { + restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); + glScissor(Attr.scissor_box[0], Attr.scissor_box[1], Attr.scissor_box[2], Attr.scissor_box[3]); + } + + if ((mask & GPU_BLEND_BIT) != 0) { + restore_mask(GL_BLEND, Attr.is_blend); + } +} + +#undef Attr +#undef AttrStack + +/* Default OpenGL State + * + * This is called on startup, for opengl offscreen render. + * Generally we should always return to this state when + * temporarily modifying the state for drawing, though that are (undocumented) + * exceptions that we should try to get rid of. */ + +void GPU_state_init(void) +{ + GPU_program_point_size(false); + + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DITHER); + + glDepthFunc(GL_LEQUAL); + glDepthRange(0.0, 1.0); + + glFrontFace(GL_CCW); + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + /* Is default but better be explicit. */ + glEnable(GL_MULTISAMPLE); + + /* This is a bit dangerous since addons could change this. */ + glEnable(GL_PRIMITIVE_RESTART); + 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); + } +} + +/** \} */ |