forked from bartvdbraak/blender
Gawain: Lookup uniforms and attributes from buckets
This way we reduce number of loops from look-over-all-inputs to loop-over-collision, which is expected to be much less CPU ticks. There is still possible optimization: use memory pool of some sort to manage memory needed for hash entries, but that will only speedup shader interface construction / deconstruction time. There are also some trickery happening to speed up process even more in the case there is no hash collisions detected when constructing shader interface.
This commit is contained in:
parent
bda3e14f05
commit
bb9b8b13e5
@ -39,9 +39,18 @@ typedef struct Gwn_ShaderInput {
|
||||
GLint location;
|
||||
} Gwn_ShaderInput;
|
||||
|
||||
typedef struct Gwn_ShaderInput_Entry {
|
||||
struct Gwn_ShaderInput_Entry* next;
|
||||
Gwn_ShaderInput* shader_input;
|
||||
} Gwn_ShaderInput_Entry;
|
||||
|
||||
#define GWN_NUM_SHADERINTERFACE_BUCKETS 1009
|
||||
|
||||
typedef struct Gwn_ShaderInterface {
|
||||
uint16_t uniform_ct;
|
||||
uint16_t attrib_ct;
|
||||
Gwn_ShaderInput_Entry* uniform_buckets[GWN_NUM_SHADERINTERFACE_BUCKETS];
|
||||
Gwn_ShaderInput_Entry* attrib_buckets[GWN_NUM_SHADERINTERFACE_BUCKETS];
|
||||
Gwn_ShaderInput inputs[0]; // dynamic size, uniforms followed by attribs
|
||||
} Gwn_ShaderInterface;
|
||||
|
||||
|
@ -67,6 +67,75 @@ GWN_INLINE void set_input_name(Gwn_ShaderInput* input, const char* name)
|
||||
input->name_hash = hash_string(name);
|
||||
}
|
||||
|
||||
GWN_INLINE void shader_input_to_bucket(Gwn_ShaderInput* input,
|
||||
Gwn_ShaderInput_Entry* buckets[GWN_NUM_SHADERINTERFACE_BUCKETS])
|
||||
{
|
||||
Gwn_ShaderInput_Entry* entry = malloc(sizeof(Gwn_ShaderInput_Entry));
|
||||
const unsigned bucket_index = input->name_hash % GWN_NUM_SHADERINTERFACE_BUCKETS;
|
||||
entry->next = buckets[bucket_index];
|
||||
entry->shader_input = input;
|
||||
buckets[bucket_index] = entry;
|
||||
}
|
||||
|
||||
GWN_INLINE Gwn_ShaderInput* buckets_lookup(Gwn_ShaderInput_Entry* const buckets[GWN_NUM_SHADERINTERFACE_BUCKETS],
|
||||
const char *name)
|
||||
{
|
||||
const unsigned name_hash = hash_string(name);
|
||||
const unsigned bucket_index = name_hash % GWN_NUM_SHADERINTERFACE_BUCKETS;
|
||||
const Gwn_ShaderInput_Entry* entry = buckets[bucket_index];
|
||||
if (entry == NULL)
|
||||
{
|
||||
// Requested uniform is not found at all.
|
||||
return NULL;
|
||||
}
|
||||
// Optimization bit: if there is no hash collision detected when constructing shader interface
|
||||
// it means we can only request the single possible uniform. Surely, it's possible we request
|
||||
// uniform which causes hash collision, but that will be detected in debug builds.
|
||||
if (entry->next == NULL)
|
||||
{
|
||||
if (name_hash == entry->shader_input->name_hash)
|
||||
{
|
||||
#if TRUST_NO_ONE
|
||||
assert(match(entry->shader_input->name, name));
|
||||
#endif
|
||||
return entry->shader_input;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// Work through possible collisions.
|
||||
while (entry != NULL)
|
||||
{
|
||||
Gwn_ShaderInput* uniform = entry->shader_input;
|
||||
entry = entry->next;
|
||||
#if SUPPORT_LEGACY_GLSL
|
||||
if (uniform->name == NULL) continue;
|
||||
#endif
|
||||
if (uniform->name_hash != name_hash)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (match(uniform->name, name))
|
||||
{
|
||||
return uniform;
|
||||
}
|
||||
}
|
||||
return NULL; // not found
|
||||
}
|
||||
|
||||
GWN_INLINE void buckets_free(Gwn_ShaderInput_Entry* buckets[GWN_NUM_SHADERINTERFACE_BUCKETS])
|
||||
{
|
||||
for (unsigned bucket_index = 0; bucket_index < GWN_NUM_SHADERINTERFACE_BUCKETS; ++bucket_index)
|
||||
{
|
||||
Gwn_ShaderInput_Entry *entry = buckets[bucket_index];
|
||||
while (entry != NULL)
|
||||
{
|
||||
Gwn_ShaderInput_Entry *entry_next = entry->next;
|
||||
free(entry);
|
||||
entry = entry_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keep these in sync with Gwn_UniformBuiltin order
|
||||
#define FIRST_MAT4_UNIFORM GWN_UNIFORM_MODELVIEW
|
||||
#define LAST_MAT4_UNIFORM GWN_UNIFORM_PROJECTION_INV
|
||||
@ -240,35 +309,33 @@ Gwn_ShaderInterface* GWN_shaderinterface_create(GLint program)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
|
||||
{
|
||||
Gwn_ShaderInput* input = &shaderface->inputs[i];
|
||||
shader_input_to_bucket(input, shaderface->uniform_buckets);
|
||||
}
|
||||
for (uint32_t i = 0; i < shaderface->attrib_ct; ++i)
|
||||
{
|
||||
Gwn_ShaderInput* input = &shaderface->inputs[i + shaderface->uniform_ct];
|
||||
shader_input_to_bucket(input, shaderface->attrib_buckets);
|
||||
}
|
||||
|
||||
return shaderface;
|
||||
}
|
||||
|
||||
void GWN_shaderinterface_discard(Gwn_ShaderInterface* shaderface)
|
||||
{
|
||||
// allocated as one chunk, so discard is simple
|
||||
// Free memory used by buckets and has entries.
|
||||
buckets_free(shaderface->uniform_buckets);
|
||||
buckets_free(shaderface->attrib_buckets);
|
||||
// Free memory used by shader interface by its self.
|
||||
free(shaderface);
|
||||
}
|
||||
|
||||
const Gwn_ShaderInput* GWN_shaderinterface_uniform(const Gwn_ShaderInterface* shaderface, const char* name)
|
||||
{
|
||||
const unsigned name_hash = hash_string(name);
|
||||
for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
|
||||
{
|
||||
const Gwn_ShaderInput* uniform = shaderface->inputs + i;
|
||||
|
||||
#if SUPPORT_LEGACY_GLSL
|
||||
if (uniform->name == NULL) continue;
|
||||
#endif
|
||||
|
||||
if (uniform->name_hash != name_hash) continue;
|
||||
|
||||
if (match(uniform->name, name))
|
||||
return uniform;
|
||||
|
||||
// TODO: warn if we find a matching builtin, since these can be looked up much quicker --v
|
||||
}
|
||||
|
||||
return NULL; // not found
|
||||
// TODO: Warn if we find a matching builtin, since these can be looked up much quicker.
|
||||
return buckets_lookup(shaderface->uniform_buckets, name);
|
||||
}
|
||||
|
||||
const Gwn_ShaderInput* GWN_shaderinterface_uniform_builtin(const Gwn_ShaderInterface* shaderface, Gwn_UniformBuiltin builtin)
|
||||
@ -291,21 +358,5 @@ const Gwn_ShaderInput* GWN_shaderinterface_uniform_builtin(const Gwn_ShaderInter
|
||||
|
||||
const Gwn_ShaderInput* GWN_shaderinterface_attr(const Gwn_ShaderInterface* shaderface, const char* name)
|
||||
{
|
||||
// attribs are stored after uniforms
|
||||
const uint32_t input_ct = shaderface->uniform_ct + shaderface->attrib_ct;
|
||||
const unsigned name_hash = hash_string(name);
|
||||
for (uint32_t i = shaderface->uniform_ct; i < input_ct; ++i)
|
||||
{
|
||||
const Gwn_ShaderInput* attrib = shaderface->inputs + i;
|
||||
|
||||
#if SUPPORT_LEGACY_GLSL
|
||||
if (attrib->name == NULL) continue;
|
||||
#endif
|
||||
|
||||
if (attrib->name_hash != name_hash) continue;
|
||||
|
||||
if (match(attrib->name, name))
|
||||
return attrib;
|
||||
}
|
||||
return NULL; // not found
|
||||
return buckets_lookup(shaderface->attrib_buckets, name);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user