GPUUniformBuf: GL backend isolation

This is in preparation of vulkan backend. We move all opengl
functionnalities behind an abstract class.

This also cleansup the "dynamic" ubo create and rename it to
`GPU_uniformbuf_from_list()`

Contains, no functional change.

Part of T68990 Vulkan support.
This commit is contained in:
Clément Foucault 2020-08-21 12:30:55 +02:00
parent 7edd8a7738
commit c4f122ac8f
12 changed files with 361 additions and 120 deletions

View File

@ -95,6 +95,7 @@ set(SRC
opengl/gl_shader.cc
opengl/gl_shader_interface.cc
opengl/gl_state.cc
opengl/gl_uniform_buffer.cc
opengl/gl_vertex_array.cc
GPU_attr_binding.h
@ -142,6 +143,7 @@ set(SRC
intern/gpu_shader_private.hh
intern/gpu_shader_interface.hh
intern/gpu_state_private.hh
intern/gpu_uniform_buffer_private.hh
intern/gpu_vertex_format_private.h
opengl/gl_backend.hh
@ -151,6 +153,7 @@ set(SRC
opengl/gl_shader.hh
opengl/gl_shader_interface.hh
opengl/gl_state.hh
opengl/gl_uniform_buffer.hh
opengl/gl_vertex_array.hh
)

View File

@ -29,10 +29,13 @@ extern "C" {
struct ListBase;
typedef struct GPUUniformBuf GPUUniformBuf;
/** Opaque pointer */
typedef struct GPUUniformBuf {
void *dummy;
} GPUUniformBuf;
GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name);
GPUUniformBuf *GPU_uniformbuf_dynamic_create(struct ListBase *inputs, const char *name);
GPUUniformBuf *GPU_uniformbuf_create_from_list(struct ListBase *inputs, const char *name);
#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, NULL, __func__);

View File

@ -25,14 +25,16 @@
#pragma once
#include "gpu_batch_private.hh"
#include "gpu_context_private.hh"
#include "gpu_drawlist_private.hh"
#include "gpu_shader_private.hh"
struct GPUContext;
namespace blender {
namespace gpu {
class Batch;
class DrawList;
class Shader;
class UniformBuf;
class GPUBackend {
public:
virtual ~GPUBackend(){};
@ -46,6 +48,7 @@ class GPUBackend {
// virtual FrameBuffer *framebuffer_alloc(void) = 0;
virtual Shader *shader_alloc(const char *name) = 0;
// virtual Texture *texture_alloc(void) = 0;
virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0;
};
} // namespace gpu

View File

@ -237,7 +237,7 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
#else
const char *name = NULL;
#endif
material->ubo = GPU_uniformbuf_dynamic_create(inputs, name);
material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
}
/* Eevee Subsurface scattering. */

View File

@ -13,7 +13,7 @@
* 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) 2005 Blender Foundation.
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
@ -27,64 +27,46 @@
#include "BLI_blenlib.h"
#include "BLI_math_base.h"
#include "gpu_context_private.hh"
#include "gpu_backend.hh"
#include "gpu_node_graph.h"
#include "GPU_extensions.h"
#include "GPU_glew.h"
#include "GPU_material.h"
#include "GPU_extensions.h"
#include "GPU_uniform_buffer.h"
#include "gpu_uniform_buffer_private.hh"
typedef struct GPUUniformBuf {
/** Data size in bytes. */
int size;
/** GL handle for UBO. */
GLuint bindcode;
/** Current binding point. */
int bindpoint;
/** Continuous memory block to copy to GPU. Is own by the GPUUniformBuf. */
void *data;
/* -------------------------------------------------------------------- */
/** \name Creation & Deletion
* \{ */
#ifndef NDEBUG
char name[64];
#endif
} GPUUniformBuf;
namespace blender::gpu {
GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
UniformBuf::UniformBuf(size_t size, const char *name)
{
/* Make sure that UBO is padded to size of vec4 */
BLI_assert((size % 16) == 0);
BLI_assert(size <= GPU_max_ubo_size());
if (size > GPU_max_ubo_size()) {
fprintf(stderr, "GPUUniformBuf: UBO too big. %s", name);
return NULL;
}
size_in_bytes_ = size;
GPUUniformBuf *ubo = (GPUUniformBuf *)MEM_mallocN(sizeof(GPUUniformBuf), __func__);
ubo->size = size;
ubo->data = NULL;
ubo->bindcode = 0;
ubo->bindpoint = -1;
#ifndef NDEBUG
BLI_strncpy(ubo->name, name, sizeof(ubo->name));
#endif
/* Direct init. */
if (data != NULL) {
GPU_uniformbuf_update(ubo, data);
}
return ubo;
BLI_strncpy(name_, name, sizeof(name_));
}
void GPU_uniformbuf_free(GPUUniformBuf *ubo)
UniformBuf::~UniformBuf()
{
MEM_SAFE_FREE(ubo->data);
GPU_buf_free(ubo->bindcode);
MEM_freeN(ubo);
MEM_SAFE_FREE(data_);
}
} // namespace blender::gpu
/** \} */
/* -------------------------------------------------------------------- */
/** \name Uniform buffer from GPUInput list
* \{ */
/**
* We need to pad some data types (vec3) on the C side
* To match the GPU expected memory block alignment.
@ -117,10 +99,10 @@ static int inputs_cmp(const void *a, const void *b)
* Make sure we respect the expected alignment of UBOs.
* mat4, vec4, pad vec3 as vec4, then vec2, then floats.
*/
static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
static void buffer_from_list_inputs_sort(ListBase *inputs)
{
/* Only support up to this type, if you want to extend it, make sure the
* padding logic is correct for the new types. */
/* Only support up to this type, if you want to extend it, make sure tstatic void
* inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */
#define MAX_UBO_GPU_TYPE GPU_MAT4
/* Order them as mat4, vec4, vec3, vec2, float. */
@ -179,23 +161,9 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
#undef MAX_UBO_GPU_TYPE
}
/**
* Create dynamic UBO from parameters
* Return NULL if failed to create or if \param inputs: is empty.
*
* \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
*/
GPUUniformBuf *GPU_uniformbuf_dynamic_create(ListBase *inputs, const char *name)
static inline size_t buffer_size_from_list(ListBase *inputs)
{
/* There is no point on creating an UBO if there is no arguments. */
if (BLI_listbase_is_empty(inputs)) {
return NULL;
}
/* Make sure we comply to the ubo alignment requirements. */
gpu_uniformbuffer_inputs_sort(inputs);
size_t buffer_size = 0;
LISTBASE_FOREACH (LinkData *, link, inputs) {
const eGPUType gputype = get_padded_gpu_type(link);
buffer_size += gputype * sizeof(float);
@ -203,8 +171,12 @@ GPUUniformBuf *GPU_uniformbuf_dynamic_create(ListBase *inputs, const char *name)
/* Round up to size of vec4. (Opengl Requirement) */
size_t alignment = sizeof(float[4]);
buffer_size = divide_ceil_u(buffer_size, alignment) * alignment;
void *data = MEM_mallocN(buffer_size, __func__);
return buffer_size;
}
static inline void buffer_fill_from_list(void *data, ListBase *inputs)
{
/* Now that we know the total ubo size we can start populating it. */
float *offset = (float *)data;
LISTBASE_FOREACH (LinkData *, link, inputs) {
@ -212,71 +184,73 @@ GPUUniformBuf *GPU_uniformbuf_dynamic_create(ListBase *inputs, const char *name)
memcpy(offset, input->vec, input->type * sizeof(float));
offset += get_padded_gpu_type(link);
}
/* Pass data as NULL for late init. */
GPUUniformBuf *ubo = GPU_uniformbuf_create_ex(buffer_size, NULL, name);
/* Data will be update just before binding. */
ubo->data = data;
return ubo;
}
static void gpu_uniformbuffer_init(GPUUniformBuf *ubo)
{
BLI_assert(ubo->bindcode == 0);
ubo->bindcode = GPU_buf_alloc();
/** \} */
if (ubo->bindcode == 0) {
fprintf(stderr, "GPUUniformBuf: UBO create failed");
BLI_assert(0);
return;
/* -------------------------------------------------------------------- */
/** \name C-API
* \{ */
using namespace blender::gpu;
GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
{
UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(size, name);
/* Direct init. */
if (data != NULL) {
ubo->update(data);
}
return reinterpret_cast<GPUUniformBuf *>(ubo);
}
/**
* Create UBO from inputs list.
* Return NULL if failed to create or if \param inputs: is empty.
*
* \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
*/
GPUUniformBuf *GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name)
{
/* There is no point on creating an UBO if there is no arguments. */
if (BLI_listbase_is_empty(inputs)) {
return NULL;
}
glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW);
buffer_from_list_inputs_sort(inputs);
size_t buffer_size = buffer_size_from_list(inputs);
void *data = MEM_mallocN(buffer_size, __func__);
buffer_fill_from_list(data, inputs);
UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name);
/* Defer data upload. */
ubo->attach_data(data);
return reinterpret_cast<GPUUniformBuf *>(ubo);
}
void GPU_uniformbuf_free(GPUUniformBuf *ubo)
{
delete reinterpret_cast<UniformBuf *>(ubo);
}
void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data)
{
if (ubo->bindcode == 0) {
gpu_uniformbuffer_init(ubo);
}
glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
reinterpret_cast<UniformBuf *>(ubo)->update(data);
}
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int number)
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
{
if (number >= GPU_max_ubo_binds()) {
fprintf(stderr, "GPUUniformBuf: UBO too big.");
return;
}
if (ubo->bindcode == 0) {
gpu_uniformbuffer_init(ubo);
}
if (ubo->data != NULL) {
GPU_uniformbuf_update(ubo, ubo->data);
MEM_SAFE_FREE(ubo->data);
}
glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode);
ubo->bindpoint = number;
reinterpret_cast<UniformBuf *>(ubo)->bind(slot);
}
void GPU_uniformbuf_unbind(GPUUniformBuf *ubo)
{
#ifndef NDEBUG
glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0);
#endif
ubo->bindpoint = 0;
reinterpret_cast<UniformBuf *>(ubo)->unbind();
}
void GPU_uniformbuf_unbind_all(void)
{
for (int i = 0; i < GPU_max_ubo_binds(); i++) {
glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
}
/* FIXME */
}
/** \} */

View File

@ -0,0 +1,65 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup gpu
*/
#pragma once
#include "BLI_sys_types.h"
namespace blender {
namespace gpu {
#ifdef DEBUG
# define DEBUG_NAME_LEN 8
#else
# define DEBUG_NAME_LEN 64
#endif
class UniformBuf {
protected:
/** Data size in bytes. */
size_t size_in_bytes_;
/** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */
void *data_ = NULL;
/** Debugging name */
char name_[DEBUG_NAME_LEN];
public:
UniformBuf(size_t size, const char *name);
virtual ~UniformBuf();
virtual void update(const void *data) = 0;
virtual void bind(int slot) = 0;
virtual void unbind(void) = 0;
/** Used to defer data upload at drawing time.
* This is useful if the thread has no context bound.
* This transfers ownership to this UniformBuf. */
void attach_data(void *data)
{
data_ = data;
}
};
#undef DEBUG_NAME_LEN
} // namespace gpu
} // namespace blender

View File

@ -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);

View File

@ -35,6 +35,7 @@
namespace blender {
namespace gpu {
class GLContext;
class GLShaderInterface;
#define GPU_VAO_STATIC_LEN 3
@ -45,7 +46,7 @@ class GLShaderInterface;
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. */
GLShaderInterface *interface_ = NULL;
/** Cached vao for the last interface. */

View File

@ -32,13 +32,13 @@
#include "glew-mx.h"
#include "gl_batch.hh"
#include <mutex>
namespace blender {
namespace gpu {
class GLVaoCache;
class GLSharedOrphanLists {
public:
/** Mutex for the bellow structures. */
@ -51,7 +51,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. */

View File

@ -49,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

View File

@ -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

View File

@ -0,0 +1,55 @@
/*
* 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 {
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