Gawain: vertex format now uses fixed allocations (CPU perf++)

API stays exactly the same.

Attribute names can still be of variable length, as long as the average length does not exceed AVG_VERTEX_ATTRIB_NAME_LEN. Since this includes unused attributes (length = 0) the current avg of 5 might even be too high.
This commit is contained in:
Mike Erwin 2016-10-13 14:49:33 -04:00
parent dfa5b32c8c
commit 945f8e3f93
2 changed files with 36 additions and 18 deletions

View File

@ -21,29 +21,19 @@
void VertexFormat_clear(VertexFormat* format)
{
for (unsigned a = 0; a < format->attrib_ct; ++a)
free(format->attribs[a].name);
#if TRUST_NO_ONE
memset(format, 0, sizeof(VertexFormat));
#else
format->attrib_ct = 0;
format->packed = false;
format->name_offset = 0;
#endif
}
void VertexFormat_copy(VertexFormat* dest, const VertexFormat* src)
{
// discard dest format's old name strings
for (unsigned a = 0; a < dest->attrib_ct; ++a)
free(dest->attribs[a].name);
// copy regular struct fields
memcpy(dest, src, sizeof(VertexFormat));
// give dest attribs their own copy of name strings
for (unsigned i = 0; i < src->attrib_ct; ++i)
dest->attribs[i].name = strdup(src->attribs[i].name);
}
static unsigned comp_sz(GLenum type)
@ -79,6 +69,33 @@ unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct)
return format->stride * vertex_ct;
}
static const char* copy_attrib_name(VertexFormat* format, const char* name)
{
// strncpy does 110% of what we need; let's do exactly 100%
char* name_copy = format->names + format->name_offset;
unsigned available = VERTEX_ATTRIB_NAMES_BUFFER_LEN - format->name_offset;
bool terminated = false;
for (unsigned i = 0; i < available; ++i)
{
const char c = name[i];
name_copy[i] = c;
if (c == '\0')
{
terminated = true;
format->name_offset += (i + 1);
break;
}
}
#if TRUST_NO_ONE
assert(terminated);
assert(format->name_offset <= VERTEX_ATTRIB_NAMES_BUFFER_LEN);
#endif
return name_copy;
}
unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode fetch_mode)
{
#if TRUST_NO_ONE
@ -114,7 +131,7 @@ unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, un
const unsigned attrib_id = format->attrib_ct++;
Attrib* attrib = format->attribs + attrib_id;
attrib->name = strdup(name);
attrib->name = copy_attrib_name(format, name);
attrib->comp_type = comp_type;
attrib->comp_ct = comp_ct;
attrib->sz = attrib_sz(attrib);
@ -149,11 +166,8 @@ void VertexFormat_pack(VertexFormat* format)
// later we can implement more efficient packing w/ reordering
// (keep attrib ID order, adjust their offsets to reorder in buffer)
// TODO: concatentate name strings into attribs[0].name, point attribs[i] to
// offset into the combined string. Free all other name strings. Could save more
// space by storing combined string in VertexFormat, with each attrib having an
// offset into it. Could also append each name string as it's added... pack()
// could alloc just enough to hold the final combo string. And just enough to
// TODO:
// realloc just enough to hold the final combo string. And just enough to
// hold used attribs, not all 16.
Attrib* a0 = format->attribs + 0;

View File

@ -14,6 +14,8 @@
#include "common.h"
#define MAX_VERTEX_ATTRIBS 16
#define AVG_VERTEX_ATTRIB_NAME_LEN 5
#define VERTEX_ATTRIB_NAMES_BUFFER_LEN ((AVG_VERTEX_ATTRIB_NAME_LEN + 1) * MAX_VERTEX_ATTRIBS)
typedef enum {
KEEP_FLOAT,
@ -28,7 +30,7 @@ typedef struct {
unsigned sz; // size in bytes, 1 to 16
unsigned offset; // from beginning of vertex, in bytes
VertexFetchMode fetch_mode;
char* name; // TODO: shared allocation of all names within a VertexFormat
const char* name;
} Attrib;
typedef struct {
@ -36,6 +38,8 @@ typedef struct {
unsigned stride; // stride in bytes, 1 to 256
bool packed;
Attrib attribs[MAX_VERTEX_ATTRIBS]; // TODO: variable-size attribs array
char names[VERTEX_ATTRIB_NAMES_BUFFER_LEN];
unsigned name_offset;
} VertexFormat;
void VertexFormat_clear(VertexFormat*);