GPUBackend: Add new GPUBackend object to manage GL object allocations
This just set a global object responsible for allocating new objects in a thread safe way without needing any GPUContext bound to this thread. This also introduce the GLContext which will contain all the GL related functions for the current context. # Conflicts: # source/blender/gpu/intern/gpu_context.cc
This commit is contained in:
		@@ -52,59 +52,35 @@
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
static std::vector<GLuint> orphaned_buffer_ids;
 | 
			
		||||
static std::vector<GLuint> orphaned_texture_ids;
 | 
			
		||||
 | 
			
		||||
static std::mutex orphans_mutex;
 | 
			
		||||
static std::mutex main_context_mutex;
 | 
			
		||||
using namespace blender::gpu;
 | 
			
		||||
 | 
			
		||||
static thread_local GPUContext *active_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void orphans_add(GPUContext *ctx, std::vector<GLuint> *orphan_list, GLuint id)
 | 
			
		||||
{
 | 
			
		||||
  std::mutex *mutex = (ctx) ? &ctx->orphans_mutex : &orphans_mutex;
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name GPUContext methods
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
  mutex->lock();
 | 
			
		||||
  orphan_list->emplace_back(id);
 | 
			
		||||
  mutex->unlock();
 | 
			
		||||
GPUContext::GPUContext()
 | 
			
		||||
{
 | 
			
		||||
  thread_ = pthread_self();
 | 
			
		||||
  is_active_ = false;
 | 
			
		||||
  matrix_state = GPU_matrix_state_create();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void orphans_clear(GPUContext *ctx)
 | 
			
		||||
GPUContext::~GPUContext()
 | 
			
		||||
{
 | 
			
		||||
  /* 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();
 | 
			
		||||
  GPU_matrix_state_discard(matrix_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GPUContext::is_active_on_thread(void)
 | 
			
		||||
{
 | 
			
		||||
  return (this == active_ctx) && pthread_equal(pthread_self(), thread_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
GPUContext *GPU_context_create(void *ghost_window)
 | 
			
		||||
{
 | 
			
		||||
  if (gpu_backend_get() == NULL) {
 | 
			
		||||
@@ -113,15 +89,7 @@ GPUContext *GPU_context_create(void *ghost_window)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window);
 | 
			
		||||
  glGenVertexArrays(1, &ctx->default_vao);
 | 
			
		||||
  if (ghost_window != NULL) {
 | 
			
		||||
    ctx->default_framebuffer = GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    ctx->default_framebuffer = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ctx->matrix_state = GPU_matrix_state_create();
 | 
			
		||||
  GPU_context_active_set(ctx);
 | 
			
		||||
  return ctx;
 | 
			
		||||
}
 | 
			
		||||
@@ -129,21 +97,6 @@ GPUContext *GPU_context_create(void *ghost_window)
 | 
			
		||||
/* 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);
 | 
			
		||||
  delete ctx;
 | 
			
		||||
  active_ctx = NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -151,22 +104,15 @@ 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;
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  if (ctx) {
 | 
			
		||||
    orphans_clear(ctx);
 | 
			
		||||
    active_ctx->deactivate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  active_ctx = ctx;
 | 
			
		||||
 | 
			
		||||
  if (ctx) {
 | 
			
		||||
    ctx->activate();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GPUContext *GPU_context_active_get(void)
 | 
			
		||||
@@ -177,23 +123,18 @@ GPUContext *GPU_context_active_get(void)
 | 
			
		||||
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;
 | 
			
		||||
  return static_cast<GLContext *>(active_ctx)->default_vao_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GLuint GPU_framebuffer_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_framebuffer;
 | 
			
		||||
  return static_cast<GLContext *>(active_ctx)->default_framebuffer_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GLuint GPU_vao_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
  GLuint new_vao_id = 0;
 | 
			
		||||
  orphans_clear(active_ctx);
 | 
			
		||||
  glGenVertexArrays(1, &new_vao_id);
 | 
			
		||||
  return new_vao_id;
 | 
			
		||||
}
 | 
			
		||||
@@ -201,7 +142,6 @@ GLuint GPU_vao_alloc(void)
 | 
			
		||||
GLuint GPU_fbo_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
  GLuint new_fbo_id = 0;
 | 
			
		||||
  orphans_clear(active_ctx);
 | 
			
		||||
  glGenFramebuffers(1, &new_fbo_id);
 | 
			
		||||
  return new_fbo_id;
 | 
			
		||||
}
 | 
			
		||||
@@ -209,7 +149,6 @@ GLuint GPU_fbo_alloc(void)
 | 
			
		||||
GLuint GPU_buf_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
  GLuint new_buffer_id = 0;
 | 
			
		||||
  orphans_clear(active_ctx);
 | 
			
		||||
  glGenBuffers(1, &new_buffer_id);
 | 
			
		||||
  return new_buffer_id;
 | 
			
		||||
}
 | 
			
		||||
@@ -217,51 +156,32 @@ GLuint GPU_buf_alloc(void)
 | 
			
		||||
GLuint GPU_tex_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
  GLuint new_texture_id = 0;
 | 
			
		||||
  orphans_clear(active_ctx);
 | 
			
		||||
  glGenTextures(1, &new_texture_id);
 | 
			
		||||
  return new_texture_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  }
 | 
			
		||||
  static_cast<GLContext *>(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);
 | 
			
		||||
  }
 | 
			
		||||
  static_cast<GLContext *>(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);
 | 
			
		||||
  }
 | 
			
		||||
  /* TODO avoid using backend */
 | 
			
		||||
  GPUBackend *backend = gpu_backend_get();
 | 
			
		||||
  static_cast<GLBackend *>(backend)->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);
 | 
			
		||||
  }
 | 
			
		||||
  /* TODO avoid using backend */
 | 
			
		||||
  GPUBackend *backend = gpu_backend_get();
 | 
			
		||||
  static_cast<GLBackend *>(backend)->tex_free(tex_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices
 | 
			
		||||
@@ -271,26 +191,20 @@ void GPU_tex_free(GLuint tex_id)
 | 
			
		||||
void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch)
 | 
			
		||||
{
 | 
			
		||||
  BLI_assert(ctx);
 | 
			
		||||
  ctx->orphans_mutex.lock();
 | 
			
		||||
  ctx->batches.emplace(batch);
 | 
			
		||||
  ctx->orphans_mutex.unlock();
 | 
			
		||||
  static_cast<GLContext *>(ctx)->batch_register(batch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch)
 | 
			
		||||
{
 | 
			
		||||
  BLI_assert(ctx);
 | 
			
		||||
  ctx->orphans_mutex.lock();
 | 
			
		||||
  ctx->batches.erase(batch);
 | 
			
		||||
  ctx->orphans_mutex.unlock();
 | 
			
		||||
  static_cast<GLContext *>(ctx)->batch_unregister(batch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
  static_cast<GLContext *>(ctx)->framebuffer_register(fb);
 | 
			
		||||
#else
 | 
			
		||||
  UNUSED_VARS(ctx, fb);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -300,9 +214,7 @@ 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();
 | 
			
		||||
  static_cast<GLContext *>(ctx)->framebuffer_unregister(fb);
 | 
			
		||||
#else
 | 
			
		||||
  UNUSED_VARS(ctx, fb);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -324,6 +236,14 @@ struct GPUMatrixState *gpu_context_active_matrix_state_get()
 | 
			
		||||
  return active_ctx->matrix_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Main context global mutex
 | 
			
		||||
 *
 | 
			
		||||
 * Used to avoid crash on some old drivers.
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
static std::mutex main_context_mutex;
 | 
			
		||||
 | 
			
		||||
void GPU_context_main_lock(void)
 | 
			
		||||
{
 | 
			
		||||
  main_context_mutex.lock();
 | 
			
		||||
@@ -334,6 +254,8 @@ void GPU_context_main_unlock(void)
 | 
			
		||||
  main_context_mutex.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Backend selection
 | 
			
		||||
 * \{ */
 | 
			
		||||
@@ -358,7 +280,11 @@ void GPU_backend_init(eGPUBackendType backend_type)
 | 
			
		||||
 | 
			
		||||
void GPU_backend_exit(void)
 | 
			
		||||
{
 | 
			
		||||
  delete g_backend;
 | 
			
		||||
  if (g_backend) {
 | 
			
		||||
    /* TODO assert no resource left. Currently UI textures are still not freed in their context
 | 
			
		||||
     * correctly. */
 | 
			
		||||
    delete g_backend;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GPUBackend *gpu_backend_get(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "MEM_guardedalloc.h"
 | 
			
		||||
 | 
			
		||||
#include "GPU_context.h"
 | 
			
		||||
 | 
			
		||||
/* TODO cleanup this ifdef */
 | 
			
		||||
@@ -37,34 +39,29 @@
 | 
			
		||||
#  include <vector>
 | 
			
		||||
 | 
			
		||||
struct GPUFrameBuffer;
 | 
			
		||||
struct GPUMatrixState;
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 public:
 | 
			
		||||
  /** State managment */
 | 
			
		||||
  GPUFrameBuffer *current_fbo = NULL;
 | 
			
		||||
  GPUMatrixState *matrix_state = NULL;
 | 
			
		||||
 | 
			
		||||
  GPUContext()
 | 
			
		||||
  {
 | 
			
		||||
#  if TRUST_NO_ONE
 | 
			
		||||
    thread_is_used = false;
 | 
			
		||||
#  endif
 | 
			
		||||
    current_fbo = 0;
 | 
			
		||||
  };
 | 
			
		||||
 protected:
 | 
			
		||||
  /** Thread on which this context is active. */
 | 
			
		||||
  pthread_t thread_;
 | 
			
		||||
  bool is_active_;
 | 
			
		||||
 | 
			
		||||
  virtual ~GPUContext(){};
 | 
			
		||||
 public:
 | 
			
		||||
  GPUContext();
 | 
			
		||||
  virtual ~GPUContext();
 | 
			
		||||
 | 
			
		||||
  virtual void activate(void) = 0;
 | 
			
		||||
  virtual void deactivate(void) = 0;
 | 
			
		||||
 | 
			
		||||
  bool is_active_on_thread(void);
 | 
			
		||||
 | 
			
		||||
  MEM_CXX_CLASS_ALLOC_FUNCS("GPUContext")
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -93,8 +93,6 @@ void GPU_exit(void)
 | 
			
		||||
  gpu_extensions_exit();
 | 
			
		||||
  gpu_platform_exit(); /* must come last */
 | 
			
		||||
 | 
			
		||||
  GPU_backend_exit();
 | 
			
		||||
 | 
			
		||||
  initialized = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,12 +25,33 @@
 | 
			
		||||
 | 
			
		||||
#include "gpu_backend.hh"
 | 
			
		||||
 | 
			
		||||
#include "BLI_vector.hh"
 | 
			
		||||
 | 
			
		||||
#include "gl_context.hh"
 | 
			
		||||
 | 
			
		||||
namespace blender {
 | 
			
		||||
namespace gpu {
 | 
			
		||||
 | 
			
		||||
class GLBackend : public GPUBackend {
 | 
			
		||||
 private:
 | 
			
		||||
  GLSharedOrphanLists shared_orphan_list_;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  GPUContext *context_alloc(void *ghost_window)
 | 
			
		||||
  {
 | 
			
		||||
    return new GLContext(ghost_window);
 | 
			
		||||
    return new GLContext(ghost_window, shared_orphan_list_);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /* TODO remove */
 | 
			
		||||
  void buf_free(GLuint buf_id);
 | 
			
		||||
  void tex_free(GLuint tex_id);
 | 
			
		||||
  void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, unsigned int id)
 | 
			
		||||
  {
 | 
			
		||||
    list_mutex.lock();
 | 
			
		||||
    orphan_list.append(id);
 | 
			
		||||
    list_mutex.unlock();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gpu
 | 
			
		||||
}  // namespace blender
 | 
			
		||||
 
 | 
			
		||||
@@ -30,24 +30,209 @@
 | 
			
		||||
 | 
			
		||||
#include "gpu_context_private.h"
 | 
			
		||||
 | 
			
		||||
#include "gl_backend.hh" /* TODO remove */
 | 
			
		||||
#include "gl_context.hh"
 | 
			
		||||
 | 
			
		||||
// TODO(fclem) this requires too much refactor for now.
 | 
			
		||||
// using namespace blender::gpu;
 | 
			
		||||
using namespace blender::gpu;
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Constructor / Destructor
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
GLContext::GLContext(void *ghost_window) : GPUContext()
 | 
			
		||||
GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list)
 | 
			
		||||
    : shared_orphan_list_(shared_orphan_list)
 | 
			
		||||
{
 | 
			
		||||
  default_framebuffer_ = ghost_window ?
 | 
			
		||||
                             GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window) :
 | 
			
		||||
                             0;
 | 
			
		||||
 | 
			
		||||
  glGenVertexArrays(1, &default_vao_);
 | 
			
		||||
 | 
			
		||||
  float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 | 
			
		||||
  glGenBuffers(1, &default_attr_vbo_);
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_);
 | 
			
		||||
  glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
 | 
			
		||||
  glBindBuffer(GL_ARRAY_BUFFER, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GLContext::~GLContext()
 | 
			
		||||
{
 | 
			
		||||
  BLI_assert(orphaned_framebuffers_.is_empty());
 | 
			
		||||
  BLI_assert(orphaned_vertarrays_.is_empty());
 | 
			
		||||
  /* For now don't allow GPUFrameBuffers to be reuse in another context. */
 | 
			
		||||
  BLI_assert(framebuffers_.is_empty());
 | 
			
		||||
  /* Delete vaos so the batch can be reused in another context. */
 | 
			
		||||
  for (GPUBatch *batch : batches_) {
 | 
			
		||||
    GPU_batch_vao_cache_clear(batch);
 | 
			
		||||
  }
 | 
			
		||||
  glDeleteVertexArrays(1, &default_vao_);
 | 
			
		||||
  glDeleteBuffers(1, &default_attr_vbo_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Activate / Deactivate context
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
void GLContext::activate(void)
 | 
			
		||||
{
 | 
			
		||||
  /* Make sure no other context is already bound to this thread. */
 | 
			
		||||
  BLI_assert(is_active_ == false);
 | 
			
		||||
 | 
			
		||||
  is_active_ = true;
 | 
			
		||||
  thread_ = pthread_self();
 | 
			
		||||
 | 
			
		||||
  /* Clear accumulated orphans. */
 | 
			
		||||
  orphans_clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::deactivate(void)
 | 
			
		||||
{
 | 
			
		||||
  is_active_ = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Safe object deletion
 | 
			
		||||
 *
 | 
			
		||||
 * GPU objects can be freed when the context is not bound.
 | 
			
		||||
 * In this case we delay the deletion until the context is bound again.
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
void GLSharedOrphanLists::orphans_clear(void)
 | 
			
		||||
{
 | 
			
		||||
  /* Check if any context is active on this thread! */
 | 
			
		||||
  BLI_assert(GPU_context_active_get());
 | 
			
		||||
 | 
			
		||||
  lists_mutex.lock();
 | 
			
		||||
  if (!buffers.is_empty()) {
 | 
			
		||||
    glDeleteBuffers((uint)buffers.size(), buffers.data());
 | 
			
		||||
    buffers.clear();
 | 
			
		||||
  }
 | 
			
		||||
  if (!textures.is_empty()) {
 | 
			
		||||
    glDeleteTextures((uint)textures.size(), textures.data());
 | 
			
		||||
    textures.clear();
 | 
			
		||||
  }
 | 
			
		||||
  lists_mutex.unlock();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void GLContext::orphans_clear(void)
 | 
			
		||||
{
 | 
			
		||||
  /* Check if context has been activated by another thread! */
 | 
			
		||||
  BLI_assert(this->is_active_on_thread());
 | 
			
		||||
 | 
			
		||||
  lists_mutex_.lock();
 | 
			
		||||
  if (!orphaned_vertarrays_.is_empty()) {
 | 
			
		||||
    glDeleteVertexArrays((uint)orphaned_vertarrays_.size(), orphaned_vertarrays_.data());
 | 
			
		||||
    orphaned_vertarrays_.clear();
 | 
			
		||||
  }
 | 
			
		||||
  if (!orphaned_framebuffers_.is_empty()) {
 | 
			
		||||
    glDeleteFramebuffers((uint)orphaned_framebuffers_.size(), orphaned_framebuffers_.data());
 | 
			
		||||
    orphaned_framebuffers_.clear();
 | 
			
		||||
  }
 | 
			
		||||
  lists_mutex_.unlock();
 | 
			
		||||
 | 
			
		||||
  shared_orphan_list_.orphans_clear();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void GLContext::orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id)
 | 
			
		||||
{
 | 
			
		||||
  list_mutex.lock();
 | 
			
		||||
  orphan_list.append(id);
 | 
			
		||||
  list_mutex.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::vao_free(GLuint vao_id)
 | 
			
		||||
{
 | 
			
		||||
  if (this == GPU_context_active_get()) {
 | 
			
		||||
    glDeleteVertexArrays(1, &vao_id);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    orphans_add(orphaned_vertarrays_, lists_mutex_, vao_id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::fbo_free(GLuint fbo_id)
 | 
			
		||||
{
 | 
			
		||||
  if (this == GPU_context_active_get()) {
 | 
			
		||||
    glDeleteFramebuffers(1, &fbo_id);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    orphans_add(orphaned_framebuffers_, lists_mutex_, fbo_id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLBackend::buf_free(GLuint buf_id)
 | 
			
		||||
{
 | 
			
		||||
  /* Any context can free. */
 | 
			
		||||
  if (GPU_context_active_get()) {
 | 
			
		||||
    glDeleteBuffers(1, &buf_id);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    orphans_add(shared_orphan_list_.buffers, shared_orphan_list_.lists_mutex, buf_id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLBackend::tex_free(GLuint tex_id)
 | 
			
		||||
{
 | 
			
		||||
  /* Any context can free. */
 | 
			
		||||
  if (GPU_context_active_get()) {
 | 
			
		||||
    glDeleteTextures(1, &tex_id);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    orphans_add(shared_orphan_list_.textures, shared_orphan_list_.lists_mutex, tex_id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------------- */
 | 
			
		||||
/** \name Linked object deletion
 | 
			
		||||
 *
 | 
			
		||||
 * These objects contain data that are stored per context. We
 | 
			
		||||
 * need to do some cleanup if they are used accross context or if context
 | 
			
		||||
 * is discarded.
 | 
			
		||||
 * \{ */
 | 
			
		||||
 | 
			
		||||
void GLContext::batch_register(struct GPUBatch *batch)
 | 
			
		||||
{
 | 
			
		||||
  lists_mutex_.lock();
 | 
			
		||||
  batches_.add(batch);
 | 
			
		||||
  lists_mutex_.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::batch_unregister(struct GPUBatch *batch)
 | 
			
		||||
{
 | 
			
		||||
  /* vao_cache_clear() can acquire lists_mutex_ so avoid deadlock. */
 | 
			
		||||
  // reinterpret_cast<GLBatch *>(batch)->vao_cache_clear();
 | 
			
		||||
 | 
			
		||||
  lists_mutex_.lock();
 | 
			
		||||
  batches_.remove(batch);
 | 
			
		||||
  lists_mutex_.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::framebuffer_register(struct GPUFrameBuffer *fb)
 | 
			
		||||
{
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
  lists_mutex_.lock();
 | 
			
		||||
  framebuffers_.add(fb);
 | 
			
		||||
  lists_mutex_.unlock();
 | 
			
		||||
#else
 | 
			
		||||
  UNUSED_VARS(fb);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb)
 | 
			
		||||
{
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
  lists_mutex_.lock();
 | 
			
		||||
  framebuffers_.remove(fb);
 | 
			
		||||
  lists_mutex_.unlock();
 | 
			
		||||
#else
 | 
			
		||||
  UNUSED_VARS(fb);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,9 @@
 | 
			
		||||
 | 
			
		||||
#include "gpu_context_private.h"
 | 
			
		||||
 | 
			
		||||
#include "BLI_vector.hh"
 | 
			
		||||
#include "BLI_set.hh"
 | 
			
		||||
 | 
			
		||||
#include "glew-mx.h"
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -32,19 +35,63 @@
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
// TODO(fclem) this requires too much refactor for now.
 | 
			
		||||
// namespace blender {
 | 
			
		||||
// namespace gpu {
 | 
			
		||||
namespace blender {
 | 
			
		||||
namespace gpu {
 | 
			
		||||
 | 
			
		||||
class GLContext : public GPUContext {
 | 
			
		||||
class GLSharedOrphanLists {
 | 
			
		||||
 public:
 | 
			
		||||
  GLContext(void *ghost_window);
 | 
			
		||||
  ~GLContext();
 | 
			
		||||
  /** Mutex for the bellow structures. */
 | 
			
		||||
  std::mutex lists_mutex;
 | 
			
		||||
  /** Buffers and textures are shared across context. Any context can free them. */
 | 
			
		||||
  Vector<GLuint> textures;
 | 
			
		||||
  Vector<GLuint> buffers;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  /** Default framebuffer object for some GL implementation. */
 | 
			
		||||
  GLuint default_framebuffer_;
 | 
			
		||||
 public:
 | 
			
		||||
  void orphans_clear(void);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// }  // namespace gpu
 | 
			
		||||
// }  // namespace blender
 | 
			
		||||
class GLContext : public GPUContext {
 | 
			
		||||
  /* TODO(fclem) these needs to become private. */
 | 
			
		||||
 public:
 | 
			
		||||
  /** Default VAO for procedural draw calls. */
 | 
			
		||||
  GLuint default_vao_;
 | 
			
		||||
  /** Default framebuffer object for some GL implementation. */
 | 
			
		||||
  GLuint default_framebuffer_;
 | 
			
		||||
  /** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */
 | 
			
		||||
  GLuint default_attr_vbo_;
 | 
			
		||||
  /**
 | 
			
		||||
   * GPUBatch & GPUFramebuffer have references to the context they are from, in the case the
 | 
			
		||||
   * context is destroyed, we need to remove any reference to it.
 | 
			
		||||
   */
 | 
			
		||||
  Set<GPUBatch *> batches_;
 | 
			
		||||
  Set<GPUFrameBuffer *> framebuffers_;
 | 
			
		||||
  /** Mutex for the bellow structures. */
 | 
			
		||||
  std::mutex lists_mutex_;
 | 
			
		||||
  /** VertexArrays and framebuffers are not shared across context. */
 | 
			
		||||
  Vector<GLuint> orphaned_vertarrays_;
 | 
			
		||||
  Vector<GLuint> orphaned_framebuffers_;
 | 
			
		||||
  /** GLBackend onws this data. */
 | 
			
		||||
  GLSharedOrphanLists &shared_orphan_list_;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list);
 | 
			
		||||
  ~GLContext();
 | 
			
		||||
 | 
			
		||||
  void activate(void) override;
 | 
			
		||||
  void deactivate(void) override;
 | 
			
		||||
 | 
			
		||||
  /* TODO(fclem) these needs to become private. */
 | 
			
		||||
 public:
 | 
			
		||||
  void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id);
 | 
			
		||||
  void orphans_clear(void);
 | 
			
		||||
 | 
			
		||||
  void vao_free(GLuint vao_id);
 | 
			
		||||
  void fbo_free(GLuint fbo_id);
 | 
			
		||||
  void batch_register(struct GPUBatch *batch);
 | 
			
		||||
  void batch_unregister(struct GPUBatch *batch);
 | 
			
		||||
  void framebuffer_register(struct GPUFrameBuffer *fb);
 | 
			
		||||
  void framebuffer_unregister(struct GPUFrameBuffer *fb);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gpu
 | 
			
		||||
}  // namespace blender
 | 
			
		||||
 
 | 
			
		||||
@@ -121,6 +121,7 @@
 | 
			
		||||
#include "UI_interface.h"
 | 
			
		||||
#include "UI_resources.h"
 | 
			
		||||
 | 
			
		||||
#include "GPU_context.h"
 | 
			
		||||
#include "GPU_init_exit.h"
 | 
			
		||||
#include "GPU_material.h"
 | 
			
		||||
 | 
			
		||||
@@ -634,6 +635,8 @@ void WM_exit_ex(bContext *C, const bool do_python)
 | 
			
		||||
 | 
			
		||||
  RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */
 | 
			
		||||
 | 
			
		||||
  GPU_backend_exit();
 | 
			
		||||
 | 
			
		||||
  wm_ghost_exit();
 | 
			
		||||
 | 
			
		||||
  CTX_free(C);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user