PyGPU: GPUShader: implementation of 'attrs_info_get' method

With the new `attrs_info_get` method, we can get information about
the attributes used in a `GPUShader` and thus have more freedom in the
automatic creation of `GPUVertFormat`s

Reviewed By: fclem, campbellbarton

Differential Revision: https://developer.blender.org/D15764
This commit is contained in:
Germano Cavalcante 2022-09-01 08:21:10 -03:00 committed by Germano Cavalcante
parent 05fe7ca5af
commit 6269d66da2
12 changed files with 254 additions and 110 deletions

View File

@ -22,15 +22,46 @@ def batch_for_shader(shader, type, content, *, indices=None):
GPUBatch,
GPUIndexBuf,
GPUVertBuf,
GPUVertFormat,
)
def recommended_comp_type(attr_type):
if attr_type in {'FLOAT', 'VEC2', 'VEC3', 'VEC4', 'MAT3', 'MAT4'}:
return 'F32'
if attr_type in {'UINT', 'UVEC2', 'UVEC3', 'UVEC4'}:
return 'U32'
# `attr_type` in {'INT', 'IVEC2', 'IVEC3', 'IVEC4', 'BOOL'}.
return 'I32'
def recommended_attr_len(attr_name):
item = content[attr_name][0]
attr_len = 1
try:
while True:
attr_len *= len(item)
item = item[0]
except TypeError:
pass
return attr_len
def recommended_fetch_mode(comp_type):
if comp_type == 'F32':
return 'FLOAT'
return 'INT'
for data in content.values():
vbo_len = len(data)
break
else:
raise ValueError("Empty 'content'")
vbo_format = shader.format_calc()
vbo_format = GPUVertFormat()
attrs_info = shader.attrs_info_get()
for name, attr_type in attrs_info:
comp_type = recommended_comp_type(attr_type)
attr_len = recommended_attr_len(name)
vbo_format.attr_add(id=name, comp_type=comp_type, len=attr_len, fetch_mode=recommended_fetch_mode(comp_type))
vbo = GPUVertBuf(vbo_format, vbo_len)
for id, data in content.items():

View File

@ -191,7 +191,12 @@ void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const floa
void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]);
void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]);
unsigned int GPU_shader_get_attribute_len(const GPUShader *shader);
int GPU_shader_get_attribute(GPUShader *shader, const char *name);
bool GPU_shader_get_attribute_info(const GPUShader *shader,
int attr_location,
char r_name[256],
int *r_type);
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear);

View File

@ -612,6 +612,12 @@ int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
return tex ? tex->binding : -1;
}
uint GPU_shader_get_attribute_len(const GPUShader *shader)
{
ShaderInterface *interface = unwrap(shader)->interface;
return interface->attr_len_;
}
int GPU_shader_get_attribute(GPUShader *shader, const char *name)
{
ShaderInterface *interface = unwrap(shader)->interface;
@ -619,6 +625,23 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name)
return attr ? attr->location : -1;
}
bool GPU_shader_get_attribute_info(const GPUShader *shader,
int attr_location,
char r_name[256],
int *r_type)
{
ShaderInterface *interface = unwrap(shader)->interface;
const ShaderInput *attr = interface->attr_get(attr_location);
if (!attr) {
return false;
}
BLI_strncpy(r_name, interface->input_name_get(attr), 256);
*r_type = attr->location != -1 ? interface->attr_types_[attr->location] : -1;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -18,6 +18,7 @@
#include "BLI_utildefines.h"
#include "GPU_shader.h"
#include "GPU_vertex_format.h" /* GPU_VERT_ATTR_MAX_LEN */
#include "gpu_shader_create_info.hh"
namespace blender::gpu {
@ -58,6 +59,13 @@ class ShaderInterface {
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
int32_t builtin_buffers_[GPU_NUM_STORAGE_BUFFERS];
/**
* Currently only used for `GPU_shader_get_attribute_info`.
* This utility is useful for automatic creation of `GPUVertFormat` in Python.
* Use `ShaderInput::location` to identify the `Type`.
*/
uint8_t attr_types_[GPU_VERT_ATTR_MAX_LEN];
public:
ShaderInterface();
ShaderInterface(const shader::ShaderCreateInfo &info);
@ -69,6 +77,10 @@ class ShaderInterface {
{
return input_lookup(inputs_, attr_len_, name);
}
inline const ShaderInput *attr_get(const int binding) const
{
return input_lookup(inputs_, attr_len_, binding);
}
inline const ShaderInput *ubo_get(const char *name) const
{

View File

@ -55,8 +55,6 @@ class Shader {
virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
virtual void vertformat_from_shader(GPUVertFormat *) const = 0;
std::string defines_declare(const shader::ShaderCreateInfo &info) const;
virtual std::string resources_declare(const shader::ShaderCreateInfo &info) const = 0;
virtual std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const = 0;

View File

@ -8,6 +8,7 @@
*/
#include "GPU_vertex_format.h"
#include "gpu_shader_create_info.hh"
#include "gpu_shader_private.hh"
#include "gpu_vertex_format_private.h"
@ -25,6 +26,7 @@
#endif
using namespace blender::gpu;
using namespace blender::gpu::shader;
void GPU_vertformat_clear(GPUVertFormat *format)
{
@ -338,8 +340,83 @@ void VertexFormat_pack(GPUVertFormat *format)
format->packed = true;
}
static uint component_size_get(const Type gpu_type)
{
switch (gpu_type) {
case Type::VEC2:
case Type::IVEC2:
case Type::UVEC2:
return 2;
case Type::VEC3:
case Type::IVEC3:
case Type::UVEC3:
return 3;
case Type::VEC4:
case Type::IVEC4:
case Type::UVEC4:
return 4;
case Type::MAT3:
return 12;
case Type::MAT4:
return 16;
default:
return 1;
}
}
static void recommended_fetch_mode_and_comp_type(Type gpu_type,
GPUVertCompType *r_comp_type,
GPUVertFetchMode *r_fetch_mode)
{
switch (gpu_type) {
case Type::FLOAT:
case Type::VEC2:
case Type::VEC3:
case Type::VEC4:
case Type::MAT3:
case Type::MAT4:
*r_comp_type = GPU_COMP_F32;
*r_fetch_mode = GPU_FETCH_FLOAT;
break;
case Type::INT:
case Type::IVEC2:
case Type::IVEC3:
case Type::IVEC4:
*r_comp_type = GPU_COMP_I32;
*r_fetch_mode = GPU_FETCH_INT;
break;
case Type::UINT:
case Type::UVEC2:
case Type::UVEC3:
case Type::UVEC4:
*r_comp_type = GPU_COMP_U32;
*r_fetch_mode = GPU_FETCH_INT;
break;
default:
BLI_assert(0);
}
}
void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader)
{
const Shader *shader = reinterpret_cast<const Shader *>(gpushader);
shader->vertformat_from_shader(format);
GPU_vertformat_clear(format);
uint attr_len = GPU_shader_get_attribute_len(gpushader);
int location_test = 0, attrs_added = 0;;
while (attrs_added < attr_len) {
char name[256];
Type gpu_type;
if (!GPU_shader_get_attribute_info(gpushader, location_test++, name, (int *)&gpu_type)) {
continue;
}
GPUVertCompType comp_type;
GPUVertFetchMode fetch_mode;
recommended_fetch_mode_and_comp_type(gpu_type, &comp_type, &fetch_mode);
int comp_len = component_size_get(gpu_type);
GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
attrs_added++;
}
}

View File

@ -1136,108 +1136,6 @@ void GLShader::uniform_int(int location, int comp_len, int array_size, const int
/** \name GPUVertFormat from Shader
* \{ */
static uint calc_component_size(const GLenum gl_type)
{
switch (gl_type) {
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
case GL_UNSIGNED_INT_VEC2:
return 2;
case GL_FLOAT_VEC3:
case GL_INT_VEC3:
case GL_UNSIGNED_INT_VEC3:
return 3;
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_INT_VEC4:
case GL_UNSIGNED_INT_VEC4:
return 4;
case GL_FLOAT_MAT3:
return 9;
case GL_FLOAT_MAT4:
return 16;
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT3x2:
return 6;
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT4x2:
return 8;
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x3:
return 12;
default:
return 1;
}
}
static void get_fetch_mode_and_comp_type(int gl_type,
GPUVertCompType *r_comp_type,
GPUVertFetchMode *r_fetch_mode)
{
switch (gl_type) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
*r_comp_type = GPU_COMP_F32;
*r_fetch_mode = GPU_FETCH_FLOAT;
break;
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
*r_comp_type = GPU_COMP_I32;
*r_fetch_mode = GPU_FETCH_INT;
break;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
case GL_UNSIGNED_INT_VEC3:
case GL_UNSIGNED_INT_VEC4:
*r_comp_type = GPU_COMP_U32;
*r_fetch_mode = GPU_FETCH_INT;
break;
default:
BLI_assert(0);
}
}
void GLShader::vertformat_from_shader(GPUVertFormat *format) const
{
GPU_vertformat_clear(format);
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), nullptr, &size, &gl_type, name);
/* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
if (glGetAttribLocation(shader_program_, name) == -1) {
continue;
}
GPUVertCompType comp_type;
GPUVertFetchMode fetch_mode;
get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode);
int comp_len = calc_component_size(gl_type) * size;
GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
}
}
int GLShader::program_handle_get() const
{
return (int)this->shader_program_;

View File

@ -67,8 +67,6 @@ class GLShader : public Shader {
void uniform_float(int location, int comp_len, int array_size, const float *data) override;
void uniform_int(int location, int comp_len, int array_size, const int *data) override;
void vertformat_from_shader(GPUVertFormat *format) const override;
/** DEPRECATED: Kept only because of BGL API. */
int program_handle_get() const override;

View File

@ -16,6 +16,7 @@
#include "GPU_capabilities.h"
using namespace blender::gpu::shader;
namespace blender::gpu {
/* -------------------------------------------------------------------- */
@ -151,6 +152,52 @@ static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
/** \name Creation / Destruction
* \{ */
static Type gpu_type_from_gl_type(int gl_type)
{
switch (gl_type) {
case GL_FLOAT:
return Type::FLOAT;
case GL_FLOAT_VEC2:
return Type::VEC2;
case GL_FLOAT_VEC3:
return Type::VEC3;
case GL_FLOAT_VEC4:
return Type::VEC4;
case GL_FLOAT_MAT3:
return Type::MAT3;
case GL_FLOAT_MAT4:
return Type::MAT4;
case GL_UNSIGNED_INT:
return Type::UINT;
case GL_UNSIGNED_INT_VEC2:
return Type::UVEC2;
case GL_UNSIGNED_INT_VEC3:
return Type::UVEC3;
case GL_UNSIGNED_INT_VEC4:
return Type::UVEC4;
case GL_INT:
return Type::INT;
case GL_INT_VEC2:
return Type::IVEC2;
case GL_INT_VEC3:
return Type::IVEC3;
case GL_INT_VEC4:
return Type::IVEC4;
case GL_BOOL:
return Type::BOOL;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT2x3:
case GL_FLOAT_MAT2x4:
case GL_FLOAT_MAT3x2:
case GL_FLOAT_MAT3x4:
case GL_FLOAT_MAT4x2:
case GL_FLOAT_MAT4x3:
default:
BLI_assert(0);
}
return Type::FLOAT;
}
GLShaderInterface::GLShaderInterface(GLuint program)
{
/* Necessary to make #glUniform works. */
@ -246,6 +293,9 @@ GLShaderInterface::GLShaderInterface(GLuint program)
name_buffer_offset += set_input_name(input, name, name_len);
enabled_attr_mask_ |= (1 << input->location);
/* Used in `GPU_shader_get_attribute_info`. */
attr_types_[input->location] = (uint8_t)gpu_type_from_gl_type(type);
}
/* Uniform Blocks */
@ -405,7 +455,11 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
}
if (input->location != -1) {
enabled_attr_mask_ |= (1 << input->location);
/* Used in `GPU_shader_get_attribute_info`. */
attr_types_[input->location] = (uint8_t)attr.type;
}
input++;
}

View File

@ -16,6 +16,7 @@
#include "GPU_uniform_buffer.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
#include "../mathutils/mathutils.h"
#include "gpu_py.h"
@ -614,6 +615,44 @@ static PyObject *pygpu_shader_format_calc(BPyGPUShader *self, PyObject *UNUSED(a
return (PyObject *)ret;
}
PyDoc_STRVAR(
pygpu_shader_attrs_info_get_doc,
".. method:: attrs_info_get()\n"
"\n"
" Information about the attributes used in the Shader.\n"
"\n"
" :return: tuples containing information about the attributes in order (name, type)\n"
" :rtype: tuple\n");
static PyObject *pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject *UNUSED(arg))
{
uint attr_len = GPU_shader_get_attribute_len(self->shader);
int location_test = 0, attrs_added = 0;
;
PyObject *ret = PyTuple_New(attr_len);
while (attrs_added < attr_len) {
char name[256];
int type;
if (!GPU_shader_get_attribute_info(self->shader, location_test++, name, &type)) {
continue;
}
PyObject *py_type;
if (type != -1) {
py_type = PyUnicode_InternFromString(
PyC_StringEnum_FindIDFromValue(pygpu_attrtype_items, type));
}
else {
py_type = Py_None;
Py_INCREF(py_type);
}
PyObject *attr_info = PyTuple_New(2);
PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
PyTuple_SetItem(ret, attrs_added, attr_info);
attrs_added++;
}
return ret;
}
static struct PyMethodDef pygpu_shader__tp_methods[] = {
{"bind", (PyCFunction)pygpu_shader_bind, METH_NOARGS, pygpu_shader_bind_doc},
{"uniform_from_name",
@ -660,6 +699,10 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = {
(PyCFunction)pygpu_shader_format_calc,
METH_NOARGS,
pygpu_shader_format_calc_doc},
{"attrs_info_get",
(PyCFunction)pygpu_shader_attrs_info_get,
METH_NOARGS,
pygpu_shader_attrs_info_get_doc},
{NULL, NULL, 0, NULL},
};

View File

@ -6,6 +6,10 @@
#pragma once
#ifndef __cplusplus
#include "../generic/py_capi_utils.h"
#endif
/* Make sure that there is always a reference count for PyObjects of type String as the strings are
* passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */
#define USE_GPU_PY_REFERENCES
@ -31,6 +35,7 @@ extern "C" {
/* gpu_py_shader_create_info.cc */
extern const struct PyC_StringEnumItems pygpu_attrtype_items[];
extern PyTypeObject BPyGPUShaderCreateInfo_Type;
extern PyTypeObject BPyGPUStageInterfaceInfo_Type;

View File

@ -58,7 +58,7 @@ static const struct PyC_FlagSet pygpu_qualifiers[] = {
" - ``IVEC3``\n" \
" - ``IVEC4``\n" \
" - ``BOOL``\n"
static const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
{(int)Type::FLOAT, "FLOAT"},
{(int)Type::VEC2, "VEC2"},
{(int)Type::VEC3, "VEC3"},