GPU: ShaderInterface: Refactor to setup all uniform at creation time

This remove the complexity of queriying the locations at runtime and
allows for more performance and upfront binding specifications.

The benefit of doing everything at creation time is that we can assign binding
points in a predictable order which is going to be somewhat the same for
every similar shader.

This also rewrite GPU_vertformat_from_shader to not use shaderface.

This is to keep the shaderface simple. If it becomes necessary to not query
the shader after creation (i.e: vulkan?) we could just create the vert
format in advance at compilation for PyGPU shaders.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D7879
This commit is contained in:
Clément Foucault 2020-06-02 12:11:39 +02:00
parent bdda53fdb2
commit cecda64e2e
Notes: blender-bot 2023-02-14 19:45:25 +01:00
Referenced by issue #78175, Hair drawing crash with NVIDIA GPU under macOS 10.15
Referenced by issue #77496, Invisible loose edges on objects used in bone visualization
Referenced by issue #70445, Very slow Eevee performance on Mac with AMD Radeon
8 changed files with 342 additions and 247 deletions

View File

@ -263,11 +263,6 @@ static void updateGLSLShader(OCIO_GLSLShader *shader,
shader->curve_mapping_loc = glGetUniformLocation(shader->program, "curve_mapping");
glUseProgram(shader->program);
/* Set texture bind point uniform once. This is saved by the shader. */
glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0);
glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2);
glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3);
glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4);
/* Set UBO binding location. */
GLuint index = glGetUniformBlockIndex(shader->program, "OCIO_GLSLCurveMappingParameters");
@ -276,6 +271,12 @@ static void updateGLSLShader(OCIO_GLSLShader *shader,
/* TODO(fclem) Remove this. Make caller always assume viewport space and
* specify texco via vertex attribs. */
shader->interface = GPU_shaderinterface_create(shader->program);
/* Set texture bind point uniform once. This is saved by the shader. */
glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0);
glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2);
glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3);
glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4);
}
shader->cacheId = cache_id;

View File

@ -1604,7 +1604,7 @@ static void icon_draw_cache_texture_flush_ex(GLuint texture,
GPU_shader_bind(shader);
int img_loc = GPU_shader_get_uniform_ensure(shader, "image");
int data_loc = GPU_shader_get_uniform_ensure(shader, "calls_data[0]");
int data_loc = GPU_shader_get_uniform_ensure(shader, "calls_data");
glUniform1i(img_loc, 0);
glUniform4fv(data_loc, ICON_DRAW_CACHE_SIZE * 3, (float *)texture_draw_calls->drawcall_cache);

View File

@ -64,33 +64,34 @@ typedef enum {
} GPUUniformBuiltin;
typedef struct GPUShaderInput {
struct GPUShaderInput *next;
uint32_t name_offset;
uint name_hash;
/** Only for uniform inputs. */
GPUUniformBuiltin builtin_type;
/** Only for attribute inputs. */
uint32_t gl_type;
/** Only for attribute inputs. */
int32_t size;
uint32_t name_hash;
int32_t location;
/** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */
int32_t binding;
} GPUShaderInput;
#define GPU_NUM_SHADERINTERFACE_BUCKETS 257
#define GPU_SHADERINTERFACE_REF_ALLOC_COUNT 16
typedef struct GPUShaderInterface {
int32_t program;
uint32_t name_buffer_offset;
GPUShaderInput *attr_buckets[GPU_NUM_SHADERINTERFACE_BUCKETS];
GPUShaderInput *uniform_buckets[GPU_NUM_SHADERINTERFACE_BUCKETS];
GPUShaderInput *ubo_buckets[GPU_NUM_SHADERINTERFACE_BUCKETS];
GPUShaderInput *builtin_uniforms[GPU_NUM_UNIFORMS];
/** Buffer containing all inputs names separated by '\0'. */
char *name_buffer;
struct GPUBatch **batches; /* references to batches using this interface */
/** Reference to GPUBatches using this interface */
struct GPUBatch **batches;
uint batches_len;
/** All enabled attributes in this shader. Used to set default values for unbound attributes. */
/** Input counts. */
uint attribute_len;
uint ubo_len;
uint uniform_len;
/** Enabled bindpoints that needs to be fed with data. */
uint16_t enabled_attr_mask;
uint16_t enabled_ubo_mask;
uint64_t enabled_tex_mask;
/** Opengl Location of builtin uniforms. Fast access, no lookup needed. */
/* TODO replace by location only array. */
GPUShaderInput builtins[GPU_NUM_UNIFORMS];
/** Flat array. In this order: Attributes, Ubos, Uniforms. */
GPUShaderInput inputs[0];
} GPUShaderInterface;
GPUShaderInterface *GPU_shaderinterface_create(int32_t program_id);

View File

@ -101,12 +101,11 @@ typedef struct GPUVertFormat {
char names[GPU_VERT_ATTR_NAMES_BUF_LEN];
} GPUVertFormat;
struct GPUShaderInterface;
struct GPUShader;
void GPU_vertformat_clear(GPUVertFormat *);
void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src);
void GPU_vertformat_from_interface(GPUVertFormat *format,
const struct GPUShaderInterface *shaderface);
void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *shader);
uint GPU_vertformat_attr_add(
GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode);

View File

@ -375,7 +375,7 @@ void GPU_batch_program_set_no_use(GPUBatch *batch,
const GPUShaderInterface *shaderface)
{
#if TRUST_NO_ONE
assert(glIsProgram(shaderface->program));
assert(glIsProgram(program));
assert(batch->program_in_use == 0);
#endif
batch->interface = shaderface;

View File

@ -24,6 +24,9 @@
*/
#include "BKE_global.h"
#include "BLI_math_base.h"
#include "MEM_guardedalloc.h"
#include "GPU_shader_interface.h"
@ -91,131 +94,132 @@ GPU_INLINE uint hash_string(const char *str)
return i;
}
GPU_INLINE void set_input_name(GPUShaderInterface *shaderface,
GPUShaderInput *input,
const char *name,
uint32_t name_len)
GPU_INLINE uint32_t set_input_name(GPUShaderInterface *shaderface,
GPUShaderInput *input,
char *name,
uint32_t name_len)
{
input->name_offset = shaderface->name_buffer_offset;
/* remove "[0]" from array name */
if (name[name_len - 1] == ']') {
name[name_len - 3] = '\0';
name_len -= 3;
}
input->name_offset = (uint32_t)(name - shaderface->name_buffer);
input->name_hash = hash_string(name);
shaderface->name_buffer_offset += name_len + 1; /* include NULL terminator */
return name_len + 1; /* include NULL terminator */
}
GPU_INLINE void shader_input_to_bucket(GPUShaderInput *input,
GPUShaderInput *buckets[GPU_NUM_SHADERINTERFACE_BUCKETS])
{
const uint bucket_index = input->name_hash % GPU_NUM_SHADERINTERFACE_BUCKETS;
input->next = buckets[bucket_index];
buckets[bucket_index] = input;
}
GPU_INLINE const GPUShaderInput *buckets_lookup(
GPUShaderInput *const buckets[GPU_NUM_SHADERINTERFACE_BUCKETS],
const char *name_buffer,
const char *name)
GPU_INLINE const GPUShaderInput *input_lookup(const GPUShaderInterface *shaderface,
const GPUShaderInput *const inputs,
const uint inputs_len,
const char *name)
{
const uint name_hash = hash_string(name);
const uint bucket_index = name_hash % GPU_NUM_SHADERINTERFACE_BUCKETS;
const GPUShaderInput *input = buckets[bucket_index];
if (input == 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 (input->next == NULL) {
if (name_hash == input->name_hash) {
#if TRUST_NO_ONE
assert(match(name_buffer + input->name_offset, name));
#endif
return input;
}
return NULL;
}
/* Work through possible collisions. */
const GPUShaderInput *next = input;
while (next != NULL) {
input = next;
next = input->next;
if (input->name_hash != name_hash) {
continue;
}
if (match(name_buffer + input->name_offset, name)) {
return input;
/* Simple linear search for now. */
for (int i = inputs_len - 1; i >= 0; i--) {
if (inputs[i].name_hash == name_hash) {
if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) {
/* Hash colision resolve. */
for (; i >= 0 && inputs[i].name_hash == name_hash; i--) {
if (match(name, shaderface->name_buffer + inputs[i].name_offset)) {
return inputs + i; /* not found */
}
}
return NULL; /* not found */
}
else {
/* This is a bit dangerous since we could have a hash collision.
* where the asked uniform that does not exist has the same hash
* as a real uniform. */
BLI_assert(match(name, shaderface->name_buffer + inputs[i].name_offset));
return inputs + i;
}
}
}
return NULL; /* not found */
}
GPU_INLINE void buckets_free(GPUShaderInput *buckets[GPU_NUM_SHADERINTERFACE_BUCKETS])
/* Note that this modify the src array. */
GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const uint input_len)
{
for (uint bucket_index = 0; bucket_index < GPU_NUM_SHADERINTERFACE_BUCKETS; bucket_index++) {
GPUShaderInput *input = buckets[bucket_index];
while (input != NULL) {
GPUShaderInput *input_next = input->next;
MEM_freeN(input);
input = input_next;
for (uint i = 0; i < input_len; i++) {
GPUShaderInput *input_src = &src[0];
for (uint j = 1; j < input_len; j++) {
if (src[j].name_hash > input_src->name_hash) {
input_src = &src[j];
}
}
dst[i] = *input_src;
input_src->name_hash = 0;
}
}
static bool setup_builtin_uniform(GPUShaderInput *input, const char *name)
static int block_binding(int32_t program, uint32_t block_index)
{
/* TODO: reject DOUBLE, IMAGE, ATOMIC_COUNTER gl_types */
/* detect built-in uniforms (name must match) */
for (GPUUniformBuiltin u = GPU_UNIFORM_NONE + 1; u < GPU_UNIFORM_CUSTOM; u++) {
const char *builtin_name = BuiltinUniform_name(u);
if (match(name, builtin_name)) {
input->builtin_type = u;
return true;
}
}
input->builtin_type = GPU_UNIFORM_CUSTOM;
return false;
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
glUniformBlockBinding(program, block_index, block_index);
return block_index;
}
static const GPUShaderInput *add_uniform(GPUShaderInterface *shaderface, const char *name)
static int sampler_binding(int32_t program,
uint32_t uniform_index,
int32_t uniform_location,
int *sampler_len)
{
GPUShaderInput *input = MEM_mallocN(sizeof(GPUShaderInput), "GPUShaderInput Unif");
/* Identify sampler uniforms and asign sampler units to them. */
GLint type;
glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
input->location = glGetUniformLocation(shaderface->program, name);
const uint name_len = strlen(name);
/* Include NULL terminator. */
shaderface->name_buffer = MEM_reallocN(shaderface->name_buffer,
shaderface->name_buffer_offset + name_len + 1);
char *name_buffer = shaderface->name_buffer + shaderface->name_buffer_offset;
strcpy(name_buffer, name);
set_input_name(shaderface, input, name, name_len);
setup_builtin_uniform(input, name);
shader_input_to_bucket(input, shaderface->uniform_buckets);
if (input->builtin_type != GPU_UNIFORM_NONE && input->builtin_type != GPU_UNIFORM_CUSTOM) {
shaderface->builtin_uniforms[input->builtin_type] = input;
switch (type) {
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_1D:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_BUFFER: {
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
int binding = *sampler_len;
glUniform1i(uniform_location, binding);
(*sampler_len)++;
return binding;
}
default:
return -1;
}
#if DEBUG_SHADER_INTERFACE
printf("GPUShaderInterface %p, program %d, uniform[] '%s' at location %d\n",
shaderface,
shaderface->program,
name,
input->location);
#endif
return input;
}
GPUShaderInterface *GPU_shaderinterface_create(int32_t program)
{
GPUShaderInterface *shaderface = MEM_callocN(sizeof(GPUShaderInterface), "GPUShaderInterface");
shaderface->program = program;
#if DEBUG_SHADER_INTERFACE
printf("%s {\n", __func__); /* enter function */
printf("GPUShaderInterface %p, program %d\n", shaderface, program);
#endif
GLint max_attr_name_len = 0, attr_len = 0;
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len);
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len);
@ -224,6 +228,11 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program)
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len);
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len);
GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
uniform_len = active_uniform_len;
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
if (attr_len > 0 && max_attr_name_len == 0) {
@ -232,87 +241,185 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program)
if (ubo_len > 0 && max_ubo_name_len == 0) {
max_ubo_name_len = 256;
}
if (uniform_len > 0 && max_uniform_name_len == 0) {
max_uniform_name_len = 256;
}
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len;
/* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
* allocating the uniform array. */
GLint *uniforms_block_index = MEM_mallocN(sizeof(GLint) * active_uniform_len, __func__);
if (uniform_len > 0) {
GLuint *indices = MEM_mallocN(sizeof(GLuint) * active_uniform_len, __func__);
for (uint i = 0; i < uniform_len; i++) {
indices[i] = i;
}
glGetActiveUniformsiv(
program, uniform_len, indices, GL_UNIFORM_BLOCK_INDEX, uniforms_block_index);
MEM_freeN(indices);
for (int i = 0; i < active_uniform_len; i++) {
/* If GL_UNIFORM_BLOCK_INDEX is not -1 it means the uniform belongs to a UBO. */
if (uniforms_block_index[i] != -1) {
uniform_len--;
}
}
}
uint32_t name_buffer_offset = 0;
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
uniform_len * max_uniform_name_len;
int input_tot_len = attr_len + ubo_len + uniform_len;
size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len;
GPUShaderInterface *shaderface = MEM_callocN(interface_size, "GPUShaderInterface");
shaderface->attribute_len = attr_len;
shaderface->ubo_len = ubo_len;
shaderface->uniform_len = uniform_len;
shaderface->name_buffer = MEM_mallocN(name_buffer_len, "name_buffer");
GPUShaderInput *inputs = shaderface->inputs;
/* Temp buffer. */
int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len);
GPUShaderInput *inputs_tmp = MEM_mallocN(sizeof(GPUShaderInput) * input_tmp_len, "name_buffer");
/* Attributes */
shaderface->enabled_attr_mask = 0;
for (uint32_t i = 0; i < attr_len; i++) {
GPUShaderInput *input = MEM_mallocN(sizeof(GPUShaderInput), "GPUShaderInput Attr");
GLsizei remaining_buffer = name_buffer_len - shaderface->name_buffer_offset;
char *name = shaderface->name_buffer + shaderface->name_buffer_offset;
for (int i = 0, idx = 0; i < attr_len; i++) {
char *name = shaderface->name_buffer + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
GLenum type;
GLint size;
glGetActiveAttrib(
program, i, remaining_buffer, &name_len, &input->size, &input->gl_type, name);
/* remove "[0]" from array name */
if (name[name_len - 1] == ']') {
name[name_len - 3] = '\0';
name_len -= 3;
}
/* TODO: reject DOUBLE gl_types */
input->location = glGetAttribLocation(program, name);
glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name);
GLint location = glGetAttribLocation(program, name);
/* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
if (input->location == -1) {
MEM_freeN(input);
if (location == -1) {
shaderface->attribute_len--;
continue;
}
if (input->location != -1) {
shaderface->enabled_attr_mask |= (1 << input->location);
}
GPUShaderInput *input = &inputs_tmp[idx++];
input->location = input->binding = location;
set_input_name(shaderface, input, name, name_len);
shader_input_to_bucket(input, shaderface->attr_buckets);
#if DEBUG_SHADER_INTERFACE
printf("attr[%u] '%s' at location %d\n", i, name, input->location);
#endif
name_buffer_offset += set_input_name(shaderface, input, name, name_len);
shaderface->enabled_attr_mask |= (1 << input->location);
}
sort_input_list(inputs, inputs_tmp, shaderface->attribute_len);
inputs += shaderface->attribute_len;
/* Uniform Blocks */
for (uint32_t i = 0; i < ubo_len; i++) {
GPUShaderInput *input = MEM_mallocN(sizeof(GPUShaderInput), "GPUShaderInput UBO");
GLsizei remaining_buffer = name_buffer_len - shaderface->name_buffer_offset;
char *name = shaderface->name_buffer + shaderface->name_buffer_offset;
for (int i = 0, idx = 0; i < ubo_len; i++) {
char *name = shaderface->name_buffer + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name);
input->location = i;
GPUShaderInput *input = &inputs_tmp[idx++];
input->binding = input->location = block_binding(program, i);
set_input_name(shaderface, input, name, name_len);
shader_input_to_bucket(input, shaderface->ubo_buckets);
#if DEBUG_SHADER_INTERFACE
printf("ubo '%s' at location %d\n", name, input->location);
#endif
name_buffer_offset += set_input_name(shaderface, input, name, name_len);
shaderface->enabled_ubo_mask |= (1 << input->binding);
}
sort_input_list(inputs, inputs_tmp, shaderface->ubo_len);
inputs += shaderface->ubo_len;
/* Uniforms */
for (int i = 0, idx = 0, sampler = 0; i < active_uniform_len; i++) {
/* If GL_UNIFORM_BLOCK_INDEX is not -1 it means the uniform belongs to a UBO. */
if (uniforms_block_index[i] != -1) {
continue;
}
char *name = shaderface->name_buffer + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
glGetActiveUniformName(program, i, remaining_buffer, &name_len, name);
GPUShaderInput *input = &inputs_tmp[idx++];
input->location = glGetUniformLocation(program, name);
input->binding = sampler_binding(program, i, input->location, &sampler);
name_buffer_offset += set_input_name(shaderface, input, name, name_len);
shaderface->enabled_tex_mask |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
}
sort_input_list(inputs, inputs_tmp, shaderface->uniform_len);
/* Builtin Uniforms */
for (GPUUniformBuiltin u = GPU_UNIFORM_NONE + 1; u < GPU_UNIFORM_CUSTOM; u++) {
const char *builtin_name = BuiltinUniform_name(u);
if (glGetUniformLocation(program, builtin_name) != -1) {
add_uniform((GPUShaderInterface *)shaderface, builtin_name);
}
shaderface->builtins[u].location = glGetUniformLocation(program, BuiltinUniform_name(u));
shaderface->builtins[u].binding = -1;
}
/* Batches ref buffer */
shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT;
shaderface->batches = MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *),
"GPUShaderInterface batches");
MEM_freeN(uniforms_block_index);
MEM_freeN(inputs_tmp);
/* Resize name buffer to save some memory. */
if (name_buffer_offset < name_buffer_len) {
shaderface->name_buffer = MEM_reallocN(shaderface->name_buffer, name_buffer_offset);
}
#if DEBUG_SHADER_INTERFACE
char *name_buf = shaderface->name_buffer;
printf("--- GPUShaderInterface %p, program %d ---\n", shaderface, program);
if (shaderface->attribute_len > 0) {
printf("Attributes {\n");
for (int i = 0; i < shaderface->attribute_len; i++) {
GPUShaderInput *input = shaderface->inputs + i;
printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset);
}
printf("};\n");
}
if (shaderface->ubo_len > 0) {
printf("Uniform Buffer Objects {\n");
for (int i = 0; i < shaderface->ubo_len; i++) {
GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + i;
printf("\t(binding = %d) %s;\n", input->binding, name_buf + input->name_offset);
}
printf("};\n");
}
if (shaderface->enabled_tex_mask > 0) {
printf("Samplers {\n");
for (int i = 0; i < shaderface->ubo_len; i++) {
GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len +
shaderface->ubo_len + i;
if (input->binding != -1) {
printf("\t(location = %d, binding = %d) %s;\n",
input->location,
input->binding,
name_buf + input->name_offset);
}
}
printf("};\n");
}
if (shaderface->uniform_len > 0) {
printf("Uniforms {\n");
for (int i = 0; i < shaderface->uniform_len; i++) {
GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len +
shaderface->ubo_len + i;
if (input->binding == -1) {
printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset);
}
}
printf("};\n");
}
printf("--- GPUShaderInterface end ---\n\n");
#endif
return shaderface;
}
void GPU_shaderinterface_discard(GPUShaderInterface *shaderface)
{
/* Free memory used by buckets and has entries. */
buckets_free(shaderface->uniform_buckets);
buckets_free(shaderface->attr_buckets);
buckets_free(shaderface->ubo_buckets);
/* Free memory used by name_buffer. */
MEM_freeN(shaderface->name_buffer);
/* Remove this interface from all linked Batches vao cache. */
@ -326,10 +433,25 @@ void GPU_shaderinterface_discard(GPUShaderInterface *shaderface)
MEM_freeN(shaderface);
}
const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface,
const char *name)
{
uint ofs = 0;
return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->attribute_len, name);
}
const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface,
const char *name)
{
uint ofs = shaderface->attribute_len;
return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->ubo_len, name);
}
const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *shaderface,
const char *name)
{
return buckets_lookup(shaderface->uniform_buckets, shaderface->name_buffer, name);
uint ofs = shaderface->attribute_len + shaderface->ubo_len;
return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->uniform_len, name);
}
const GPUShaderInput *GPU_shaderinterface_uniform_ensure(const GPUShaderInterface *shaderface,
@ -338,24 +460,11 @@ const GPUShaderInput *GPU_shaderinterface_uniform_ensure(const GPUShaderInterfac
const GPUShaderInput *input = GPU_shaderinterface_uniform(shaderface, name);
/* If input is not found add it so it's found next time. */
if (input == NULL) {
input = add_uniform((GPUShaderInterface *)shaderface, name);
if ((G.debug & G_DEBUG_GPU) && (input->location == -1)) {
fprintf(stderr, "GPUShaderInterface: Warning: Uniform '%s' not found!\n", name);
}
}
#if DEBUG_SHADER_UNIFORMS
if ((G.debug & G_DEBUG_GPU) && input->builtin_type != GPU_UNIFORM_NONE &&
input->builtin_type != GPU_UNIFORM_CUSTOM) {
/* Warn if we find a matching builtin, since these can be looked up much quicker. */
fprintf(stderr,
"GPUShaderInterface: Warning: Uniform '%s' is a builtin uniform but not queried as "
"such!\n",
name);
}
#endif
return (input->location != -1) ? input : NULL;
return input;
}
const GPUShaderInput *GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface,
@ -366,19 +475,7 @@ const GPUShaderInput *GPU_shaderinterface_uniform_builtin(const GPUShaderInterfa
assert(builtin != GPU_UNIFORM_CUSTOM);
assert(builtin != GPU_NUM_UNIFORMS);
#endif
return shaderface->builtin_uniforms[builtin];
}
const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface,
const char *name)
{
return buckets_lookup(shaderface->ubo_buckets, shaderface->name_buffer, name);
}
const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface,
const char *name)
{
return buckets_lookup(shaderface->attr_buckets, shaderface->name_buffer, name);
return &shaderface->builtins[builtin];
}
void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, GPUBatch *batch)

View File

@ -23,8 +23,6 @@
* GPU vertex format
*/
#include "GPU_shader_interface.h"
#include "GPU_vertex_format.h"
#include "gpu_vertex_format_private.h"
#include <stddef.h>
@ -34,6 +32,9 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "GPU_shader.h"
#include "gpu_shader_private.h"
#define PACK_DEBUG 0
#if PACK_DEBUG
@ -391,38 +392,37 @@ void VertexFormat_pack(GPUVertFormat *format)
format->packed = true;
}
static uint calc_input_component_size(const GPUShaderInput *input)
static uint calc_component_size(const GLenum gl_type)
{
int size = input->size;
switch (input->gl_type) {
switch (gl_type) {
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
case GL_UNSIGNED_INT_VEC2:
return size * 2;
return 2;
case GL_FLOAT_VEC3:
case GL_INT_VEC3:
case GL_UNSIGNED_INT_VEC3:
return size * 3;
return 3;
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_INT_VEC4:
case GL_UNSIGNED_INT_VEC4:
return size * 4;
return 4;
case GL_FLOAT_MAT3:
return size * 9;
return 9;
case GL_FLOAT_MAT4:
return size * 16;
return 16;
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT3x2:
return size * 6;
return 6;
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT4x2:
return size * 8;
return 8;
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x3:
return size * 12;
return 12;
default:
return size;
return 1;
}
}
@ -466,42 +466,39 @@ static void get_fetch_mode_and_comp_type(int gl_type,
}
}
void GPU_vertformat_from_interface(GPUVertFormat *format, const GPUShaderInterface *shaderface)
void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
{
const char *name_buffer = shaderface->name_buffer;
GPU_vertformat_clear(format);
GPUVertAttr *attr = &format->attrs[0];
for (int i = 0; i < GPU_NUM_SHADERINTERFACE_BUCKETS; i++) {
const GPUShaderInput *input = shaderface->attr_buckets[i];
if (input == NULL) {
GLint attr_len;
glGetProgramiv(shader->program, GL_ACTIVE_ATTRIBUTES, &attr_len);
for (int i = 0; i < attr_len; i++) {
char name[256];
GLenum gl_type;
GLint size;
glGetActiveAttrib(shader->program, i, sizeof(name), NULL, &size, &gl_type, name);
/* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
if (glGetAttribLocation(shader->program, name) == -1) {
continue;
}
const GPUShaderInput *next = input;
while (next != NULL) {
input = next;
next = input->next;
format->name_len++; /* multiname support */
format->attr_len++;
/* OpenGL attributes such as `gl_VertexID` have a location of -1. */
if (input->location < 0) {
continue;
}
GPUVertCompType comp_type;
GPUVertFetchMode fetch_mode;
get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode);
format->name_len++; /* multiname support */
format->attr_len++;
GPUVertCompType comp_type;
GPUVertFetchMode fetch_mode;
get_fetch_mode_and_comp_type(input->gl_type, &comp_type, &fetch_mode);
GPUVertAttr *attr = &format->attrs[input->location];
attr->names[attr->name_len++] = copy_attr_name(format, name_buffer + input->name_offset);
attr->offset = 0; /* offsets & stride are calculated later (during pack) */
attr->comp_len = calc_input_component_size(input);
attr->sz = attr->comp_len * 4;
attr->fetch_mode = fetch_mode;
attr->comp_type = comp_type;
attr->gl_comp_type = convert_comp_type_to_gl(comp_type);
}
attr->names[attr->name_len++] = copy_attr_name(format, name);
attr->offset = 0; /* offsets & stride are calculated later (during pack) */
attr->comp_len = calc_component_size(gl_type) * size;
attr->sz = attr->comp_len * 4;
attr->fetch_mode = fetch_mode;
attr->comp_type = comp_type;
attr->gl_comp_type = convert_comp_type_to_gl(comp_type);
attr += 1;
}
}

View File

@ -524,7 +524,7 @@ PyDoc_STRVAR(bpygpu_shader_calc_format_doc,
static PyObject *bpygpu_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg))
{
BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL);
GPU_vertformat_from_interface(&ret->fmt, GPU_shader_get_interface(self->shader));
GPU_vertformat_from_shader(&ret->fmt, self->shader);
return (PyObject *)ret;
}