GWN: Vertex Buffer: Remove the use of glMapBufferRange

We revert to the malloc/realloc and manually manage the upload.
There seems to be a performance penalty from using glMapBuffer on some
hardware, prefering way is glBufferData(NULL) with glBufferSubData.
This commit is contained in:
Clément Foucault
2018-03-19 16:13:00 +01:00
parent 772e558e92
commit b5bf3011bf
2 changed files with 72 additions and 87 deletions

View File

@@ -25,13 +25,14 @@
typedef enum {
// can be extended to support more types
GWN_USAGE_STREAM,
GWN_USAGE_STATIC,
GWN_USAGE_STATIC, // do not keep data in memory
GWN_USAGE_DYNAMIC
} Gwn_UsageType;
typedef struct Gwn_VertBuf {
Gwn_VertFormat format;
unsigned vertex_ct;
bool dirty;
GLubyte* data; // NULL indicates data in VRAM (unmapped)
GLuint vbo_id; // 0 indicates not yet allocated
Gwn_UsageType usage; // usage hint for GL optimisation
@@ -53,10 +54,7 @@ void GWN_vertbuf_init_with_format_ex(Gwn_VertBuf*, const Gwn_VertFormat*, Gwn_Us
unsigned GWN_vertbuf_size_get(const Gwn_VertBuf*);
void GWN_vertbuf_data_alloc(Gwn_VertBuf*, unsigned v_ct);
void GWN_vertbuf_data_resize_ex(Gwn_VertBuf*, unsigned v_ct, bool keep_data);
#define GWN_vertbuf_data_resize(verts, v_ct) \
GWN_vertbuf_data_resize_ex(verts, v_ct, true)
void GWN_vertbuf_data_resize(Gwn_VertBuf*, unsigned v_ct);
// The most important set_attrib variant is the untyped one. Get it right first.
// It takes a void* so the app developer is responsible for matching their app data types

View File

@@ -52,6 +52,7 @@ void GWN_vertbuf_init(Gwn_VertBuf* verts, Gwn_UsageType usage)
{
memset(verts, 0, sizeof(Gwn_VertBuf));
verts->usage = usage;
verts->dirty = true;
}
void GWN_vertbuf_init_with_format_ex(Gwn_VertBuf* verts, const Gwn_VertFormat* format, Gwn_UsageType usage)
@@ -72,6 +73,9 @@ void GWN_vertbuf_discard(Gwn_VertBuf* verts)
#endif
}
if (verts->data)
free(verts->data);
free(verts);
}
@@ -80,90 +84,58 @@ unsigned GWN_vertbuf_size_get(const Gwn_VertBuf* verts)
return vertex_buffer_size(&verts->format, verts->vertex_ct);
}
// create a new allocation, discarding any existing data
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;
#if TRUST_NO_ONE
assert(verts->vbo_id == 0);
// catch any unnecessary use
assert(verts->vertex_ct != v_ct || verts->data == NULL);
#endif
unsigned buffer_sz = GWN_vertbuf_size_get(verts);
#if VRAM_USAGE
vbo_memory_usage += buffer_sz;
#endif
// only create the buffer the 1st time
if (verts->vbo_id == 0)
verts->vbo_id = GWN_buf_id_alloc();
// create an array buffer and map it to memory
verts->vbo_id = GWN_buf_id_alloc();
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
verts->data = glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_sz, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
}
void GWN_vertbuf_data_resize_ex(Gwn_VertBuf* verts, unsigned v_ct, bool keep_data)
{
#if TRUST_NO_ONE
assert(verts->vbo_id != 0);
#endif
if (verts->vertex_ct == v_ct)
return;
unsigned old_buf_sz = GWN_vertbuf_size_get(verts);
verts->vertex_ct = v_ct;
unsigned new_buf_sz = GWN_vertbuf_size_get(verts);
#if VRAM_USAGE
vbo_memory_usage += new_buf_sz - old_buf_sz;
#endif
if (keep_data)
{
// we need to do a copy to keep the existing data
GLuint vbo_tmp;
glGenBuffers(1, &vbo_tmp);
// only copy the data that can fit in the new buffer
unsigned copy_sz = (old_buf_sz < new_buf_sz) ? old_buf_sz : new_buf_sz;
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo_tmp);
glBufferData(GL_COPY_WRITE_BUFFER, copy_sz, NULL, GL_STREAM_COPY);
glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
// we cannot copy from/to a mapped buffer
if (verts->data)
glUnmapBuffer(GL_COPY_READ_BUFFER);
// save data, resize the buffer, restore data
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, copy_sz);
glBufferData(GL_COPY_READ_BUFFER, new_buf_sz, NULL, convert_usage_type_to_gl(verts->usage));
glCopyBufferSubData(GL_COPY_WRITE_BUFFER, GL_COPY_READ_BUFFER, 0, 0, copy_sz);
glDeleteBuffers(1, &vbo_tmp);
}
else
{
glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id);
glBufferData(GL_COPY_READ_BUFFER, new_buf_sz, NULL, convert_usage_type_to_gl(verts->usage));
}
// if the buffer was mapped, update it's pointer
// discard previous data if any
if (verts->data)
verts->data = glMapBufferRange(GL_COPY_READ_BUFFER, 0, new_buf_sz, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
free(verts->data);
#if VRAM_USAGE
vbo_memory_usage -= GWN_vertbuf_size_get(verts);
#endif
verts->dirty = true;
verts->vertex_ct = v_ct;
verts->data = malloc(sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
#if VRAM_USAGE
vbo_memory_usage += GWN_vertbuf_size_get(verts);
#endif
}
static void VertexBuffer_map(Gwn_VertBuf* verts)
// resize buffer keeping existing data
void GWN_vertbuf_data_resize(Gwn_VertBuf* verts, unsigned v_ct)
{
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
verts->data = glMapBufferRange(GL_ARRAY_BUFFER, 0, GWN_vertbuf_size_get(verts), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
}
#if TRUST_NO_ONE
assert(verts->data != NULL);
assert(verts->vertex_ct != v_ct);
#endif
static void VertexBuffer_unmap(Gwn_VertBuf* verts)
{
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
glUnmapBuffer(GL_ARRAY_BUFFER);
verts->data = NULL;
#if VRAM_USAGE
vbo_memory_usage -= GWN_vertbuf_size_get(verts);
#endif
verts->dirty = true;
verts->vertex_ct = v_ct;
verts->data = realloc(verts->data, sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
#if VRAM_USAGE
vbo_memory_usage += GWN_vertbuf_size_get(verts);
#endif
}
void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, const void* data)
@@ -174,11 +146,10 @@ void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, co
#if TRUST_NO_ONE
assert(a_idx < format->attrib_ct);
assert(v_idx < verts->vertex_ct);
assert(verts->data != NULL);
#endif
if (verts->data == NULL)
VertexBuffer_map(verts);
verts->dirty = true;
memcpy((GLubyte*)verts->data + a->offset + v_idx * format->stride, data, a->sz);
}
@@ -203,13 +174,12 @@ void GWN_vertbuf_attr_fill_stride(Gwn_VertBuf* verts, unsigned a_idx, unsigned s
#if TRUST_NO_ONE
assert(a_idx < format->attrib_ct);
assert(verts->data != NULL);
#endif
verts->dirty = true;
const unsigned vertex_ct = verts->vertex_ct;
if (verts->data == NULL)
VertexBuffer_map(verts);
if (format->attrib_ct == 1 && stride == format->stride)
{
// we can copy it all at once
@@ -230,10 +200,10 @@ void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertB
#if TRUST_NO_ONE
assert(a_idx < format->attrib_ct);
assert(verts->data != NULL);
#endif
if (verts->data == NULL)
VertexBuffer_map(verts);
verts->dirty = true;
access->size = a->sz;
access->stride = format->stride;
@@ -244,13 +214,30 @@ void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertB
#endif
}
static void VertBuffer_upload_data(Gwn_VertBuf* verts)
{
unsigned buffer_sz = GWN_vertbuf_size_get(verts);
// orphan the vbo to avoid sync
glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
// upload data
glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_sz, verts->data);
if (verts->usage == GWN_USAGE_STATIC)
{
free(verts->data);
verts->data = NULL;
}
verts->dirty = false;
}
void GWN_vertbuf_use(Gwn_VertBuf* verts)
{
if (verts->data)
// this also calls glBindBuffer
VertexBuffer_unmap(verts);
else
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
if (verts->dirty)
VertBuffer_upload_data(verts);
}
unsigned GWN_vertbuf_get_memory_usage(void)