This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/intern/gawain/src/gwn_vertex_buffer.c

272 lines
6.7 KiB
C
Raw Normal View History

// 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 "gwn_vertex_buffer.h"
#include "gwn_buffer_id.h"
#include "gwn_vertex_format_private.h"
#include <stdlib.h>
#include <string.h>
#define KEEP_SINGLE_COPY 1
static unsigned vbo_memory_usage;
Gwn_VertBuf* GWN_vertbuf_create(void)
{
Gwn_VertBuf* verts = malloc(sizeof(Gwn_VertBuf));
GWN_vertbuf_init(verts);
return verts;
}
Gwn_VertBuf* GWN_vertbuf_create_with_format(const Gwn_VertFormat* format)
{
Gwn_VertBuf* verts = GWN_vertbuf_create();
GWN_vertformat_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
}
Gwn_VertBuf* GWN_vertbuf_create_dynamic_with_format(const Gwn_VertFormat* format)
{
Gwn_VertBuf* verts = GWN_vertbuf_create_with_format(format);
verts->data_dynamic = true;
return verts;
}
void GWN_vertbuf_init(Gwn_VertBuf* verts)
{
memset(verts, 0, sizeof(Gwn_VertBuf));
}
void GWN_vertbuf_init_with_format(Gwn_VertBuf* verts, const Gwn_VertFormat* format)
{
GWN_vertbuf_init(verts);
GWN_vertformat_copy(&verts->format, format);
if (!format->packed)
VertexFormat_pack(&verts->format);
}
/**
* Like #GWN_vertbuf_discard but doesn't free.
*/
void GWN_vertbuf_clear(Gwn_VertBuf* verts)
{
if (verts->vbo_id) {
GWN_buf_id_free(verts->vbo_id);
#if VRAM_USAGE
vbo_memory_usage -= verts->vram_size;
#endif
}
if (verts->data)
{
free(verts->data);
verts->data = NULL;
}
}
void GWN_vertbuf_discard(Gwn_VertBuf* verts)
{
if (verts->vbo_id)
{
GWN_buf_id_free(verts->vbo_id);
#if VRAM_USAGE
vbo_memory_usage -= verts->vram_size;
#endif
}
if (verts->data)
{
free(verts->data);
}
free(verts);
}
unsigned GWN_vertbuf_size_get(const Gwn_VertBuf* verts)
{
return vertex_buffer_size(&verts->format, verts->vertex_ct);
}
void GWN_vertbuf_data_alloc(Gwn_VertBuf* verts, unsigned v_ct)
{
Gwn_VertFormat* 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(GWN_vertbuf_size_get(verts));
}
void GWN_vertbuf_data_resize(Gwn_VertBuf* 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 || verts->data_dynamic); // has not been sent to VRAM
#endif
// for dynamic buffers
verts->data_resized = true;
verts->vertex_ct = v_ct;
verts->data = realloc(verts->data, GWN_vertbuf_size_get(verts));
// TODO: skip realloc if v_ct < existing vertex count
// extra space will be reclaimed, and never sent to VRAM (see VertexBuffer_prime)
}
void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, const void* data)
{
const Gwn_VertFormat* format = &verts->format;
const Gwn_VertAttr* a = format->attribs + a_idx;
verts->data_dirty = true;
#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 GWN_vertbuf_attr_fill(Gwn_VertBuf* verts, unsigned a_idx, const void* data)
{
const Gwn_VertFormat* format = &verts->format;
const Gwn_VertAttr* a = format->attribs + a_idx;
verts->data_dirty = true;
#if TRUST_NO_ONE
assert(a_idx < format->attrib_ct);
#endif
const unsigned stride = a->sz; // tightly packed input data
GWN_vertbuf_attr_fill_stride(verts, a_idx, stride, data);
}
void GWN_vertbuf_attr_fill_stride(Gwn_VertBuf* verts, unsigned a_idx, unsigned stride, const void* data)
{
const Gwn_VertFormat* format = &verts->format;
const Gwn_VertAttr* a = format->attribs + a_idx;
verts->data_dirty = true;
#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);
}
}
void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertBufRaw *access)
{
const Gwn_VertFormat* format = &verts->format;
const Gwn_VertAttr* 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
access->size = a->sz;
access->stride = format->stride;
access->data = (GLubyte*)verts->data + a->offset;
access->data_init = access->data;
#if TRUST_NO_ONE
access->_data_end = access->data_init + (size_t)(verts->vertex_ct * format->stride);
#endif
}
static void VertexBuffer_prime(Gwn_VertBuf* verts)
{
unsigned buffer_sz = GWN_vertbuf_size_get(verts);
#if VRAM_USAGE
vbo_memory_usage += buffer_sz;
verts->vram_size = buffer_sz;
#endif
verts->vbo_id = GWN_buf_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, (verts->data_dynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
// now that GL has a copy, discard original
if (!verts->data_dynamic)
{
free(verts->data);
verts->data = NULL;
}
verts->data_dirty = false;
}
static void VertexBuffer_update(Gwn_VertBuf* verts)
{
unsigned buffer_sz = GWN_vertbuf_size_get(verts);
#if VRAM_USAGE
vbo_memory_usage -= verts->vram_size;
vbo_memory_usage += buffer_sz;
verts->vram_size = buffer_sz;
#endif
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
// fill with delicious data & send to GPU ... AGAIN
if (verts->data_resized)
glBufferData(GL_ARRAY_BUFFER, buffer_sz, verts->data, GL_DYNAMIC_DRAW);
else
glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_sz, verts->data); // .. todo try glMapBuffer
verts->data_dirty = false;
verts->data_resized = false;
}
void GWN_vertbuf_use(Gwn_VertBuf* verts)
{
if (!verts->vbo_id)
VertexBuffer_prime(verts);
else if (verts->data_dirty)
VertexBuffer_update(verts);
else
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
}
unsigned GWN_vertbuf_get_memory_usage(void)
{
return vbo_memory_usage;
}