Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDalai Felinto <dfelinto@gmail.com>2017-07-14 18:40:54 +0300
committerDalai Felinto <dfelinto@gmail.com>2017-07-14 18:46:10 +0300
commit2a489273d7e2354a9f1afc3212863ff4b463cf86 (patch)
treea12701d9ca43b6ace0993cfc31bc278eed3cc4fb /source/blender/gpu
parent73b142529705e75790a4b9279109763014ca63e6 (diff)
Implement Uniformbuffer objects for nodetree parameters
For users that means you can tweak shaders in the nodetree and things are way faster. This is a huge improvement, particularly in systems that have no shader cache. From the code perspective it means we are no longer re-compiling the shader every time a value is tweaked in the UI. We are using uniforms for those values. It would be slow to add that many uniforms for all the shaders. So instead we are using UBO (Uniform Buffer Objects). This fixes the main issue of T51467. However GWN_shaderinterface_create() still needs to be improvedi. When opening a .blend all shaders are compiled once, so optimizing it will bring a measurable impact. ======================================================================== NOTE: This breaks update of Cycles material upon nodetree nodes tweaking. It will be fixed separately by depsgraph, once tackling T51925 (Animated Eevee values slowdown). The idea is to make Depsgraph update more granular. The XXX TODO in rna_nodetree.c will be tackled at that time as well. ======================================================================== Reviewers: sergey, brecht, fclem Differential Revision: https://developer.blender.org/D2739
Diffstat (limited to 'source/blender/gpu')
-rw-r--r--source/blender/gpu/GPU_material.h14
-rw-r--r--source/blender/gpu/GPU_uniformbuffer.h13
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c150
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h1
-rw-r--r--source/blender/gpu/intern/gpu_material.c32
-rw-r--r--source/blender/gpu/intern/gpu_uniformbuffer.c214
6 files changed, 397 insertions, 27 deletions
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index ec50bd9d9f5..47035c8c64e 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -43,6 +43,7 @@ extern "C" {
struct Image;
struct ImageUser;
+struct ListBase;
struct Material;
struct Object;
struct Scene;
@@ -53,9 +54,11 @@ struct GPUNodeLink;
struct GPUNodeStack;
struct GPUMaterial;
struct GPUTexture;
+struct GPUUniformBuffer;
struct GPULamp;
struct PreviewImage;
struct World;
+struct bNode;
struct bNodeTree;
typedef struct GPUNode GPUNode;
@@ -143,6 +146,7 @@ typedef struct GPUNodeStack {
#define GPU_DYNAMIC_GROUP_MIST 0x00050000
#define GPU_DYNAMIC_GROUP_WORLD 0x00060000
#define GPU_DYNAMIC_GROUP_MAT 0x00070000
+#define GPU_DYNAMIC_UBO 0x00080000
typedef enum GPUDynamicType {
@@ -202,6 +206,7 @@ typedef enum GPUDynamicType {
GPUNodeLink *GPU_attribute(CustomDataType type, const char *name);
GPUNodeLink *GPU_uniform(float *num);
GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data);
+GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype);
GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data);
GPUNodeLink *GPU_cube_map(struct Image *ima, struct ImageUser *iuser, bool is_data);
GPUNodeLink *GPU_image_preview(struct PreviewImage *prv);
@@ -212,7 +217,10 @@ GPUNodeLink *GPU_opengl_builtin(GPUOpenGLBuiltin builtin);
void GPU_node_link_set_type(GPUNodeLink *link, GPUType type);
bool GPU_link(GPUMaterial *mat, const char *name, ...);
-bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...);
+bool GPU_stack_link(GPUMaterial *mat, struct bNode *node, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...);
+GPUNodeLink *GPU_uniformbuffer_link_out(
+ struct GPUMaterial *mat, struct bNode *node,
+ struct GPUNodeStack *stack, const int index);
void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
void GPU_material_enable_alpha(GPUMaterial *material);
@@ -244,6 +252,10 @@ struct Scene *GPU_material_scene(GPUMaterial *material);
GPUMatType GPU_Material_get_type(GPUMaterial *material);
struct GPUPass *GPU_material_get_pass(GPUMaterial *material);
+struct GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material);
+void GPU_material_create_uniform_buffer(GPUMaterial *material, struct ListBase *inputs);
+void GPU_material_uniform_buffer_tag_dirty(struct ListBase *gpumaterials);
+
void GPU_material_vertex_attributes(GPUMaterial *material,
struct GPUVertexAttribs *attrib);
diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h
index 36b659e30b1..e342078d8fd 100644
--- a/source/blender/gpu/GPU_uniformbuffer.h
+++ b/source/blender/gpu/GPU_uniformbuffer.h
@@ -32,12 +32,19 @@
#ifndef __GPU_UNIFORMBUFFER_H__
#define __GPU_UNIFORMBUFFER_H__
+typedef enum GPUType GPUType;
+struct ListBase;
+
typedef struct GPUUniformBuffer GPUUniformBuffer;
+typedef struct GPUUniformBufferDynamicItem GPUUniformBufferDynamicItem;
GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]);
+GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(struct ListBase *inputs, char err_out[256]);
+
void GPU_uniformbuffer_free(GPUUniformBuffer *ubo);
void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data);
+void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_);
void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number);
#if 0
@@ -46,4 +53,10 @@ void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo);
int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo);
+bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo);
+bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo);
+void GPU_uniformbuffer_tag_dirty(GPUUniformBuffer *ubo);
+
+#define GPU_UBO_BLOCK_NAME "nodeTree"
+
#endif /* __GPU_UNIFORMBUFFER_H__ */
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index cd367c821ba..9fa78bfe5b9 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -36,6 +36,7 @@
#include "DNA_customdata_types.h"
#include "DNA_image_types.h"
#include "DNA_material_types.h"
+#include "DNA_node_types.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
@@ -47,6 +48,7 @@
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
#include "BLI_sys_types.h" /* for intptr_t support */
@@ -508,12 +510,16 @@ static void codegen_set_unique_ids(ListBase *nodes)
BLI_ghash_free(definehash, NULL, NULL);
}
-static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
+/**
+ * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO.
+ */
+static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, ListBase *nodes)
{
GPUNode *node;
GPUInput *input;
const char *name;
int builtins = 0;
+ ListBase ubo_inputs = {NULL, NULL};
/* print uniforms */
for (node = nodes->first; node; node = node->next) {
@@ -545,7 +551,13 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
}
}
else if (input->source == GPU_SOURCE_VEC_UNIFORM) {
- if (input->dynamicvec) {
+ if (input->dynamictype == GPU_DYNAMIC_UBO) {
+ if (!input->link) {
+ /* We handle the UBOuniforms separately. */
+ BLI_addtail(&ubo_inputs, BLI_genericNodeN(input));
+ }
+ }
+ else if (input->dynamicvec) {
/* only create uniforms for dynamic vectors */
BLI_dynstr_appendf(ds, "uniform %s unf%d;\n",
GPU_DATATYPE_STR[input->type], input->id);
@@ -584,6 +596,22 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
}
}
+ /* Handle the UBO block separately. */
+ if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) {
+ GPU_material_create_uniform_buffer(material, &ubo_inputs);
+
+ /* Inputs are sorted */
+ BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME);
+
+ for (LinkData *link = ubo_inputs.first; link; link = link->next) {
+ input = link->data;
+ BLI_dynstr_appendf(ds, "\t%s unf%d;\n",
+ GPU_DATATYPE_STR[input->type], input->id);
+ }
+ BLI_dynstr_append(ds, "};\n");
+ BLI_freelistN(&ubo_inputs);
+ }
+
BLI_dynstr_append(ds, "\n");
return builtins;
@@ -686,7 +714,7 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final
BLI_dynstr_append(ds, ";\n");
}
-static char *code_generate_fragment(ListBase *nodes, GPUOutput *output, bool use_new_shading)
+static char *code_generate_fragment(GPUMaterial *material, ListBase *nodes, GPUOutput *output, bool use_new_shading)
{
DynStr *ds = BLI_dynstr_new();
char *code;
@@ -703,7 +731,7 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output, bool use
#endif
codegen_set_unique_ids(nodes);
- builtins = codegen_print_uniforms_functions(ds, nodes);
+ builtins = codegen_process_uniforms_functions(material, ds, nodes);
#if 0
if (G.debug & G_DEBUG)
@@ -1397,7 +1425,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType
name = outnode->name;
input = outnode->inputs.first;
- if ((STREQ(name, "set_value") || STREQ(name, "set_rgb")) &&
+ if ((STREQ(name, "set_value") || STREQ(name, "set_rgb") || STREQ(name, "set_rgba")) &&
(input->type == type))
{
input = MEM_dupallocN(outnode->inputs.first);
@@ -1514,15 +1542,84 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType
BLI_addtail(&node->inputs, input);
}
-static void gpu_node_input_socket(GPUNode *node, GPUNodeStack *sock)
+
+static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type)
{
- GPUNodeLink *link;
+ switch (type) {
+ case SOCK_FLOAT:
+ return "set_value";
+ case SOCK_VECTOR:
+ return "set_rgb";
+ case SOCK_RGBA:
+ return "set_rgba";
+ default:
+ BLI_assert(!"No gpu function for non-supported eNodeSocketDatatype");
+ return NULL;
+ }
+}
+
+/**
+ * Link stack uniform buffer.
+ * This is called for the input/output sockets that are note connected.
+ */
+static GPUNodeLink *gpu_uniformbuffer_link(
+ GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index, const eNodeSocketInOut in_out)
+{
+ bNodeSocket *socket;
+ if (in_out == SOCK_IN) {
+ socket = BLI_findlink(&node->original->inputs, index);
+ }
+ else {
+ socket = BLI_findlink(&node->original->outputs, index);
+ }
+
+ BLI_assert(socket != NULL);
+ BLI_assert(socket->in_out == in_out);
+
+ if ((socket->flag & SOCK_HIDE_VALUE) == 0) {
+ GPUNodeLink *link;
+ switch (socket->type) {
+ case SOCK_FLOAT:
+ {
+ bNodeSocketValueFloat *socket_data = socket->default_value;
+ link = GPU_uniform_buffer(&socket_data->value, GPU_FLOAT);
+ break;
+ }
+ case SOCK_VECTOR:
+ {
+ bNodeSocketValueRGBA *socket_data = socket->default_value;
+ link = GPU_uniform_buffer(socket_data->value, GPU_VEC3);
+ break;
+ }
+ case SOCK_RGBA:
+ {
+ bNodeSocketValueRGBA *socket_data = socket->default_value;
+ link = GPU_uniform_buffer(socket_data->value, GPU_VEC4);
+ break;
+ }
+ default:
+ return NULL;
+ break;
+ }
+
+ if (in_out == SOCK_IN) {
+ GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link);
+ }
+ return link;
+ }
+ return NULL;
+}
+static void gpu_node_input_socket(GPUMaterial *material, bNode *bnode, GPUNode *node, GPUNodeStack *sock, const int index)
+{
if (sock->link) {
gpu_node_input_link(node, sock->link, sock->type);
}
+ else if ((material != NULL) && (gpu_uniformbuffer_link(material, bnode, sock, index, SOCK_IN) != NULL)) {
+ gpu_node_input_link(node, sock->link, sock->type);
+ }
else {
- link = GPU_node_link_create();
+ GPUNodeLink *link = GPU_node_link_create();
link->ptr1 = sock->vec;
gpu_node_input_link(node, link, sock->type);
}
@@ -1685,6 +1782,21 @@ GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *d
return link;
}
+/**
+ * Add uniform to UBO struct of GPUMaterial.
+ */
+GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype)
+{
+ GPUNodeLink *link = GPU_node_link_create();
+ link->ptr1 = num;
+ link->ptr2 = NULL;
+ link->dynamic = true;
+ link->dynamictype = GPU_DYNAMIC_UBO;
+ link->type = gputype;
+
+ return link;
+}
+
GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data)
{
GPUNodeLink *link = GPU_node_link_create();
@@ -1795,7 +1907,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
return true;
}
-bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...)
+bool GPU_stack_link(GPUMaterial *material, bNode *bnode, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...)
{
GPUNode *node;
GPUFunction *function;
@@ -1815,11 +1927,11 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
if (in) {
for (i = 0; in[i].type != GPU_NONE; i++) {
- gpu_node_input_socket(node, &in[i]);
+ gpu_node_input_socket(material, bnode, node, &in[i], i);
totin++;
}
}
-
+
if (out) {
for (i = 0; out[i].type != GPU_NONE; i++) {
gpu_node_output(node, out[i].type, &out[i].link);
@@ -1841,7 +1953,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
if (totin == 0) {
link = va_arg(params, GPUNodeLink *);
if (link->socket)
- gpu_node_input_socket(node, link->socket);
+ gpu_node_input_socket(NULL, NULL, node, link->socket, -1);
else
gpu_node_input_link(node, link, function->paramtype[i]);
}
@@ -1851,8 +1963,8 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
}
va_end(params);
- gpu_material_add_node(mat, node);
-
+ gpu_material_add_node(material, node);
+
return true;
}
@@ -1877,6 +1989,11 @@ int GPU_link_changed(GPUNodeLink *link)
return 0;
}
+GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index)
+{
+ return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT);
+}
+
/* Pass create/free */
static void gpu_nodes_tag(GPUNodeLink *link)
@@ -1917,6 +2034,7 @@ static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink)
}
GPUPass *GPU_generate_pass_new(
+ struct GPUMaterial *material,
ListBase *nodes, struct GPUNodeLink *frag_outlink,
GPUVertexAttribs *attribs,
const char *vert_code, const char *geom_code,
@@ -1933,7 +2051,7 @@ GPUPass *GPU_generate_pass_new(
gpu_nodes_get_vertex_attributes(nodes, attribs);
/* generate code and compile with opengl */
- fragmentgen = code_generate_fragment(nodes, frag_outlink->output, true);
+ fragmentgen = code_generate_fragment(material, nodes, frag_outlink->output, true);
vertexgen = code_generate_vertex_new(nodes, vert_code, (geom_code != NULL));
tmp = BLI_strdupcat(frag_lib, glsl_material_library);
@@ -2012,7 +2130,7 @@ GPUPass *GPU_generate_pass(
gpu_nodes_get_builtin_flag(nodes, builtins);
/* generate code and compile with opengl */
- fragmentcode = code_generate_fragment(nodes, outlink->output, false);
+ fragmentcode = code_generate_fragment(NULL, nodes, outlink->output, false);
vertexcode = code_generate_vertex(nodes, type);
geometrycode = code_generate_geometry(nodes, use_opensubdiv);
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index 159b1f4ef8e..28eb55968bb 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -168,6 +168,7 @@ struct GPUPass {
typedef struct GPUPass GPUPass;
GPUPass *GPU_generate_pass_new(
+ struct GPUMaterial *material,
ListBase *nodes, struct GPUNodeLink *frag_outlink,
struct GPUVertexAttribs *attribs,
const char *vert_code, const char *geom_code,
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index efe79c0386c..f62c599a9b6 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -64,6 +64,7 @@
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
#include "gpu_codegen.h"
#include "gpu_lamp_private.h"
@@ -133,6 +134,7 @@ struct GPUMaterial {
bool bound;
bool is_opensubdiv;
+ GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
};
/* Forward declaration so shade_light_textures() can use this, while still keeping the code somewhat organized */
@@ -250,6 +252,10 @@ void GPU_material_free(ListBase *gpumaterial)
if (material->pass)
GPU_pass_free(material->pass);
+ if (material->ubo != NULL) {
+ GPU_uniformbuffer_free(material->ubo);
+ }
+
BLI_freelistN(&material->lamps);
MEM_freeN(material);
@@ -429,6 +435,30 @@ GPUPass *GPU_material_get_pass(GPUMaterial *material)
return material->pass;
}
+GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material)
+{
+ return material->ubo;
+}
+
+/**
+ * Create dynamic UBO from parameters
+ * \param ListBase of BLI_genericNodeN(GPUInput)
+ */
+void GPU_material_create_uniform_buffer(GPUMaterial *material, ListBase *inputs)
+{
+ material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL);
+}
+
+void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials)
+{
+ for (LinkData *link = gpumaterials->first; link; link = link->next) {
+ GPUMaterial *material = link->data;
+ if (material->ubo != NULL) {
+ GPU_uniformbuffer_tag_dirty(material->ubo);
+ }
+ }
+}
+
void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs)
{
*attribs = material->attribs;
@@ -2130,7 +2160,7 @@ GPUMaterial *GPU_material_from_nodetree(
if (mat->outlink) {
outlink = mat->outlink;
mat->pass = GPU_generate_pass_new(
- &mat->nodes, outlink, &mat->attribs, vert_code, geom_code, frag_lib, defines);
+ mat, &mat->nodes, outlink, &mat->attribs, vert_code, geom_code, frag_lib, defines);
}
/* note that even if building the shader fails in some way, we still keep
diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.c
index 76aa1a8226f..e3072d729c3 100644
--- a/source/blender/gpu/intern/gpu_uniformbuffer.c
+++ b/source/blender/gpu/intern/gpu_uniformbuffer.c
@@ -29,23 +29,68 @@
* \ingroup gpu
*/
+#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "gpu_codegen.h"
+
#include "GPU_extensions.h"
#include "GPU_glew.h"
+#include "GPU_material.h"
#include "GPU_uniformbuffer.h"
-struct GPUUniformBuffer {
+typedef enum GPUUniformBufferFlag {
+ GPU_UBO_FLAG_INITIALIZED = (1 << 0),
+ GPU_UBO_FLAG_DIRTY = (1 << 1),
+} GPUUniformBufferFlag;
+
+typedef enum GPUUniformBufferType {
+ GPU_UBO_STATIC = 0,
+ GPU_UBO_DYNAMIC = 1,
+} GPUUniformBufferType;
+
+typedef struct GPUUniformBuffer {
int size; /* in bytes */
GLuint bindcode; /* opengl identifier for UBO */
int bindpoint; /* current binding point */
-};
+ GPUUniformBufferType type;
+} GPUUniformBuffer;
+
+#define GPUUniformBufferStatic GPUUniformBuffer
+
+typedef struct GPUUniformBufferDynamic {
+ GPUUniformBuffer buffer;
+ ListBase items; /* GPUUniformBufferDynamicItem */
+ void *data;
+ char flag;
+} GPUUniformBufferDynamic;
+
+typedef struct GPUUniformBufferDynamicItem {
+ struct GPUUniformBufferDynamicItem *next, *prev;
+ GPUType gputype;
+ float *data;
+ int size;
+} GPUUniformBufferDynamicItem;
+
+
+/* Prototypes */
+static void gpu_uniformbuffer_inputs_sort(struct ListBase *inputs);
+
+static GPUUniformBufferDynamicItem *gpu_uniformbuffer_populate(
+ GPUUniformBufferDynamic *ubo, const GPUType gputype, float *num);
+
+static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data)
+{
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
+ glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256])
{
- GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBuffer), "GPUUniformBuffer");
+ GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic");
ubo->size = size;
/* Generate Buffer object */
@@ -65,26 +110,164 @@ GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_
return NULL;
}
- glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
- glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
+ gpu_uniformbuffer_initialize(ubo, data);
return ubo;
}
+/**
+ * Create dynamic UBO from parameters
+ * Return NULL if failed to create or if \param inputs is empty.
+ *
+ * \param inputs ListBase of BLI_genericNodeN(GPUInput)
+ */
+GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256])
+{
+ /* There is no point on creating an UBO if there is no arguments. */
+ if (BLI_listbase_is_empty(inputs)) {
+ return NULL;
+ }
+
+ GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic), "GPUUniformBufferDynamic");
+ ubo->buffer.type = GPU_UBO_DYNAMIC;
+ ubo->flag = GPU_UBO_FLAG_DIRTY;
+
+ /* Generate Buffer object. */
+ glGenBuffers(1, &ubo->buffer.bindcode);
+
+ if (!ubo->buffer.bindcode) {
+ if (err_out)
+ BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO create failed");
+ GPU_uniformbuffer_free(&ubo->buffer);
+ return NULL;
+ }
+
+ if (ubo->buffer.size > GPU_max_ubo_size()) {
+ if (err_out)
+ BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO too big");
+ GPU_uniformbuffer_free(&ubo->buffer);
+ return NULL;
+ }
+
+ /* Make sure we comply to the ubo alignment requirements. */
+ gpu_uniformbuffer_inputs_sort(inputs);
+
+ for (LinkData *link = inputs->first; link; link = link->next) {
+ GPUInput *input = link->data;
+ gpu_uniformbuffer_populate(ubo, input->type, input->dynamicvec);
+ }
+
+ ubo->data = MEM_mallocN(ubo->buffer.size, __func__);
+
+ /* Initialize buffer data. */
+ GPU_uniformbuffer_dynamic_update(&ubo->buffer);
+ return &ubo->buffer;
+}
+
+/**
+ * Free the data, and clean the items list.
+ */
+static void gpu_uniformbuffer_dynamic_reset(GPUUniformBufferDynamic *ubo)
+{
+ ubo->buffer.size = 0;
+ if (ubo->data) {
+ MEM_freeN(ubo->data);
+ }
+ BLI_freelistN(&ubo->items);
+}
+
void GPU_uniformbuffer_free(GPUUniformBuffer *ubo)
{
+ if (ubo->type == GPU_UBO_DYNAMIC) {
+ gpu_uniformbuffer_dynamic_reset((GPUUniformBufferDynamic *)ubo);
+ }
+
glDeleteBuffers(1, &ubo->bindcode);
MEM_freeN(ubo);
}
-void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
+static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
{
glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
+void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
+{
+ BLI_assert(ubo->type == GPU_UBO_STATIC);
+ gpu_uniformbuffer_update(ubo, data);
+}
+
+/**
+ * We need to recalculate the internal data, and re-generate it
+ * from its populated items.
+ */
+void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_)
+{
+ BLI_assert(ubo_->type == GPU_UBO_DYNAMIC);
+ GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_;
+
+ float *offset = ubo->data;
+ for (GPUUniformBufferDynamicItem *item = ubo->items.first; item; item = item->next) {
+ memcpy(offset, item->data, item->size);
+ offset += item->gputype;
+ }
+
+ if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) {
+ gpu_uniformbuffer_update(ubo_, ubo->data);
+ }
+ else {
+ ubo->flag |= GPU_UBO_FLAG_INITIALIZED;
+ gpu_uniformbuffer_initialize(ubo_, ubo->data);
+ }
+
+ ubo->flag &= ~GPU_UBO_FLAG_DIRTY;
+}
+
+/**
+ * Returns 1 if the first item shold be after second item.
+ * We make sure the vec4 uniforms come first.
+ */
+static int inputs_cmp(const void *a, const void *b)
+{
+ const LinkData *link_a = a, *link_b = b;
+ const GPUInput *input_a = link_a->data, *input_b = link_b->data;
+ return input_a->type < input_b->type ? 1 : 0;
+}
+
+/**
+ * Make sure we respect the expected alignment of UBOs.
+ * vec4, pad vec3 as vec4, then vec2, then floats.
+ */
+static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
+{
+ BLI_listbase_sort(inputs, inputs_cmp);
+}
+
+/**
+ * This may now happen from the main thread, so we can't update the UBO
+ * We simply flag it as dirty
+ */
+static GPUUniformBufferDynamicItem *gpu_uniformbuffer_populate(
+ GPUUniformBufferDynamic *ubo, const GPUType gputype, float *num)
+{
+ BLI_assert(gputype <= GPU_VEC4);
+ GPUUniformBufferDynamicItem *item = MEM_callocN(sizeof(GPUUniformBufferDynamicItem), __func__);
+
+ /* Treat VEC3 as VEC4 because of UBO struct alignment requirements. */
+ GPUType type = gputype == GPU_VEC3 ? GPU_VEC4 : gputype;
+
+ item->gputype = type;
+ item->data = num;
+ item->size = type * sizeof(float);
+ ubo->buffer.size += item->size;
+
+ ubo->flag |= GPU_UBO_FLAG_DIRTY;
+ BLI_addtail(&ubo->items, item);
+
+ return item;
+}
+
void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
{
if (number >= GPU_max_ubo_binds()) {
@@ -92,6 +275,13 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
return;
}
+ if (ubo->type == GPU_UBO_DYNAMIC) {
+ GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo;
+ if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) {
+ GPU_uniformbuffer_dynamic_update(ubo);
+ }
+ }
+
if (ubo->bindcode != 0) {
glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode);
}
@@ -102,4 +292,10 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo)
{
return ubo->bindpoint;
-} \ No newline at end of file
+}
+
+void GPU_uniformbuffer_tag_dirty(GPUUniformBuffer *ubo_) {
+ BLI_assert(ubo_->type == GPU_UBO_DYNAMIC);
+ GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_;
+ ubo->flag |= GPU_UBO_FLAG_DIRTY;
+}