DRW/GWN: Bypass glUseProgram.

Turns out to be the call that was destroying performance.

I get 18ms->6ms improvement of drawing time with 10 000 unique objects.

And we can still improve upon this!
This commit is contained in:
Clément Foucault 2018-02-25 05:24:06 +01:00
parent 24d51a05a9
commit 241c90c92d
Notes: blender-bot 2023-02-14 06:10:04 +01:00
Referenced by issue #54178, Blender UI crash
3 changed files with 107 additions and 101 deletions

View File

@ -95,8 +95,8 @@ int GWN_batch_vertbuf_add_ex(Gwn_Batch*, Gwn_VertBuf*, bool own_vbo);
#define GWN_batch_vertbuf_add(batch, verts) \
GWN_batch_vertbuf_add_ex(batch, verts, false)
void GWN_batch_program_set_no_use(Gwn_Batch*, GLuint program, const Gwn_ShaderInterface*);
void GWN_batch_program_set(Gwn_Batch*, GLuint program, const Gwn_ShaderInterface*);
void GWN_batch_program_unset(Gwn_Batch*);
// Entire batch draws with one shader program, but can be redrawn later with another program.
// Vertex shader's inputs must be compatible with the batch's vertex format.

View File

@ -187,112 +187,118 @@ int GWN_batch_vertbuf_add_ex(
return -1;
}
void GWN_batch_program_set(Gwn_Batch* batch, GLuint program, const Gwn_ShaderInterface* shaderface)
static GLuint batch_vao_get(Gwn_Batch *batch)
{
// Search through cache
if (batch->is_dynamic_vao_count)
{
for (int i = 0; i < batch->dynamic_vaos.count; ++i)
if (batch->dynamic_vaos.interfaces[i] == batch->interface)
return batch->dynamic_vaos.vao_ids[i];
}
else
{
for (int i = 0; i < GWN_BATCH_VAO_STATIC_LEN; ++i)
if (batch->static_vaos.interfaces[i] == batch->interface)
return batch->static_vaos.vao_ids[i];
}
// Set context of this batch.
// It will be bound to it until gwn_batch_vao_cache_clear is called.
// Until then it can only be drawn with this context.
if (batch->context == NULL)
{
batch->context = GWN_context_active_get();
gwn_context_add_batch(batch->context, batch);
}
#if TRUST_NO_ONE && 0 // disabled until we use a separate single context for UI.
else // Make sure you are not trying to draw this batch in another context.
assert(batch->context == GWN_context_active_get());
#endif
// Cache miss, time to add a new entry!
GLuint new_vao = 0;
if (!batch->is_dynamic_vao_count)
{
int i; // find first unused slot
for (i = 0; i < GWN_BATCH_VAO_STATIC_LEN; ++i)
if (batch->static_vaos.vao_ids[i] == 0)
break;
if (i < GWN_BATCH_VAO_STATIC_LEN)
{
batch->static_vaos.interfaces[i] = batch->interface;
batch->static_vaos.vao_ids[i] = new_vao = GWN_vao_alloc();
}
else
{
// Not enough place switch to dynamic.
batch->is_dynamic_vao_count = true;
// Erase previous entries, they will be added back if drawn again.
for (int j = 0; j < GWN_BATCH_VAO_STATIC_LEN; ++j)
{
GWN_shaderinterface_remove_batch_ref((Gwn_ShaderInterface*)batch->static_vaos.interfaces[j], batch);
GWN_vao_free(batch->static_vaos.vao_ids[j], batch->context);
}
// Init dynamic arrays and let the branch below set the values.
batch->dynamic_vaos.count = GWN_BATCH_VAO_DYN_ALLOC_COUNT;
batch->dynamic_vaos.interfaces = calloc(batch->dynamic_vaos.count, sizeof(Gwn_ShaderInterface*));
batch->dynamic_vaos.vao_ids = calloc(batch->dynamic_vaos.count, sizeof(GLuint));
}
}
if (batch->is_dynamic_vao_count)
{
int i; // find first unused slot
for (i = 0; i < batch->dynamic_vaos.count; ++i)
if (batch->dynamic_vaos.vao_ids[i] == 0)
break;
if (i == batch->dynamic_vaos.count)
{
// Not enough place, realloc the array.
i = batch->dynamic_vaos.count;
batch->dynamic_vaos.count += GWN_BATCH_VAO_DYN_ALLOC_COUNT;
batch->dynamic_vaos.interfaces = realloc(batch->dynamic_vaos.interfaces, sizeof(Gwn_ShaderInterface*) * batch->dynamic_vaos.count);
batch->dynamic_vaos.vao_ids = realloc(batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count);
memset(batch->dynamic_vaos.interfaces + i, 0, sizeof(Gwn_ShaderInterface*) * GWN_BATCH_VAO_DYN_ALLOC_COUNT);
memset(batch->dynamic_vaos.vao_ids + i, 0, sizeof(GLuint) * GWN_BATCH_VAO_DYN_ALLOC_COUNT);
}
batch->dynamic_vaos.interfaces[i] = batch->interface;
batch->dynamic_vaos.vao_ids[i] = new_vao = GWN_vao_alloc();
}
GWN_shaderinterface_add_batch_ref((Gwn_ShaderInterface*)batch->interface, batch);
#if TRUST_NO_ONE
assert(new_vao != 0);
#endif
// We just got a fresh VAO we need to initialize it.
glBindVertexArray(new_vao);
batch_update_program_bindings(batch, 0);
glBindVertexArray(0);
return new_vao;
}
void GWN_batch_program_set_no_use(Gwn_Batch* batch, GLuint program, const Gwn_ShaderInterface* shaderface)
{
#if TRUST_NO_ONE
assert(glIsProgram(shaderface->program));
assert(batch->program_in_use == 0);
#endif
batch->vao_id = 0;
batch->program = program;
batch->interface = shaderface;
// Search through cache
if (batch->is_dynamic_vao_count)
{
for (int i = 0; i < batch->dynamic_vaos.count && batch->vao_id == 0; ++i)
if (batch->dynamic_vaos.interfaces[i] == shaderface)
batch->vao_id = batch->dynamic_vaos.vao_ids[i];
}
else
{
for (int i = 0; i < GWN_BATCH_VAO_STATIC_LEN && batch->vao_id == 0; ++i)
if (batch->static_vaos.interfaces[i] == shaderface)
batch->vao_id = batch->static_vaos.vao_ids[i];
}
if (batch->vao_id == 0)
{
if (batch->context == NULL)
{
batch->context = GWN_context_active_get();
gwn_context_add_batch(batch->context, batch);
}
#if TRUST_NO_ONE && 0 // disabled until we use a separate single context for UI.
else // Make sure you are not trying to draw this batch in another context.
assert(batch->context == GWN_context_active_get());
#endif
// Cache miss, time to add a new entry!
if (!batch->is_dynamic_vao_count)
{
int i; // find first unused slot
for (i = 0; i < GWN_BATCH_VAO_STATIC_LEN; ++i)
if (batch->static_vaos.vao_ids[i] == 0)
break;
if (i < GWN_BATCH_VAO_STATIC_LEN)
{
batch->static_vaos.interfaces[i] = shaderface;
batch->static_vaos.vao_ids[i] = batch->vao_id = GWN_vao_alloc();
}
else
{
// Not enough place switch to dynamic.
batch->is_dynamic_vao_count = true;
// Erase previous entries, they will be added back if drawn again.
for (int j = 0; j < GWN_BATCH_VAO_STATIC_LEN; ++j)
{
GWN_shaderinterface_remove_batch_ref((Gwn_ShaderInterface*)batch->static_vaos.interfaces[j], batch);
GWN_vao_free(batch->static_vaos.vao_ids[j], batch->context);
}
// Init dynamic arrays and let the branch below set the values.
batch->dynamic_vaos.count = GWN_BATCH_VAO_DYN_ALLOC_COUNT;
batch->dynamic_vaos.interfaces = calloc(batch->dynamic_vaos.count, sizeof(Gwn_ShaderInterface*));
batch->dynamic_vaos.vao_ids = calloc(batch->dynamic_vaos.count, sizeof(GLuint));
}
}
if (batch->is_dynamic_vao_count)
{
int i; // find first unused slot
for (i = 0; i < batch->dynamic_vaos.count; ++i)
if (batch->dynamic_vaos.vao_ids[i] == 0)
break;
if (i == batch->dynamic_vaos.count)
{
// Not enough place, realloc the array.
i = batch->dynamic_vaos.count;
batch->dynamic_vaos.count += GWN_BATCH_VAO_DYN_ALLOC_COUNT;
batch->dynamic_vaos.interfaces = realloc(batch->dynamic_vaos.interfaces, sizeof(Gwn_ShaderInterface*) * batch->dynamic_vaos.count);
batch->dynamic_vaos.vao_ids = realloc(batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count);
memset(batch->dynamic_vaos.interfaces + i, 0, sizeof(Gwn_ShaderInterface*) * GWN_BATCH_VAO_DYN_ALLOC_COUNT);
memset(batch->dynamic_vaos.vao_ids + i, 0, sizeof(GLuint) * GWN_BATCH_VAO_DYN_ALLOC_COUNT);
}
batch->dynamic_vaos.interfaces[i] = shaderface;
batch->dynamic_vaos.vao_ids[i] = batch->vao_id = GWN_vao_alloc();
}
GWN_shaderinterface_add_batch_ref((Gwn_ShaderInterface*)shaderface, batch);
// We just got a fresh VAO we need to initialize it.
glBindVertexArray(batch->vao_id);
batch_update_program_bindings(batch, 0);
glBindVertexArray(0);
}
GWN_batch_program_use_begin(batch); // hack! to make Batch_Uniform* simpler
batch->program = program;
batch->vao_id = batch_vao_get(batch);
}
// fclem : hack !
// we need this because we don't want to unbind the shader between drawcalls
// but we still want the correct shader to be bound outside the draw manager
void GWN_batch_program_unset(Gwn_Batch* batch)
void GWN_batch_program_set(Gwn_Batch* batch, GLuint program, const Gwn_ShaderInterface* shaderface)
{
batch->program_in_use = false;
GWN_batch_program_set_no_use(batch, program, shaderface);
GWN_batch_program_use_begin(batch); // hack! to make Batch_Uniform* simpler
}
void gwn_batch_remove_interface_ref(Gwn_Batch* batch, const Gwn_ShaderInterface* interface)

View File

@ -1781,16 +1781,16 @@ static void draw_geometry_execute_ex(
}
/* step 2 : bind vertex array & draw */
GWN_batch_program_set(geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
GWN_batch_program_set_no_use(geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
/* XXX hacking gawain. we don't want to call glUseProgram! (huge performance loss) */
geom->program_in_use = true;
if (ELEM(shgroup->type, DRW_SHG_INSTANCE, DRW_SHG_INSTANCE_EXTERNAL)) {
GWN_batch_draw_range_ex(geom, start, count, true);
}
else {
GWN_batch_draw_range(geom, start, count);
}
/* XXX this just tells gawain we are done with the shader.
* This does not unbind the shader. */
GWN_batch_program_unset(geom);
geom->program_in_use = false; /* XXX hacking gawain */
}
static void draw_geometry_execute(DRWShadingGroup *shgroup, Gwn_Batch *geom)