diff options
Diffstat (limited to 'intern/gawain/src/vertex_buffer.c')
-rw-r--r-- | intern/gawain/src/vertex_buffer.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/intern/gawain/src/vertex_buffer.c b/intern/gawain/src/vertex_buffer.c new file mode 100644 index 00000000000..769a0842a7e --- /dev/null +++ b/intern/gawain/src/vertex_buffer.c @@ -0,0 +1,183 @@ + +// Gawain vertex buffer +// +// This code is part of the Gawain library, with modifications +// specific to integration with Blender. +// +// Copyright 2016 Mike Erwin +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "vertex_buffer.h" +#include "buffer_id.h" +#include "vertex_format_private.h" +#include <stdlib.h> +#include <string.h> + +#define KEEP_SINGLE_COPY 1 + +static unsigned vbo_memory_usage; + +VertexBuffer* VertexBuffer_create(void) + { + VertexBuffer* verts = malloc(sizeof(VertexBuffer)); + VertexBuffer_init(verts); + return verts; + } + +VertexBuffer* VertexBuffer_create_with_format(const VertexFormat* format) + { + VertexBuffer* verts = VertexBuffer_create(); + VertexFormat_copy(&verts->format, format); + if (!format->packed) + VertexFormat_pack(&verts->format); + return verts; + + // this function might seem redundant, but there is potential for memory savings here... + // TODO: implement those memory savings + } + +void VertexBuffer_init(VertexBuffer* verts) + { + memset(verts, 0, sizeof(VertexBuffer)); + } + +void VertexBuffer_init_with_format(VertexBuffer* verts, const VertexFormat* format) + { + VertexBuffer_init(verts); + VertexFormat_copy(&verts->format, format); + if (!format->packed) + VertexFormat_pack(&verts->format); + } + +void VertexBuffer_discard(VertexBuffer* verts) + { + if (verts->vbo_id) { + buffer_id_free(verts->vbo_id); + vbo_memory_usage -= VertexBuffer_size(verts); + } +#if KEEP_SINGLE_COPY + else +#endif + if (verts->data) + free(verts->data); + + + free(verts); + } + +unsigned VertexBuffer_size(const VertexBuffer* verts) + { + return vertex_buffer_size(&verts->format, verts->vertex_ct); + } + +void VertexBuffer_allocate_data(VertexBuffer* verts, unsigned v_ct) + { + VertexFormat* format = &verts->format; + if (!format->packed) + VertexFormat_pack(format); + + verts->vertex_ct = v_ct; + + // Data initially lives in main memory. Will be transferred to VRAM when we "prime" it. + verts->data = malloc(VertexBuffer_size(verts)); + } + +void VertexBuffer_resize_data(VertexBuffer* verts, unsigned v_ct) + { +#if TRUST_NO_ONE + assert(verts->vertex_ct != v_ct); // allow this? + assert(verts->data != NULL); // has already been allocated + assert(verts->vbo_id == 0); // has not been sent to VRAM +#endif + + verts->vertex_ct = v_ct; + verts->data = realloc(verts->data, VertexBuffer_size(verts)); + // TODO: skip realloc if v_ct < existing vertex count + // extra space will be reclaimed, and never sent to VRAM (see VertexBuffer_prime) + } + +void VertexBuffer_set_attrib(VertexBuffer* verts, unsigned a_idx, unsigned v_idx, const void* data) + { + const VertexFormat* format = &verts->format; + const Attrib* a = format->attribs + a_idx; + +#if TRUST_NO_ONE + assert(a_idx < format->attrib_ct); + assert(v_idx < verts->vertex_ct); + assert(verts->data != NULL); // data must be in main mem +#endif + + memcpy((GLubyte*)verts->data + a->offset + v_idx * format->stride, data, a->sz); + } + +void VertexBuffer_fill_attrib(VertexBuffer* verts, unsigned a_idx, const void* data) + { + const VertexFormat* format = &verts->format; + const Attrib* a = format->attribs + a_idx; + +#if TRUST_NO_ONE + assert(a_idx < format->attrib_ct); +#endif + + const unsigned stride = a->sz; // tightly packed input data + + VertexBuffer_fill_attrib_stride(verts, a_idx, stride, data); + } + +void VertexBuffer_fill_attrib_stride(VertexBuffer* verts, unsigned a_idx, unsigned stride, const void* data) + { + const VertexFormat* format = &verts->format; + const Attrib* a = format->attribs + a_idx; + +#if TRUST_NO_ONE + assert(a_idx < format->attrib_ct); + assert(verts->data != NULL); // data must be in main mem +#endif + + const unsigned vertex_ct = verts->vertex_ct; + + if (format->attrib_ct == 1 && stride == format->stride) + { + // we can copy it all at once + memcpy(verts->data, data, vertex_ct * a->sz); + } + else + { + // we must copy it per vertex + for (unsigned v = 0; v < vertex_ct; ++v) + memcpy((GLubyte*)verts->data + a->offset + v * format->stride, (const GLubyte*)data + v * stride, a->sz); + } + } + +static void VertexBuffer_prime(VertexBuffer* verts) + { + const unsigned buffer_sz = VertexBuffer_size(verts); + + verts->vbo_id = buffer_id_alloc(); + glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id); + // fill with delicious data & send to GPU the first time only + glBufferData(GL_ARRAY_BUFFER, buffer_sz, verts->data, GL_STATIC_DRAW); + + vbo_memory_usage += buffer_sz; + +#if KEEP_SINGLE_COPY + // now that GL has a copy, discard original + free(verts->data); + verts->data = NULL; +#endif + } + +void VertexBuffer_use(VertexBuffer* verts) + { + if (verts->vbo_id) + glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id); + else + VertexBuffer_prime(verts); + } + +unsigned VertexBuffer_get_memory_usage(void) + { + return vbo_memory_usage; + } |