diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2020-07-31 12:56:29 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2020-07-31 19:57:56 +0300 |
commit | 41406fcacf74b7aad7f1c842cb9fa19eb886caac (patch) | |
tree | 5e5eabd247036f2c55cdd30cfc3b98d192ce6bab | |
parent | 510d009f53705e0dccff5d5f792aa34a481addc9 (diff) |
GPU: Hide GL context specificitied behind class methodstmp-gpu-context-isolation
-rw-r--r-- | source/blender/gpu/CMakeLists.txt | 9 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_batch.cc | 97 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_context.cc | 265 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_context_private.h | 68 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_context_private.hh | 98 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_element.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_framebuffer.cc | 18 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_immediate.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_matrix.cc | 16 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_interface.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_uniformbuffer.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_vertex_buffer.cc | 2 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_batch.cc | 124 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_context.cc | 188 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_context.hh | 93 |
16 files changed, 573 insertions, 415 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 3ea18f72166..c18fa5f508c 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set(INC . + ./opengl ../blenkernel ../blenlib ../bmesh @@ -85,6 +86,12 @@ set(SRC intern/gpu_vertex_format.cc intern/gpu_viewport.c + # opengl/ogl_context.cc + opengl/gl_context.hh + + # vulkan/vk_context.cc + # vulkan/vk_context.hh + GPU_attr_binding.h GPU_batch.h GPU_batch_presets.h @@ -118,7 +125,7 @@ set(SRC intern/gpu_attr_binding_private.h intern/gpu_batch_private.h intern/gpu_codegen.h - intern/gpu_context_private.h + intern/gpu_context_private.hh intern/gpu_material_library.h intern/gpu_matrix_private.h intern/gpu_node_graph.h diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 9f9adcacfa6..d354d828044 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -34,7 +34,7 @@ #include "GPU_shader.h" #include "gpu_batch_private.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_primitive_private.h" #include "gpu_shader_private.h" #include "gpu_vertex_format_private.h" @@ -81,7 +81,7 @@ void GPU_batch_vao_cache_clear(GPUBatch *batch) batch->static_vaos.vao_ids[i] = 0; batch->static_vaos.interfaces[i] = NULL; } - gpu_context_remove_batch(batch->context, batch); + batch->context->batch_remove(batch); batch->context = NULL; } @@ -294,7 +294,7 @@ static GLuint batch_vao_get(GPUBatch *batch) * Until then it can only be drawn with this context. */ if (batch->context == NULL) { batch->context = GPU_context_active_get(); - gpu_context_add_batch(batch->context, batch); + batch->context->batch_add(batch); } #if TRUST_NO_ONE else { @@ -634,16 +634,6 @@ void GPU_batch_uniform_mat4(GPUBatch *batch, const char *name, const float data[ glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (const float *)data); } -static void *elem_offset(const GPUIndexBuf *el, int v_first) -{ -#if GPU_TRACK_INDEX_RANGE - if (el->index_type == GPU_INDEX_U16) { - return (GLushort *)0 + v_first + el->index_start; - } -#endif - return (GLuint *)0 + v_first + el->index_start; -} - /* Use when drawing with GPU_batch_draw_advanced */ void GPU_batch_bind(GPUBatch *batch) { @@ -704,90 +694,13 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi return; } - /* Verify there is enough data do draw. */ - /* TODO(fclem) Nice to have but this is invalid when using procedural draw-calls. - * The right assert would be to check if there is an enabled attribute from each VBO - * and check their length. */ - // BLI_assert(i_first + i_count <= (batch->inst ? batch->inst->vertex_len : INT_MAX)); - // BLI_assert(v_first + v_count <= - // (batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len)); - -#ifdef __APPLE__ - GLuint vao = 0; -#endif - - if (!GPU_arb_base_instance_is_supported()) { - if (i_first > 0) { -#ifdef __APPLE__ - /** - * There seems to be a nasty bug when drawing using the same VAO reconfiguring. (see T71147) - * We just use a throwaway VAO for that. Note that this is likely to degrade performance. - **/ - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); -#else - /* If using offset drawing with instancing, we must - * use the default VAO and redo bindings. */ - glBindVertexArray(GPU_vao_default()); -#endif - batch_update_program_bindings(batch, i_first); - } - else { - /* Previous call could have bind the default vao - * see above. */ - glBindVertexArray(batch->vao_id); - } - } - - if (batch->elem) { - const GPUIndexBuf *el = batch->elem; - GLenum index_type = INDEX_TYPE(el); - GLint base_index = BASE_INDEX(el); - void *v_first_ofs = elem_offset(el, v_first); - - if (GPU_arb_base_instance_is_supported()) { - glDrawElementsInstancedBaseVertexBaseInstance( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); - } - else { - glDrawElementsInstancedBaseVertex( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index); - } - } - else { -#ifdef __APPLE__ - glDisable(GL_PRIMITIVE_RESTART); -#endif - if (GPU_arb_base_instance_is_supported()) { - glDrawArraysInstancedBaseInstance(batch->gl_prim_type, v_first, v_count, i_count, i_first); - } - else { - glDrawArraysInstanced(batch->gl_prim_type, v_first, v_count, i_count); - } -#ifdef __APPLE__ - glEnable(GL_PRIMITIVE_RESTART); -#endif - } - -#ifdef __APPLE__ - if (vao != 0) { - glDeleteVertexArrays(1, &vao); - } -#endif + GPU_ctx()->draw_batch(batch, v_first, v_count, i_first, i_count); } /* just draw some vertices and let shader place them where we want. */ void GPU_draw_primitive(GPUPrimType prim_type, int v_count) { - /* we cannot draw without vao ... annoying ... */ - glBindVertexArray(GPU_vao_default()); - - GLenum type = convert_prim_type_to_gl(prim_type); - glDrawArrays(type, 0, v_count); - - /* Performance hog if you are drawing with the same vao multiple time. - * Only activate for debugging.*/ - // glBindVertexArray(0); + GPU_ctx()->draw_primitive(prim_type, v_count); } /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 0b9104e5349..16e52cab0bd 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -35,137 +35,46 @@ #include "GPU_framebuffer.h" #include "gpu_batch_private.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_matrix_private.h" +#include "gl_context.hh" + #include <mutex> #include <pthread.h> #include <string.h> #include <unordered_set> #include <vector> -#if TRUST_NO_ONE -# if 0 -extern "C" { -extern int BLI_thread_is_main(void); /* Blender-specific function */ -} - -static bool thread_is_main() -{ - /* "main" here means the GL context's thread */ - return BLI_thread_is_main(); -} -# endif -#endif - -static std::vector<GLuint> orphaned_buffer_ids; -static std::vector<GLuint> orphaned_texture_ids; - -static std::mutex orphans_mutex; - -struct GPUContext { - GLuint default_vao; - GLuint default_framebuffer; - GPUFrameBuffer *current_fbo; - std::unordered_set<GPUBatch *> batches; /* Batches that have VAOs from this context */ -#ifdef DEBUG - std::unordered_set<GPUFrameBuffer *> - framebuffers; /* Framebuffers that have FBO from this context */ -#endif - struct GPUMatrixState *matrix_state; - std::vector<GLuint> orphaned_vertarray_ids; - std::vector<GLuint> orphaned_framebuffer_ids; - std::mutex orphans_mutex; /* todo: try spinlock instead */ -#if TRUST_NO_ONE - pthread_t thread; /* Thread on which this context is active. */ - bool thread_is_used; -#endif - - GPUContext() - { -#if TRUST_NO_ONE - thread_is_used = false; -#endif - current_fbo = 0; - } -}; +// TODO +// using namespace blender::gpu; static thread_local GPUContext *active_ctx = NULL; -static void orphans_add(GPUContext *ctx, std::vector<GLuint> *orphan_list, GLuint id) +GPUContext::GPUContext() { - std::mutex *mutex = (ctx) ? &ctx->orphans_mutex : &orphans_mutex; - - mutex->lock(); - orphan_list->emplace_back(id); - mutex->unlock(); + thread_ = pthread_self(); + matrix_state = GPU_matrix_state_create(); } -static void orphans_clear(GPUContext *ctx) +GPUContext *GPU_context_create(GLuint) { - /* need at least an active context */ - BLI_assert(ctx); - - /* context has been activated by another thread! */ - BLI_assert(pthread_equal(pthread_self(), ctx->thread)); - - ctx->orphans_mutex.lock(); - if (!ctx->orphaned_vertarray_ids.empty()) { - uint orphan_len = (uint)ctx->orphaned_vertarray_ids.size(); - glDeleteVertexArrays(orphan_len, ctx->orphaned_vertarray_ids.data()); - ctx->orphaned_vertarray_ids.clear(); - } - if (!ctx->orphaned_framebuffer_ids.empty()) { - uint orphan_len = (uint)ctx->orphaned_framebuffer_ids.size(); - glDeleteFramebuffers(orphan_len, ctx->orphaned_framebuffer_ids.data()); - ctx->orphaned_framebuffer_ids.clear(); - } - - ctx->orphans_mutex.unlock(); - - orphans_mutex.lock(); - if (!orphaned_buffer_ids.empty()) { - uint orphan_len = (uint)orphaned_buffer_ids.size(); - glDeleteBuffers(orphan_len, orphaned_buffer_ids.data()); - orphaned_buffer_ids.clear(); - } - if (!orphaned_texture_ids.empty()) { - uint orphan_len = (uint)orphaned_texture_ids.size(); - glDeleteTextures(orphan_len, orphaned_texture_ids.data()); - orphaned_texture_ids.clear(); - } - orphans_mutex.unlock(); + GPUContext *ctx = new GLContext(); + GPU_context_active_set(ctx); + return ctx; } -GPUContext *GPU_context_create(GLuint default_framebuffer) +bool GPUContext::is_active_on_thread(void) { - /* BLI_assert(thread_is_main()); */ - GPUContext *ctx = new GPUContext; - glGenVertexArrays(1, &ctx->default_vao); - ctx->default_framebuffer = default_framebuffer; - ctx->matrix_state = GPU_matrix_state_create(); - GPU_context_active_set(ctx); - return ctx; + return (this == active_ctx) && pthread_equal(pthread_self(), thread_); } /* to be called after GPU_context_active_set(ctx_to_destroy) */ void GPU_context_discard(GPUContext *ctx) { /* Make sure no other thread has locked it. */ - BLI_assert(ctx == active_ctx); - BLI_assert(pthread_equal(pthread_self(), ctx->thread)); - BLI_assert(ctx->orphaned_vertarray_ids.empty()); -#ifdef DEBUG - /* For now don't allow GPUFrameBuffers to be reuse in another ctx. */ - BLI_assert(ctx->framebuffers.empty()); -#endif - /* delete remaining vaos */ - while (!ctx->batches.empty()) { - /* this removes the array entry */ - GPU_batch_vao_cache_clear(*ctx->batches.begin()); - } - GPU_matrix_state_discard(ctx->matrix_state); - glDeleteVertexArrays(1, &ctx->default_vao); + BLI_assert(ctx->is_active_on_thread()); + delete ctx; active_ctx = NULL; } @@ -173,175 +82,69 @@ void GPU_context_discard(GPUContext *ctx) /* ctx can be NULL */ void GPU_context_active_set(GPUContext *ctx) { -#if TRUST_NO_ONE if (active_ctx) { - active_ctx->thread_is_used = false; - } - /* Make sure no other context is already bound to this thread. */ - if (ctx) { - /* Make sure no other thread has locked it. */ - assert(ctx->thread_is_used == false); - ctx->thread = pthread_self(); - ctx->thread_is_used = true; + active_ctx->deactivate(); } -#endif if (ctx) { - orphans_clear(ctx); + ctx->activate(); } active_ctx = ctx; } -GPUContext *GPU_context_active_get(void) +GPUContext *GPU_ctx(void) { + /* Context has been activated by another thread! */ + BLI_assert(active_ctx->is_active_on_thread()); return active_ctx; } -GLuint GPU_vao_default(void) -{ - BLI_assert(active_ctx); /* need at least an active context */ - BLI_assert(pthread_equal( - pthread_self(), active_ctx->thread)); /* context has been activated by another thread! */ - return active_ctx->default_vao; -} - -GLuint GPU_framebuffer_default(void) +GPUContext *GPU_context_active_get(void) { - BLI_assert(active_ctx); /* need at least an active context */ - BLI_assert(pthread_equal( - pthread_self(), active_ctx->thread)); /* context has been activated by another thread! */ - return active_ctx->default_framebuffer; + return active_ctx; } GLuint GPU_vao_alloc(void) { - GLuint new_vao_id = 0; - orphans_clear(active_ctx); - glGenVertexArrays(1, &new_vao_id); - return new_vao_id; + return active_ctx->vao_alloc(); } GLuint GPU_fbo_alloc(void) { - GLuint new_fbo_id = 0; - orphans_clear(active_ctx); - glGenFramebuffers(1, &new_fbo_id); - return new_fbo_id; + return active_ctx->fbo_alloc(); } GLuint GPU_buf_alloc(void) { - GLuint new_buffer_id = 0; - orphans_clear(active_ctx); - glGenBuffers(1, &new_buffer_id); - return new_buffer_id; + return active_ctx->buf_alloc(); } GLuint GPU_tex_alloc(void) { - GLuint new_texture_id = 0; - orphans_clear(active_ctx); - glGenTextures(1, &new_texture_id); - return new_texture_id; + return active_ctx->tex_alloc(); } void GPU_vao_free(GLuint vao_id, GPUContext *ctx) { BLI_assert(ctx); - if (ctx == active_ctx) { - glDeleteVertexArrays(1, &vao_id); - } - else { - orphans_add(ctx, &ctx->orphaned_vertarray_ids, vao_id); - } + ctx->vao_free(vao_id); } void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx) { BLI_assert(ctx); - if (ctx == active_ctx) { - glDeleteFramebuffers(1, &fbo_id); - } - else { - orphans_add(ctx, &ctx->orphaned_framebuffer_ids, fbo_id); - } + ctx->fbo_free(fbo_id); } void GPU_buf_free(GLuint buf_id) { - if (active_ctx) { - glDeleteBuffers(1, &buf_id); - } - else { - orphans_add(NULL, &orphaned_buffer_ids, buf_id); - } + /* FIXME active_ctx can be NULL */ + BLI_assert(active_ctx); + active_ctx->buf_free(buf_id); } void GPU_tex_free(GLuint tex_id) { - if (active_ctx) { - glDeleteTextures(1, &tex_id); - } - else { - orphans_add(NULL, &orphaned_texture_ids, tex_id); - } -} - -/* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices - * which are not shared across contexts. So we need to keep track of - * ownership. */ - -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - ctx->orphans_mutex.lock(); - ctx->batches.emplace(batch); - ctx->orphans_mutex.unlock(); -} - -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - ctx->orphans_mutex.lock(); - ctx->batches.erase(batch); - ctx->orphans_mutex.unlock(); -} - -void gpu_context_add_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - ctx->orphans_mutex.lock(); - ctx->framebuffers.emplace(fb); - ctx->orphans_mutex.unlock(); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_remove_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - ctx->orphans_mutex.lock(); - ctx->framebuffers.erase(fb); - ctx->orphans_mutex.unlock(); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_active_framebuffer_set(GPUContext *ctx, GPUFrameBuffer *fb) -{ - ctx->current_fbo = fb; -} - -GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx) -{ - return ctx->current_fbo; -} - -struct GPUMatrixState *gpu_context_active_matrix_state_get() -{ + /* FIXME active_ctx can be NULL */ BLI_assert(active_ctx); - return active_ctx->matrix_state; + active_ctx->tex_free(tex_id); } diff --git a/source/blender/gpu/intern/gpu_context_private.h b/source/blender/gpu/intern/gpu_context_private.h deleted file mode 100644 index f64cdf439a1..00000000000 --- a/source/blender/gpu/intern/gpu_context_private.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 - * - * This interface allow GPU to manage GL objects for multiple context and threads. - */ - -#ifndef __GPU_CONTEXT_PRIVATE_H__ -#define __GPU_CONTEXT_PRIVATE_H__ - -#include "GPU_context.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct GPUFrameBuffer; - -GLuint GPU_vao_default(void); -GLuint GPU_framebuffer_default(void); - -/* These require a gl ctx bound. */ -GLuint GPU_buf_alloc(void); -GLuint GPU_tex_alloc(void); -GLuint GPU_vao_alloc(void); -GLuint GPU_fbo_alloc(void); - -/* These can be called any threads even without gl ctx. */ -void GPU_buf_free(GLuint buf_id); -void GPU_tex_free(GLuint tex_id); -/* These two need the ctx the id was created with. */ -void GPU_vao_free(GLuint vao_id, GPUContext *ctx); -void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx); - -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch); -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch); - -void gpu_context_add_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); -void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); - -void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb); -struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx); - -struct GPUMatrixState *gpu_context_active_matrix_state_get(void); - -#ifdef __cplusplus -} -#endif - -#endif /* __GPU_CONTEXT_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh new file mode 100644 index 00000000000..57e58d474a5 --- /dev/null +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -0,0 +1,98 @@ +/* + * 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 + * + * This interface allow GPU to manage GL objects for multiple context and threads. + */ + +#pragma once + +#include "GPU_context.h" + +#include <pthread.h> + +struct GPUBatch; +struct GPUFrameBuffer; +struct GPUMatrixState; + +// TODO(fclem) this requires too much refactor for now. +// namespace blender { +// namespace gpu { + +class GPUContext { + public: + GPUContext(); + virtual ~GPUContext(){}; + + virtual void activate(void) = 0; + virtual void deactivate(void) = 0; + + virtual void draw_batch(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count) = 0; + virtual void draw_primitive(GPUPrimType prim_type, int v_count) = 0; + + virtual void batch_add(GPUBatch *){}; + virtual void batch_remove(GPUBatch *){}; + + virtual void framebuffer_add(struct GPUFrameBuffer *){}; + virtual void framebuffer_remove(struct GPUFrameBuffer *){}; + + /* TODO(fclem) These are gl specifics. To be hidden inside the gl backend. */ + virtual GLuint default_framebuffer_get(void) = 0; + virtual GLuint buf_alloc(void) = 0; + virtual GLuint tex_alloc(void) = 0; + virtual GLuint vao_alloc(void) = 0; + virtual GLuint fbo_alloc(void) = 0; + virtual void vao_free(GLuint vao_id) = 0; + virtual void fbo_free(GLuint fbo_id) = 0; + virtual void buf_free(GLuint buf_id) = 0; + virtual void tex_free(GLuint tex_id) = 0; + + /** State managment */ + GPUFrameBuffer *current_fbo = NULL; + GPUMatrixState *matrix_state = NULL; + + bool is_active_on_thread(void); + + protected: + /** Thread on which this context is active. */ + pthread_t thread_; + bool thread_is_used_; +}; + +/* Return context currently bound to the caller's thread. + * Note: this assume a context is active! */ +GPUContext *GPU_ctx(void); + +/* These require a gl ctx bound. */ +GLuint GPU_buf_alloc(void); +GLuint GPU_tex_alloc(void); +GLuint GPU_vao_alloc(void); +GLuint GPU_fbo_alloc(void); + +/* These can be called any threads even without gl ctx. */ +void GPU_buf_free(GLuint buf_id); +void GPU_tex_free(GLuint tex_id); +/* These two need the ctx the id was created with. */ +void GPU_vao_free(GLuint vao_id, GPUContext *ctx); +void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx); + +// } // namespace gpu +// } // namespace blender diff --git a/source/blender/gpu/intern/gpu_element.cc b/source/blender/gpu/intern/gpu_element.cc index 9f104ab3fec..e71eb805548 100644 --- a/source/blender/gpu/intern/gpu_element.cc +++ b/source/blender/gpu/intern/gpu_element.cc @@ -28,7 +28,7 @@ #include "GPU_element.h" #include "GPU_glew.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include <stdlib.h> diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 056a449ac6d..657aef0a6c0 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -33,7 +33,7 @@ #include "GPU_shader.h" #include "GPU_texture.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_private.h" typedef enum { @@ -188,10 +188,10 @@ GPUFrameBuffer *GPU_framebuffer_active_get(void) { GPUContext *ctx = GPU_context_active_get(); if (ctx) { - return gpu_context_active_framebuffer_get(ctx); + return ctx->current_fbo; } else { - return 0; + return NULL; } } @@ -199,7 +199,7 @@ static void gpu_framebuffer_current_set(GPUFrameBuffer *fb) { GPUContext *ctx = GPU_context_active_get(); if (ctx) { - gpu_context_active_framebuffer_set(ctx, fb); + ctx->current_fbo = fb; } } @@ -216,7 +216,7 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb) { fb->object = GPU_fbo_alloc(); fb->ctx = GPU_context_active_get(); - gpu_context_add_framebuffer(fb->ctx, fb); + fb->ctx->framebuffer_add(fb); } void GPU_framebuffer_free(GPUFrameBuffer *fb) @@ -231,7 +231,7 @@ void GPU_framebuffer_free(GPUFrameBuffer *fb) if (fb->object != 0) { /* This restores the framebuffer if it was bound */ GPU_fbo_free(fb->object, fb->ctx); - gpu_context_remove_framebuffer(fb->ctx, fb); + fb->ctx->framebuffer_remove(fb); } if (GPU_framebuffer_active_get() == fb) { @@ -560,7 +560,7 @@ void GPU_framebuffer_bind(GPUFrameBuffer *fb) void GPU_framebuffer_restore(void) { if (GPU_framebuffer_active_get() != NULL) { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); + glBindFramebuffer(GL_FRAMEBUFFER, GPU_ctx()->default_framebuffer_get()); gpu_framebuffer_current_set(NULL); glDisable(GL_FRAMEBUFFER_SRGB); GPU_shader_set_framebuffer_srgb_target(false); @@ -796,7 +796,7 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, gpu_framebuffer_current_set(prev_fb); } else { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); + glBindFramebuffer(GL_FRAMEBUFFER, GPU_ctx()->default_framebuffer_get()); gpu_framebuffer_current_set(NULL); } } @@ -1037,7 +1037,7 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) gpu_print_framebuffer_error(status, NULL); } - glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_ctx()->default_framebuffer_get()); } void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index ac3e653c9ff..73ef511af96 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -33,7 +33,7 @@ #include "GPU_texture.h" #include "gpu_attr_binding_private.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_primitive_private.h" #include "gpu_shader_private.h" #include "gpu_vertex_format_private.h" diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index c15bb1fba19..23505213cb4 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -23,7 +23,7 @@ #include "GPU_shader_interface.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_matrix_private.h" #define SUPPRESS_GENERIC_MATRIX_API @@ -61,10 +61,10 @@ typedef struct GPUMatrixState { */ } GPUMatrixState; -#define ModelViewStack gpu_context_active_matrix_state_get()->model_view_stack +#define ModelViewStack GPU_ctx()->matrix_state->model_view_stack #define ModelView ModelViewStack.stack[ModelViewStack.top] -#define ProjectionStack gpu_context_active_matrix_state_get()->projection_stack +#define ProjectionStack GPU_ctx()->matrix_state->projection_stack #define Projection ProjectionStack.stack[ProjectionStack.top] GPUMatrixState *GPU_matrix_state_create(void) @@ -95,13 +95,13 @@ void GPU_matrix_state_discard(GPUMatrixState *state) static void gpu_matrix_state_active_set_dirty(bool value) { - GPUMatrixState *state = gpu_context_active_matrix_state_get(); + GPUMatrixState *state = GPU_ctx()->matrix_state; state->dirty = value; } void GPU_matrix_reset(void) { - GPUMatrixState *state = gpu_context_active_matrix_state_get(); + GPUMatrixState *state = GPU_ctx()->matrix_state; state->model_view_stack.top = 0; state->projection_stack.top = 0; unit_m4(ModelView); @@ -693,7 +693,7 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface) bool GPU_matrix_dirty_get(void) { - GPUMatrixState *state = gpu_context_active_matrix_state_get(); + GPUMatrixState *state = GPU_ctx()->matrix_state; return state->dirty; } @@ -706,13 +706,13 @@ BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mis int GPU_matrix_stack_level_get_model_view(void) { - GPUMatrixState *state = gpu_context_active_matrix_state_get(); + GPUMatrixState *state = GPU_ctx()->matrix_state; return (int)state->model_view_stack.top; } int GPU_matrix_stack_level_get_projection(void) { - GPUMatrixState *state = gpu_context_active_matrix_state_get(); + GPUMatrixState *state = GPU_ctx()->matrix_state; return (int)state->projection_stack.top; } diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index b37c8176a16..ec14a59211c 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -33,7 +33,7 @@ #include "GPU_shader_interface.h" #include "gpu_batch_private.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include <stddef.h> #include <stdlib.h> diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 8a3a8a382f8..3f3316fc1b5 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -43,7 +43,7 @@ #include "GPU_platform.h" #include "GPU_texture.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #define WARN_NOT_BOUND(_tex) \ { \ diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.cc b/source/blender/gpu/intern/gpu_uniformbuffer.cc index b2e31894f9d..15bc412ce8d 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.cc +++ b/source/blender/gpu/intern/gpu_uniformbuffer.cc @@ -27,7 +27,7 @@ #include "BLI_blenlib.h" #include "BLI_math_base.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_node_graph.h" #include "GPU_extensions.h" diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index eda6d1c7300..67ad8835b6a 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -27,7 +27,7 @@ #include "GPU_vertex_buffer.h" -#include "gpu_context_private.h" +#include "gpu_context_private.hh" #include "gpu_vertex_format_private.h" #include <stdlib.h> diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc new file mode 100644 index 00000000000..9522a8f851d --- /dev/null +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -0,0 +1,124 @@ +/* + * 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 + */ + +#include "BLI_assert.h" +#include "BLI_utildefines.h" + +#include "GPU_glew.h" + +#include "gl_context.hh" + +using namespace blender::gpu; + +void GLContext::draw_batch(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count) +{ + /* Verify there is enough data do draw. */ + /* TODO(fclem) Nice to have but this is invalid when using procedural draw-calls. + * The right assert would be to check if there is an enabled attribute from each VBO + * and check their length. */ + // BLI_assert(i_first + i_count <= (batch->inst ? batch->inst->vertex_len : INT_MAX)); + // BLI_assert(v_first + v_count <= + // (batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len)); + +#ifdef __APPLE__ + GLuint vao = 0; +#endif + + if (!GPU_arb_base_instance_is_supported()) { + if (i_first > 0) { +#ifdef __APPLE__ + /** + * There seems to be a nasty bug when drawing using the same VAO reconfiguring. (see T71147) + * We just use a throwaway VAO for that. Note that this is likely to degrade performance. + **/ + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); +#else + /* If using offset drawing with instancing, we must + * use the default VAO and redo bindings. */ + glBindVertexArray(default_vao_); +#endif + batch_update_program_bindings(batch, i_first); + } + else { + /* Previous call could have bind the default vao + * see above. */ + glBindVertexArray(batch->vao_id); + } + } + + if (batch->elem) { + const GPUIndexBuf *el = batch->elem; + GLenum index_type = INDEX_TYPE(el); + GLint base_index = BASE_INDEX(el); + void *v_first_ofs = (GLuint *)0 + v_first + el->index_start; + +#if GPU_TRACK_INDEX_RANGE + if (el->index_type == GPU_INDEX_U16) { + v_first_ofs = (GLushort *)0 + v_first + el->index_start; + } +#endif + + if (GPU_arb_base_instance_is_supported()) { + glDrawElementsInstancedBaseVertexBaseInstance( + batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); + } + else { + glDrawElementsInstancedBaseVertex( + batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index); + } + } + else { +#ifdef __APPLE__ + glDisable(GL_PRIMITIVE_RESTART); +#endif + if (GPU_arb_base_instance_is_supported()) { + glDrawArraysInstancedBaseInstance(batch->gl_prim_type, v_first, v_count, i_count, i_first); + } + else { + glDrawArraysInstanced(batch->gl_prim_type, v_first, v_count, i_count); + } +#ifdef __APPLE__ + glEnable(GL_PRIMITIVE_RESTART); +#endif + } + +#ifdef __APPLE__ + if (vao != 0) { + glDeleteVertexArrays(1, &vao); + } +#endif +} + +void GLContext::draw_primitive(GPUPrimType prim_type, int v_count) +{ + /* we cannot draw without vao ... annoying ... */ + glBindVertexArray(default_vao_); + + GLenum type = convert_prim_type_to_gl(prim_type); + glDrawArrays(type, 0, v_count); + + /* Performance hog if you are drawing with the same vao multiple time. + * Only activate for debugging.*/ + // glBindVertexArray(0); +}
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc new file mode 100644 index 00000000000..918650e10c5 --- /dev/null +++ b/source/blender/gpu/opengl/gl_context.cc @@ -0,0 +1,188 @@ +/* + * 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 + * + * Manage GL vertex array IDs in a thread-safe way + * Use these instead of glGenBuffers & its friends + * - alloc must be called from a thread that is bound + * to the context that will be used for drawing with + * this vao. + * - free can be called from any thread + */ + +#include "BLI_assert.h" +#include "BLI_utildefines.h" + +#include "GPU_framebuffer.h" + +#include "gpu_context_private.hh" + +#include "gl_context.hh" + +// TODO(fclem) this requires too much refactor for now. +// using namespace blender::gpu; + +GLContext::GLContext() : GPUContext() +{ + glGenVertexArrays(1, &default_vao_); + /* TODO(fclem) call ghost here. */ + // GHOST_GetDefaultOpenGLFramebuffer(g_WS.ghost_window); + default_framebuffer_ = 0; +} + +GLContext::~GLContext() +{ + BLI_assert(ctx->orphaned_vertarray_ids.empty()); + /* For now don't allow GPUFrameBuffers to be reuse in another ctx. */ + BLI_assert(ctx->framebuffers.empty()); + + /* delete remaining vaos */ + while (!ctx->batches.empty()) { + /* this removes the array entry */ + GPU_batch_vao_cache_clear(*ctx->batches.begin()); + } + GPU_matrix_state_discard(ctx->matrix_state); + glDeleteVertexArrays(1, &ctx->default_vao); +} + +void GLContext::activate(void) +{ +#ifdef DEBUG + /* Make sure no other context is already bound to this thread. */ + BLI_assert(pthread_equal(pthread_self(), thread_)); + /* Make sure no other thread has locked it. */ + BLI_assert(thread_is_used_ == false); + thread_ = pthread_self(); + thread_is_used_ = true; +#endif + orphans_clear(); +}; + +void GLContext::deactivate(void) +{ +#ifdef DEBUG + thread_is_used_ = false; +#endif +}; + +void GLContext::orphans_clear(void) override +{ + /* Check if context has been activated by another thread! */ + BLI_assert(pthread_equal(pthread_self(), ctx->thread)); + + orphans_mutex_.lock(); + if (!orphaned_vertarrays_.empty()) { + glDeleteVertexArrays((uint)orphaned_vertarrays_.size(), orphaned_vertarrays_.data()); + orphaned_vertarrays_.clear(); + } + if (!orphaned_framebuffers_.empty()) { + glDeleteFramebuffers((uint)orphaned_framebuffers_.size(), orphaned_framebuffers_.data()); + orphaned_framebuffers_.clear(); + } + if (!orphaned_buffers_.empty()) { + glDeleteBuffers((uint)orphaned_buffers_.size(), orphaned_buffers_.data()); + orphaned_buffers_.clear(); + } + if (!orphaned_textures_.empty()) { + glDeleteTextures((uint)orphaned_textures_.size(), orphaned_textures_.data()); + orphaned_textures_.clear(); + } + orphans_mutex_.unlock(); +}; + +void GLContext::orphans_add(std::vector<GLuint> *orphan_list, GLuint id) +{ + orphans_mutex_->lock(); + orphan_list->emplace_back(id); + orphans_mutex_->unlock(); +} + +void GLContext::vao_free(GLuint vao_id) +{ + if (ctx == GPU_context_active_get()) { + glDeleteVertexArrays(1, &vao_id); + } + else { + orphans_add(ctx, &ctx->orphaned_vertarray_ids, vao_id); + } +} + +void GLContext::fbo_free(GLuint fbo_id) +{ + if (ctx == GPU_context_active_get()) { + glDeleteFramebuffers(1, &fbo_id); + } + else { + orphans_add(ctx, &ctx->orphaned_framebuffer_ids, fbo_id); + } +} + +void GLContext::buf_free(GLuint buf_id) +{ + if (GPU_context_active_get()) { + glDeleteBuffers(1, &buf_id); + } + else { + orphans_add(&orphaned_buffer_ids, buf_id); + } +} + +void GLContext::tex_free(GLuint tex_id) +{ + if (GPU_context_active_get()) { + glDeleteTextures(1, &tex_id); + } + else { + orphans_add(&orphaned_texture_ids, tex_id); + } +} + +GLuint GLContext::vao_alloc(void) +{ + GLuint new_vao_id = 0; + orphans_clear(); + glGenVertexArrays(1, &new_vao_id); + return new_vao_id; +} + +GLuint GLContext::fbo_alloc(void) +{ + GLuint new_fbo_id = 0; + orphans_clear(); + glGenFramebuffers(1, &new_fbo_id); + return new_fbo_id; +} + +GLuint GLContext::buf_alloc(void) +{ + GLuint new_buffer_id = 0; + orphans_clear(); + glGenBuffers(1, &new_buffer_id); + return new_buffer_id; +} + +GLuint GLContext::tex_alloc(void) +{ + GLuint new_texture_id = 0; + orphans_clear(); + glGenTextures(1, &new_texture_id); + return new_texture_id; +} diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh new file mode 100644 index 00000000000..26643fb4b39 --- /dev/null +++ b/source/blender/gpu/opengl/gl_context.hh @@ -0,0 +1,93 @@ +/* + * 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 "../intern/gpu_context_private.hh" + +#include "glew-mx.h" + +#include <iostream> +#include <mutex> +#include <unordered_set> +#include <vector> + +// TODO(fclem) this requires too much refactor for now. +// namespace blender { +// namespace gpu { + +class GLContext : public GPUContext { + public: + GLContext(); + ~GLContext(){}; + + void activate(void) override; + void deactivate(void) override; + + void draw_batch(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count) override; + void draw_primitive(GPUPrimType prim_type, int v_count) override; + + void batch_add(GPUBatch *batch) override; + void batch_remove(GPUBatch *batch) override; + + /* TODO remove */ + GLuint tex_alloc(void) override; + GLuint vao_alloc(void) override; + GLuint buf_alloc(void) override; + GLuint fbo_alloc(void) override; + void vao_free(GLuint vao_id) override; + void fbo_free(GLuint fbo_id) override; + void buf_free(GLuint buf_id) override; + void tex_free(GLuint tex_id) override; + GLuint default_framebuffer_get(void) override + { + return default_framebuffer_; + }; + + private: + void orphans_add(std::vector<GLuint> *orphan_list, GLuint id); + void orphans_clear(void); + + /** + * Batches & Framebuffers are not shared accross contexts. + * For this reason we keep a list of them per GPUBatch & GPUFramebuffer. + * However this list needs to be updated in the case a GPUContext is destroyed. + */ + std::unordered_set<GPUBatch *> batches; + std::unordered_set<GPUFrameBuffer *> framebuffers; + + std::vector<GLuint> orphaned_vertarrays_; + std::vector<GLuint> orphaned_framebuffers_; + std::vector<GLuint> orphaned_buffers_; + std::vector<GLuint> orphaned_textures_; + + /** Mutex for the above structures. */ + /** todo: try spinlock instead */ + std::mutex orphans_mutex_; + + GLuint default_vao_; + GLuint default_framebuffer_; +}; + +// } // namespace gpu +// } // namespace blender |