DRW: Fix/refactor UBO & Texture binding.

Previous approach was not clear enough and caused problems.
UBOs were taking slots and not release them after a shading group even
if this UBO was only for this Shading Group (notably the nodetree ubo,
since we now share the same GPUShader for identical trees).

So I choose to have a better defined approach:
- Standard texture and ubo calls are assured to be valid for the shgrp
they are called from.
- (new) Persistent texture and ubo calls are assured to be valid accross
shgrps unless the shader changes.

The standards calls are still valids for the next shgrp but are not assured
to be so if this new shgrp binds a new texture.

This enables some optimisations by not adding redundant texture and ubo
binds.
This commit is contained in:
Clément Foucault 2018-03-16 08:43:52 +01:00
parent 43d0943141
commit 93e26cb770
5 changed files with 95 additions and 41 deletions

View File

@ -382,7 +382,9 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state);
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, unsigned int mask);
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const struct GPUTexture *tex);
void DRW_shgroup_uniform_texture_persistent(DRWShadingGroup *shgroup, const char *name, const struct GPUTexture *tex);
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const struct GPUUniformBuffer *ubo);
void DRW_shgroup_uniform_block_persistent(DRWShadingGroup *shgroup, const char *name, const struct GPUUniformBuffer *ubo);
void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, struct GPUTexture **tex);
void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize);
void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize);

View File

@ -446,13 +446,13 @@ static void drw_viewport_var_init(void)
DST.RST.bound_texs = MEM_callocN(sizeof(GPUTexture *) * GPU_max_textures(), "Bound GPUTexture refs");
}
if (DST.RST.bound_tex_slots == NULL) {
DST.RST.bound_tex_slots = MEM_callocN(sizeof(bool) * GPU_max_textures(), "Bound Texture Slots");
DST.RST.bound_tex_slots = MEM_callocN(sizeof(char) * GPU_max_textures(), "Bound Texture Slots");
}
if (DST.RST.bound_ubos == NULL) {
DST.RST.bound_ubos = MEM_callocN(sizeof(GPUUniformBuffer *) * GPU_max_ubo_binds(), "Bound GPUUniformBuffer refs");
}
if (DST.RST.bound_ubo_slots == NULL) {
DST.RST.bound_ubo_slots = MEM_callocN(sizeof(bool) * GPU_max_textures(), "Bound Ubo Slots");
DST.RST.bound_ubo_slots = MEM_callocN(sizeof(char) * GPU_max_textures(), "Bound Ubo Slots");
}
if (view_ubo == NULL) {

View File

@ -159,8 +159,10 @@ typedef enum {
DRW_UNIFORM_INT,
DRW_UNIFORM_FLOAT,
DRW_UNIFORM_TEXTURE,
DRW_UNIFORM_TEXTURE_PERSIST,
DRW_UNIFORM_BUFFER,
DRW_UNIFORM_BLOCK
DRW_UNIFORM_BLOCK,
DRW_UNIFORM_BLOCK_PERSIST
} DRWUniformType;
struct DRWUniform {
@ -335,10 +337,10 @@ typedef struct DRWManager {
/** GPU Resource State: Memory storage between drawing. */
struct {
GPUTexture **bound_texs;
bool *bound_tex_slots;
char *bound_tex_slots;
int bind_tex_inc;
GPUUniformBuffer **bound_ubos;
bool *bound_ubo_slots;
char *bound_ubo_slots;
int bind_ubo_inc;
} RST;
} DRWManager;

View File

@ -99,7 +99,7 @@ static void drw_interface_uniform(DRWShadingGroup *shgroup, const char *name,
DRWUniformType type, const void *value, int length, int arraysize)
{
int location;
if (type == DRW_UNIFORM_BLOCK) {
if (ELEM(type, DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_PERSIST)) {
location = GPU_shader_get_uniform_block(shgroup->shader, name);
}
else {
@ -126,12 +126,26 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, con
drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 1);
}
/* Same as DRW_shgroup_uniform_texture but is garanteed to be bound if shader does not change between shgrp. */
void DRW_shgroup_uniform_texture_persistent(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
{
BLI_assert(tex != NULL);
drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE_PERSIST, tex, 0, 1);
}
void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
{
BLI_assert(ubo != NULL);
drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 1);
}
/* Same as DRW_shgroup_uniform_block but is garanteed to be bound if shader does not change between shgrp. */
void DRW_shgroup_uniform_block_persistent(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
{
BLI_assert(ubo != NULL);
drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK_PERSIST, ubo, 0, 1);
}
void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
{
drw_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, tex, 0, 1);
@ -461,7 +475,7 @@ static void drw_interface_init(DRWShadingGroup *shgroup, GPUShader *shader)
int view_ubo_location = GPU_shader_get_uniform_block(shader, "viewBlock");
if (view_ubo_location != -1) {
drw_interface_uniform_create_ex(shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, view_ubo, 0, 1);
drw_interface_uniform_create_ex(shgroup, view_ubo_location, DRW_UNIFORM_BLOCK_PERSIST, view_ubo, 0, 1);
}
else {
/* Only here to support builtin shaders. This should not be used by engines. */

View File

@ -704,63 +704,87 @@ static void draw_geometry_execute(DRWShadingGroup *shgroup, Gwn_Batch *geom)
draw_geometry_execute_ex(shgroup, geom, 0, 0, false);
}
static void bind_texture(GPUTexture *tex)
enum {
BIND_NONE = 0,
BIND_TEMP = 1, /* Release slot after this shading group. */
BIND_PERSIST = 2, /* Release slot only after the next shader change. */
};
static void bind_texture(GPUTexture *tex, char bind_type)
{
int index;
char *slot_flags = DST.RST.bound_tex_slots;
int bind_num = GPU_texture_bound_number(tex);
if (bind_num == -1) {
for (int i = 0; i < GPU_max_textures(); ++i) {
DST.RST.bind_tex_inc = (DST.RST.bind_tex_inc + 1) % GPU_max_textures();
if (DST.RST.bound_tex_slots[DST.RST.bind_tex_inc] == false) {
if (DST.RST.bound_texs[DST.RST.bind_tex_inc] != NULL) {
GPU_texture_unbind(DST.RST.bound_texs[DST.RST.bind_tex_inc]);
index = DST.RST.bind_tex_inc = (DST.RST.bind_tex_inc + 1) % GPU_max_textures();
if (slot_flags[index] == BIND_NONE) {
if (DST.RST.bound_texs[index] != NULL) {
GPU_texture_unbind(DST.RST.bound_texs[index]);
}
GPU_texture_bind(tex, DST.RST.bind_tex_inc);
DST.RST.bound_texs[DST.RST.bind_tex_inc] = tex;
DST.RST.bound_tex_slots[DST.RST.bind_tex_inc] = true;
GPU_texture_bind(tex, index);
DST.RST.bound_texs[index] = tex;
slot_flags[index] = bind_type;
// printf("Binds Texture %d %p\n", DST.RST.bind_tex_inc, tex);
return;
}
}
printf("Not enough texture slots! Reduce number of textures used by your shader.\n");
}
DST.RST.bound_tex_slots[bind_num] = true;
slot_flags[bind_num] = bind_type;
}
static void bind_ubo(GPUUniformBuffer *ubo)
static void bind_ubo(GPUUniformBuffer *ubo, char bind_type)
{
int index;
char *slot_flags = DST.RST.bound_ubo_slots;
int bind_num = GPU_uniformbuffer_bindpoint(ubo);
if (bind_num == -1) {
for (int i = 0; i < GPU_max_ubo_binds(); ++i) {
DST.RST.bind_ubo_inc = (DST.RST.bind_ubo_inc + 1) % GPU_max_ubo_binds();
if (DST.RST.bound_ubo_slots[DST.RST.bind_ubo_inc] == false) {
if (DST.RST.bound_ubos[DST.RST.bind_ubo_inc] != NULL) {
GPU_uniformbuffer_unbind(DST.RST.bound_ubos[DST.RST.bind_ubo_inc]);
index = DST.RST.bind_ubo_inc = (DST.RST.bind_ubo_inc + 1) % GPU_max_ubo_binds();
if (slot_flags[index] == BIND_NONE) {
if (DST.RST.bound_ubos[index] != NULL) {
GPU_uniformbuffer_unbind(DST.RST.bound_ubos[index]);
}
GPU_uniformbuffer_bind(ubo, DST.RST.bind_ubo_inc);
DST.RST.bound_ubos[DST.RST.bind_ubo_inc] = ubo;
DST.RST.bound_ubo_slots[DST.RST.bind_ubo_inc] = true;
GPU_uniformbuffer_bind(ubo, index);
DST.RST.bound_ubos[index] = ubo;
slot_flags[bind_num] = bind_type;
return;
}
}
/* This is not depending on user input.
* It is our responsability to make sure there enough slots. */
BLI_assert(0 && "Not enough ubo slots! This should not happen!\n");
/* printf so user can report bad behaviour */
printf("Not enough ubo slots! This should not happen!\n");
/* This is not depending on user input.
* It is our responsability to make sure there is enough slots. */
BLI_assert(0);
}
DST.RST.bound_ubo_slots[bind_num] = true;
slot_flags[bind_num] = bind_type;
}
static void release_texture_slots(void)
static void release_texture_slots(bool with_persist)
{
memset(DST.RST.bound_tex_slots, 0x0, sizeof(bool) * GPU_max_textures());
if (with_persist) {
memset(DST.RST.bound_tex_slots, 0x0, sizeof(*DST.RST.bound_tex_slots) * GPU_max_ubo_binds());
}
else {
for (int i = 0; i < GPU_max_ubo_binds(); ++i) {
if (DST.RST.bound_tex_slots[i] != BIND_PERSIST)
DST.RST.bound_tex_slots[i] = BIND_NONE;
}
}
}
static void release_ubo_slots(void)
static void release_ubo_slots(bool with_persist)
{
memset(DST.RST.bound_ubo_slots, 0x0, sizeof(bool) * GPU_max_textures());
if (with_persist) {
memset(DST.RST.bound_ubo_slots, 0x0, sizeof(*DST.RST.bound_ubo_slots) * GPU_max_ubo_binds());
}
else {
for (int i = 0; i < GPU_max_ubo_binds(); ++i) {
if (DST.RST.bound_ubo_slots[i] != BIND_PERSIST)
DST.RST.bound_ubo_slots[i] = BIND_NONE;
}
}
}
static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
@ -771,16 +795,17 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
GPUUniformBuffer *ubo;
int val;
float fval;
const bool shader_changed = (DST.shader != shgroup->shader);
if (DST.shader != shgroup->shader) {
if (shader_changed) {
if (DST.shader) GPU_shader_unbind();
GPU_shader_bind(shgroup->shader);
DST.shader = shgroup->shader;
release_texture_slots();
release_ubo_slots();
}
release_ubo_slots(shader_changed);
release_texture_slots(shader_changed);
drw_state_set((pass_state & shgroup->state_extra_disable) | shgroup->state_extra);
drw_stencil_set(shgroup->stencil_mask);
@ -810,7 +835,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
case DRW_UNIFORM_TEXTURE:
tex = (GPUTexture *)uni->value;
BLI_assert(tex);
bind_texture(tex);
bind_texture(tex, BIND_TEMP);
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
break;
case DRW_UNIFORM_TEXTURE_PERSIST:
tex = (GPUTexture *)uni->value;
BLI_assert(tex);
bind_texture(tex, BIND_PERSIST);
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
break;
case DRW_UNIFORM_BUFFER:
@ -819,12 +850,17 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
}
tex = *((GPUTexture **)uni->value);
BLI_assert(tex);
bind_texture(tex);
bind_texture(tex, BIND_TEMP);
GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
break;
case DRW_UNIFORM_BLOCK:
ubo = (GPUUniformBuffer *)uni->value;
bind_ubo(ubo);
bind_ubo(ubo, BIND_TEMP);
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
break;
case DRW_UNIFORM_BLOCK_PERSIST:
ubo = (GPUUniformBuffer *)uni->value;
bind_ubo(ubo, BIND_PERSIST);
GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
break;
}