diff options
Diffstat (limited to 'source/blender/gpu/opengl')
-rw-r--r-- | source/blender/gpu/opengl/gl_backend.hh | 11 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_batch.cc | 57 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_batch.hh | 19 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_context.cc | 35 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_context.hh | 15 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_drawlist.hh | 6 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_shader.cc | 13 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_shader.hh | 10 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_shader_interface.cc | 297 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_shader_interface.hh | 63 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_state.cc | 22 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_state.hh | 6 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_uniform_buffer.cc | 126 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_uniform_buffer.hh | 58 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_vertex_array.cc | 14 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_vertex_array.hh | 4 |
16 files changed, 694 insertions, 62 deletions
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 63a14af6612..0234c1a3f63 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -31,6 +31,7 @@ #include "gl_context.hh" #include "gl_drawlist.hh" #include "gl_shader.hh" +#include "gl_uniform_buffer.hh" namespace blender { namespace gpu { @@ -40,6 +41,11 @@ class GLBackend : public GPUBackend { GLSharedOrphanLists shared_orphan_list_; public: + static GLBackend *get(void) + { + return static_cast<GLBackend *>(GPUBackend::get()); + } + GPUContext *context_alloc(void *ghost_window) { return new GLContext(ghost_window, shared_orphan_list_); @@ -60,6 +66,11 @@ class GLBackend : public GPUBackend { return new GLShader(name); }; + UniformBuf *uniformbuf_alloc(int size, const char *name) + { + return new GLUniformBuf(size, name); + }; + /* TODO remove */ void buf_free(GLuint buf_id); void tex_free(GLuint tex_id); diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index fade8763065..7d6270bca93 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -33,6 +33,7 @@ #include "gpu_batch_private.hh" #include "gpu_primitive_private.h" +#include "gpu_shader_private.hh" #include "gl_batch.hh" #include "gl_context.hh" @@ -68,10 +69,11 @@ void GLVaoCache::init(void) } vao_base_instance_ = 0; base_instance_ = 0; + vao_id_ = 0; } /* Create a new VAO object and store it in the cache. */ -void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) +void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao) { /* Now insert the cache. */ if (!is_dynamic_vao_count) { @@ -90,8 +92,7 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) /* Erase previous entries, they will be added back if drawn again. */ for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { if (static_vaos.interfaces[i] != NULL) { - GPU_shaderinterface_remove_batch_ref( - const_cast<GPUShaderInterface *>(static_vaos.interfaces[i]), this); + const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this); context_->vao_free(static_vaos.vao_ids[i]); } } @@ -99,8 +100,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) is_dynamic_vao_count = true; /* Init dynamic arrays and let the branch below set the values. */ dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( - dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN( + dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces"); dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } @@ -118,8 +119,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) /* Not enough place, realloc the array. */ i = dynamic_vaos.count; dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( - (void *)dynamic_vaos.interfaces, sizeof(GPUShaderInterface *) * dynamic_vaos.count); + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN( + (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count); dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids, sizeof(GLuint) * dynamic_vaos.count); } @@ -127,15 +128,15 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) dynamic_vaos.vao_ids[i] = vao; } - GPU_shaderinterface_add_batch_ref(const_cast<GPUShaderInterface *>(interface), this); + const_cast<GLShaderInterface *>(interface)->ref_add(this); } -void GLVaoCache::remove(const GPUShaderInterface *interface) +void GLVaoCache::remove(const GLShaderInterface *interface) { const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; for (int i = 0; i < count; i++) { if (interfaces[i] == interface) { context_->vao_free(vaos[i]); @@ -151,8 +152,8 @@ void GLVaoCache::clear(void) GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; /* Early out, nothing to free. */ if (context_ == NULL) { return; @@ -171,10 +172,9 @@ void GLVaoCache::clear(void) } for (int i = 0; i < count; i++) { - if (interfaces[i] == NULL) { - continue; + if (interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this); } - GPU_shaderinterface_remove_batch_ref(const_cast<GPUShaderInterface *>(interfaces[i]), this); } if (is_dynamic_vao_count) { @@ -190,11 +190,11 @@ void GLVaoCache::clear(void) } /* Return 0 on cache miss (invalid VAO) */ -GLuint GLVaoCache::lookup(const GPUShaderInterface *interface) +GLuint GLVaoCache::lookup(const GLShaderInterface *interface) { const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; for (int i = 0; i < count; i++) { if (interfaces[i] == interface) { return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i]; @@ -226,7 +226,9 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) { this->context_check(); /* Make sure the interface is up to date. */ - if (interface_ != GPU_context_active_get()->shader->interface) { + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { vao_get(batch); /* Trigger update. */ base_instance_ = 0; @@ -238,6 +240,7 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) #ifdef __APPLE__ glDeleteVertexArrays(1, &vao_base_instance_); vao_base_instance_ = 0; + base_instance_ = 0; #endif if (vao_base_instance_ == 0) { @@ -248,16 +251,17 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) base_instance_ = i_first; GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first); } - return base_instance_; + return vao_base_instance_; } GLuint GLVaoCache::vao_get(GPUBatch *batch) { this->context_check(); - GPUContext *ctx = GPU_context_active_get(); - if (interface_ != ctx->shader->interface) { - interface_ = ctx->shader->interface; + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + interface_ = interface; vao_id_ = this->lookup(interface_); if (vao_id_ == 0) { @@ -303,6 +307,7 @@ void GLBatch::bind(int i_first) GPU_context_active_get()->state_manager->apply_state(); if (flag & GPU_BATCH_DIRTY) { + flag &= ~GPU_BATCH_DIRTY; vao_cache_.clear(); } @@ -324,6 +329,8 @@ void GLBatch::bind(int i_first) void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) { + GL_CHECK_ERROR("Batch Pre drawing"); + this->bind(i_first); BLI_assert(v_count > 0 && i_count > 0); @@ -350,6 +357,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) glDrawElementsInstancedBaseVertex( gl_type, v_count, index_type, v_first_ofs, i_count, base_index); } + GL_CHECK_ERROR("Batch Post-drawing Indexed"); } else { #ifdef __APPLE__ @@ -364,6 +372,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) #ifdef __APPLE__ glEnable(GL_PRIMITIVE_RESTART); #endif + GL_CHECK_ERROR("Batch Post-drawing Non-indexed"); } } diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index d70f43aed2a..9399148c68d 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -32,11 +32,12 @@ #include "glew-mx.h" -#include "GPU_shader_interface.h" - namespace blender { namespace gpu { +class GLContext; +class GLShaderInterface; + #define GPU_VAO_STATIC_LEN 3 /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) @@ -45,9 +46,9 @@ namespace gpu { class GLVaoCache { private: /** Context for which the vao_cache_ was generated. */ - struct GLContext *context_ = NULL; + GLContext *context_ = NULL; /** Last interface this batch was drawn with. */ - GPUShaderInterface *interface_ = NULL; + GLShaderInterface *interface_ = NULL; /** Cached vao for the last interface. */ GLuint vao_id_ = 0; /** Used whend arb_base_instance is not supported. */ @@ -58,13 +59,13 @@ class GLVaoCache { union { /** Static handle count */ struct { - const GPUShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; + const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; GLuint vao_ids[GPU_VAO_STATIC_LEN]; } static_vaos; /** Dynamic handle count */ struct { uint count; - const GPUShaderInterface **interfaces; + const GLShaderInterface **interfaces; GLuint *vao_ids; } dynamic_vaos; }; @@ -76,9 +77,9 @@ class GLVaoCache { GLuint vao_get(GPUBatch *batch); GLuint base_instance_vao_get(GPUBatch *batch, int i_first); - GLuint lookup(const GPUShaderInterface *interface); - void insert(const GPUShaderInterface *interface, GLuint vao_id); - void remove(const GPUShaderInterface *interface); + GLuint lookup(const GLShaderInterface *interface); + void insert(const GLShaderInterface *interface, GLuint vao_id); + void remove(const GLShaderInterface *interface); void clear(void); private: diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 11f313f639b..2ac361d28e1 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -22,6 +22,7 @@ */ #include "BLI_assert.h" +#include "BLI_system.h" #include "BLI_utildefines.h" #include "GPU_framebuffer.h" @@ -238,3 +239,37 @@ void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Error Checking + * + * This is only useful for implementation that does not support the KHR_debug extension. + * \{ */ + +void GLContext::check_error(const char *info) +{ + GLenum error = glGetError(); + +#define ERROR_CASE(err) \ + case err: \ + fprintf(stderr, "GL error: %s : %s\n", #err, info); \ + BLI_system_backtrace(stderr); \ + break; + + switch (error) { + ERROR_CASE(GL_INVALID_ENUM) + ERROR_CASE(GL_INVALID_VALUE) + ERROR_CASE(GL_INVALID_OPERATION) + ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION) + ERROR_CASE(GL_OUT_OF_MEMORY) + ERROR_CASE(GL_STACK_UNDERFLOW) + ERROR_CASE(GL_STACK_OVERFLOW) + case GL_NO_ERROR: + break; + default: + fprintf(stderr, "Unknown GL error: %x : %s", error, info); + break; + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index 0b762c939f1..35121a960ba 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -32,13 +32,20 @@ #include "glew-mx.h" -#include "gl_batch.hh" - #include <mutex> +/* Enabled on MacOS by default since there is no support for debug callbacks. */ +#if defined(DEBUG) && defined(__APPLE__) +# define GL_CHECK_ERROR(info) GLContext::check_error(info) +#else +# define GL_CHECK_ERROR(info) +#endif + namespace blender { namespace gpu { +class GLVaoCache; + class GLSharedOrphanLists { public: /** Mutex for the bellow structures. */ @@ -51,7 +58,7 @@ class GLSharedOrphanLists { void orphans_clear(void); }; -struct GLContext : public GPUContext { +class GLContext : public GPUContext { /* TODO(fclem) these needs to become private. */ public: /** Default VAO for procedural draw calls. */ @@ -78,6 +85,8 @@ struct GLContext : public GPUContext { GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list); ~GLContext(); + static void check_error(const char *info); + void activate(void) override; void deactivate(void) override; diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh index 4f085149388..b690b8f8a98 100644 --- a/source/blender/gpu/opengl/gl_drawlist.hh +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -19,6 +19,9 @@ /** \file * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. */ #pragma once @@ -37,6 +40,9 @@ namespace blender { namespace gpu { +/** + * Implementation of Multi Draw Indirect using OpenGL. + **/ class GLDrawList : public DrawList { public: GLDrawList(int length); diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index ea33ff00d69..3ec818b53a6 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -29,6 +29,7 @@ #include "GPU_platform.h" #include "gl_shader.hh" +#include "gl_shader_interface.hh" using namespace blender; using namespace blender::gpu; @@ -48,7 +49,7 @@ GLShader::GLShader(const char *name) : Shader(name) #ifndef __APPLE__ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { char sh_name[64]; - BLI_snprintf(sh_name, sizeof(sh_name), "ShaderProgram-%s", name); + SNPRINTF(sh_name, "ShaderProgram-%s", name); glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name); } #endif @@ -150,6 +151,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> } if (!status) { glDeleteShader(shader); + compilation_failed_ = true; return 0; } @@ -192,6 +194,10 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) bool GLShader::finalize(void) { + if (compilation_failed_) { + return false; + } + glLinkProgram(shader_program_); GLint status; @@ -203,10 +209,7 @@ bool GLShader::finalize(void) return false; } - /* TODO(fclem) We need this to modify the image binding points using glUniform. - * This could be avoided using glProgramUniform in GL 4.1. */ - glUseProgram(shader_program_); - interface = GPU_shaderinterface_create(shader_program_); + interface = new GLShaderInterface(shader_program_); return true; } diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index b432a04abaa..a686014f4c5 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -19,9 +19,6 @@ /** \file * \ingroup gpu - * - * GPUDrawList is an API to do lots of similar draw-calls very fast using - * multi-draw-indirect. There is a fallback if the feature is not supported. */ #pragma once @@ -35,14 +32,19 @@ namespace blender { namespace gpu { +/** + * Implementation of shader compilation and uniforms handling using OpenGL. + **/ class GLShader : public Shader { private: /** Handle for full program (links shader stages below). */ GLuint shader_program_ = 0; - + /** Individual shader stages. */ GLuint vert_shader_ = 0; GLuint geom_shader_ = 0; GLuint frag_shader_ = 0; + /** True if any shader failed to compile. */ + bool compilation_failed_ = false; eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc new file mode 100644 index 00000000000..423db5c8c97 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -0,0 +1,297 @@ +/* + * 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. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + */ + +#include "BLI_bitmap.h" + +#include "gl_batch.hh" + +#include "gl_shader_interface.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Binding assignment + * + * To mimic vulkan, we assign binding at shader creation to avoid shader recompilation. + * In the future, we should set it in the shader using layout(binding = i) and query its value. + * \{ */ + +static inline int block_binding(int32_t program, uint32_t block_index) +{ + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + glUniformBlockBinding(program, block_index, block_index); + return block_index; +} + +static inline int sampler_binding(int32_t program, + uint32_t uniform_index, + int32_t uniform_location, + int *sampler_len) +{ + /* Identify sampler uniforms and asign sampler units to them. */ + GLint type; + glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: { + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + int binding = *sampler_len; + glUniform1i(uniform_location, binding); + (*sampler_len)++; + return binding; + } + default: + return -1; + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShaderInterface::GLShaderInterface(GLuint program) +{ + /* Necessary to make glUniform works. */ + glUseProgram(program); + + GLint max_attr_name_len = 0, attr_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); + + GLint max_ubo_name_len = 0, ubo_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); + + GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); + uniform_len = active_uniform_len; + + /* Work around driver bug with Intel HD 4600 on Windows 7/8, where + * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ + if (attr_len > 0 && max_attr_name_len == 0) { + max_attr_name_len = 256; + } + if (ubo_len > 0 && max_ubo_name_len == 0) { + max_ubo_name_len = 256; + } + if (uniform_len > 0 && max_uniform_name_len == 0) { + max_uniform_name_len = 256; + } + + /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before + * allocating the uniform array. */ + GLint max_ubo_uni_len = 0; + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); + uniform_len -= ubo_uni_len; + } + /* Bit set to true if uniform comes from a uniform block. */ + BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); + /* Set uniforms from block for exclusion. */ + GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); + for (int u = 0; u < ubo_uni_len; u++) { + BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + } + } + MEM_freeN(ubo_uni_ids); + + int input_tot_len = attr_len + ubo_len + uniform_len; + inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); + + const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + + uniform_len * max_uniform_name_len; + name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); + uint32_t name_buffer_offset = 0; + + /* Attributes */ + enabled_attr_mask_ = 0; + for (int i = 0; i < attr_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + GLenum type; + GLint size; + + glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); + GLint location = glGetAttribLocation(program, name); + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (location == -1) { + continue; + } + + ShaderInput *input = &inputs_[attr_len_++]; + input->location = input->binding = location; + + name_buffer_offset += set_input_name(input, name, name_len); + enabled_attr_mask_ |= (1 << input->location); + } + + /* Uniform Blocks */ + for (int i = 0; i < ubo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_++]; + input->binding = input->location = block_binding(program, i); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_ubo_mask_ |= (1 << input->binding); + } + + /* Uniforms */ + for (int i = 0, sampler = 0; i < active_uniform_len; i++) { + if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { + continue; + } + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++]; + input->location = glGetUniformLocation(program, name); + input->binding = sampler_binding(program, i, input->location, &sampler); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu; + } + + /* Builtin Uniforms */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { + GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); + builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u)); + } + + /* Builtin Uniforms Blocks */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { + GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); + const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u)); + builtin_blocks_[u] = (block != NULL) ? block->binding : -1; + } + + MEM_freeN(uniforms_from_blocks); + + /* Resize name buffer to save some memory. */ + if (name_buffer_offset < name_buffer_len) { + name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset); + } + + // this->debug_print(); + + this->sort_inputs(); +} + +GLShaderInterface::~GLShaderInterface() +{ + for (auto *ref : refs_) { + if (ref != NULL) { + ref->remove(this); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Batch Reference + * \{ */ + +void GLShaderInterface::ref_add(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == NULL) { + refs_[i] = ref; + return; + } + } + refs_.append(ref); +} + +void GLShaderInterface::ref_remove(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == ref) { + refs_[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Validation + * TODO + * \{ */ + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh new file mode 100644 index 00000000000..0b9585aa389 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.hh @@ -0,0 +1,63 @@ +/* + * 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. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_vector.hh" + +#include "glew-mx.h" + +#include "gpu_shader_interface.hh" + +namespace blender::gpu { + +class GLVaoCache; + +/** + * Implementation of Shader interface using OpenGL. + **/ +class GLShaderInterface : public ShaderInterface { + private: + /** Reference to VaoCaches using this interface */ + Vector<GLVaoCache *> refs_; + + public: + GLShaderInterface(GLuint program); + ~GLShaderInterface(); + + void ref_add(GLVaoCache *ref); + void ref_remove(GLVaoCache *ref); + + // bool resource_binding_validate(); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 3e3695e0b48..7dc3e44c516 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -26,6 +26,7 @@ #include "glew-mx.h" +#include "gl_context.hh" #include "gl_state.hh" using namespace blender::gpu; @@ -53,6 +54,9 @@ GLStateManager::GLStateManager(void) : GPUStateManager() 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; @@ -65,23 +69,23 @@ void GLStateManager::set_state(const GPUState &state) GPUState changed = state ^ current_; if (changed.blend != 0) { - set_blend(state.blend); + set_blend((eGPUBlend)state.blend); } if (changed.write_mask != 0) { - set_write_mask(state.write_mask); + set_write_mask((eGPUWriteMask)state.write_mask); } if (changed.depth_test != 0) { - set_depth_test(state.depth_test); + set_depth_test((eGPUDepthTest)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_state); + 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(state.culling_test); + set_backface_culling((eGPUFaceCullTest)state.culling_test); } if (changed.logic_op_xor != 0) { set_logic_op(state.logic_op_xor); @@ -90,7 +94,7 @@ void GLStateManager::set_state(const GPUState &state) set_facing(state.invert_facing); } if (changed.provoking_vert != 0) { - set_provoking_vert(state.provoking_vert); + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); } if (changed.shadow_bias != 0) { set_shadow_bias(state.shadow_bias); @@ -150,7 +154,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) if (changed.line_width != 0) { /* TODO remove, should use wide line shader. */ - glLineWidth(clamp_f(state.line_width, 1.0f, GPU_max_line_width())); + 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) { @@ -160,7 +164,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || changed.stencil_write_mask != 0) { - set_stencil_mask(current_.stencil_test, state); + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); } current_mutable_ = state; diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index b05fcc5d044..8e806cb3e7a 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -31,11 +31,17 @@ namespace blender { namespace gpu { +/** + * State manager keeping track of the draw state and applying it before drawing. + * Opengl Implementation. + **/ class GLStateManager : public GPUStateManager { private: /** Current state of the GL implementation. Avoids resetting the whole state for every change. */ GPUState current_; GPUStateMutable current_mutable_; + /** Limits. */ + float line_width_range_[2]; public: GLStateManager(); diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc new file mode 100644 index 00000000000..5115034639c --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -0,0 +1,126 @@ +/* + * 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. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "gl_backend.hh" +#include "gl_uniform_buffer.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ + /* Do not create ubo GL buffer here to allow allocation from any thread. */ +} + +GLUniformBuf::~GLUniformBuf() +{ + GLBackend::get()->buf_free(ubo_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data upload / update + * \{ */ + +void GLUniformBuf::init(void) +{ + BLI_assert(GPU_context_active_get()); + + glGenBuffers(1, &ubo_id_); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, NULL, GL_DYNAMIC_DRAW); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "UBO-%s", name_); + glObjectLabel(GL_BUFFER, ubo_id_, -1, sh_name); + } +#endif +} + +void GLUniformBuf::update(const void *data) +{ + if (ubo_id_ == 0) { + this->init(); + } + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Usage + * \{ */ + +void GLUniformBuf::bind(int slot) +{ + if (slot >= GPU_max_ubo_binds()) { + fprintf(stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", + name_, + slot, + GPU_max_ubo_binds()); + return; + } + + if (ubo_id_ == 0) { + this->init(); + } + + if (data_ != NULL) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + slot_ = slot; + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_); +} + +void GLUniformBuf::unbind(void) +{ +#ifdef DEBUG + /* NOTE: This only unbinds the last bound slot. */ + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0); +#endif + slot_ = 0; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh new file mode 100644 index 00000000000..b71356c4121 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.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. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_uniform_buffer_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Uniform Buffers using OpenGL. + **/ +class GLUniformBuf : public UniformBuf { + private: + int slot_ = -1; + GLuint ubo_id_ = 0; + + public: + GLUniformBuf(size_t size, const char *name); + ~GLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind(void) override; + + private: + void init(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 907dc37e46f..b2d2445f113 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -23,9 +23,9 @@ #include "GPU_glew.h" -#include "GPU_shader_interface.h" #include "GPU_vertex_buffer.h" +#include "gpu_shader_interface.hh" #include "gpu_vertex_format_private.h" #include "gl_batch.hh" @@ -33,14 +33,14 @@ #include "gl_vertex_array.hh" -using namespace blender::gpu; +namespace blender::gpu { /* -------------------------------------------------------------------- */ /** \name Vertex Array Bindings * \{ */ /* Returns enabled vertex pointers as a bitflag (one bit per attrib). */ -static uint16_t vbo_bind(const GPUShaderInterface *interface, +static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertFormat *format, uint v_first, uint v_len, @@ -68,7 +68,7 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface, for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name); + const ShaderInput *input = interface->attr_get(name); if (input == NULL) { continue; @@ -111,10 +111,10 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface, /* Update the Attrib Binding of the currently bound VAO. */ void GLVertArray::update_bindings(const GLuint vao, const GPUBatch *batch, - const GPUShaderInterface *interface, + const ShaderInterface *interface, const int base_instance) { - uint16_t attr_mask = interface->enabled_attr_mask; + uint16_t attr_mask = interface->enabled_attr_mask_; glBindVertexArray(vao); @@ -156,3 +156,5 @@ void GLVertArray::update_bindings(const GLuint vao, } /** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh index 6da414d7e62..59cd50ad7b8 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.hh +++ b/source/blender/gpu/opengl/gl_vertex_array.hh @@ -26,7 +26,7 @@ #include "glew-mx.h" #include "GPU_batch.h" -#include "GPU_shader_interface.h" +#include "gl_shader_interface.hh" namespace blender { namespace gpu { @@ -35,7 +35,7 @@ namespace GLVertArray { void update_bindings(const GLuint vao, const GPUBatch *batch, - const GPUShaderInterface *interface, + const ShaderInterface *interface, const int base_instance); } // namespace GLVertArray |