Python API: new GPUVertFormat constructor and vbo.fill_attribute method

Reviewer: fclem

Differential Revision: https://developer.blender.org/D3760
This commit is contained in:
Jacques Lucke 2018-10-05 15:10:56 +02:00
parent 1aae42aa88
commit 564d37c4b6
4 changed files with 206 additions and 88 deletions

View File

@ -87,6 +87,7 @@ uint GPU_vertformat_attr_add(
GPUVertFormat *, const char *name,
GPUVertCompType, uint comp_len, GPUVertFetchMode);
void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias);
int GPU_vertformat_attr_id_get(const GPUVertFormat *, const char *name);
/* format conversion */

View File

@ -34,6 +34,8 @@
#include <stddef.h>
#include <string.h>
#include "BLI_utildefines.h"
#define PACK_DEBUG 0
#if PACK_DEBUG
@ -204,6 +206,19 @@ void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias)
attrib->name[attrib->name_len++] = copy_attrib_name(format, alias);
}
int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
{
for (int i = 0; i < format->attr_len; i++) {
const GPUVertAttr *attrib = format->attribs + i;
for (int j = 0; j < attrib->name_len; j++) {
if (STREQ(name, attrib->name[j])) {
return i;
}
}
}
return -1;
}
uint padding(uint offset, uint alignment)
{
const uint mod = offset % alignment;

View File

@ -188,20 +188,28 @@ finally:
return ok;
}
/* handy, but not used just now */
#if 0
static int bpygpu_find_id(const GPUVertFormat *fmt, const char *id)
static int bpygpu_fill_attribute(GPUVertBuf *buf, int id, PyObject *py_seq_data)
{
for (int i = 0; i < fmt->attr_len; i++) {
for (uint j = 0; j < fmt->name_len; j++) {
if (STREQ(fmt->attribs[i].name[j], id)) {
return i;
}
}
if (id < 0 || id >= buf->format.attr_len) {
PyErr_Format(PyExc_ValueError,
"Format id %d out of range",
id);
return 0;
}
return -1;
if (buf->data == NULL) {
PyErr_SetString(PyExc_ValueError,
"Can't fill, static buffer already in use");
return 0;
}
if (!bpygpu_vertbuf_fill_impl(buf, (uint)id, py_seq_data)) {
return 0;
}
return 1;
}
#endif
/** \} */
@ -218,12 +226,12 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
uint len;
} params;
static const char *_keywords[] = {"len", "format", NULL};
static _PyArg_Parser _parser = {"$IO!:GPUVertBuf.__new__", _keywords, 0};
static const char *_keywords[] = {"format", "len", NULL};
static _PyArg_Parser _parser = {"O!I:GPUVertBuf.__new__", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser,
&params.len,
&BPyGPUVertFormat_Type, &params.py_fmt))
&BPyGPUVertFormat_Type, &params.py_fmt,
&params.len))
{
return NULL;
}
@ -235,48 +243,62 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
return BPyGPUVertBuf_CreatePyObject(vbo);
}
PyDoc_STRVAR(bpygpu_VertBuf_fill_doc,
"TODO"
PyDoc_STRVAR(bpygpu_VertBuf_fill_attribute_doc,
"fill_attribute(identifier, data)\n"
"\n"
" Insert data into the buffer for a single attribute.\n"
"\n"
" :param identifier: Either the name or the id of the attribute.\n"
" :type identifier: int or str\n"
" :param data: Sequence of data that should be stored in the buffer\n"
" :type data: sequence of individual values or tuples\n"
);
static PyObject *bpygpu_VertBuf_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
static PyObject *bpygpu_VertBuf_fill_attribute(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
{
struct {
uint id;
PyObject *py_seq_data;
} params;
PyObject *data;
PyObject *identifier;
static const char *_keywords[] = {"id", "data", NULL};
static _PyArg_Parser _parser = {"$IO:fill", _keywords, 0};
static const char *_keywords[] = {"identifier", "data", NULL};
static _PyArg_Parser _parser = {"OO:fill_attribute", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser,
&params.id,
&params.py_seq_data))
&identifier, &data))
{
return NULL;
}
if (params.id >= self->buf->format.attr_len) {
PyErr_Format(PyExc_ValueError,
"Format id %d out of range",
params.id);
int id;
if (PyLong_Check(identifier)) {
id = PyLong_AsLong(identifier);
}
else if (PyUnicode_Check(identifier)) {
const char *name = PyUnicode_AsUTF8(identifier);
id = GPU_vertformat_attr_id_get(&self->buf->format, name);
if (id == -1) {
PyErr_SetString(PyExc_ValueError,
"Unknown attribute name");
return NULL;
}
}
else {
PyErr_SetString(PyExc_TypeError,
"expected int or str type as identifier");
return NULL;
}
if (self->buf->data == NULL) {
PyErr_SetString(PyExc_ValueError,
"Can't fill, static buffer already in use");
if (!bpygpu_fill_attribute(self->buf, id, data)) {
return NULL;
}
if (!bpygpu_vertbuf_fill_impl(self->buf, params.id, params.py_seq_data)) {
return NULL;
}
Py_RETURN_NONE;
}
static struct PyMethodDef bpygpu_VertBuf_methods[] = {
{"fill", (PyCFunction) bpygpu_VertBuf_fill,
METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_doc},
{"fill_attribute", (PyCFunction) bpygpu_VertBuf_fill_attribute,
METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_attribute_doc},
{NULL, NULL, 0, NULL}
};

View File

@ -55,79 +55,104 @@
* Use with PyArg_ParseTuple's "O&" formatting.
* \{ */
static int bpygpu_parse_component_type(const char *str, int length)
{
if (length == 2) {
switch (*((ushort *)str)) {
case MAKE_ID2('I', '8'): return GPU_COMP_I8;
case MAKE_ID2('U', '8'): return GPU_COMP_U8;
default: break;
}
}
else if (length == 3) {
switch (*((uint *)str)) {
case MAKE_ID3('I', '1', '6'): return GPU_COMP_I16;
case MAKE_ID3('U', '1', '6'): return GPU_COMP_U16;
case MAKE_ID3('I', '3', '2'): return GPU_COMP_I32;
case MAKE_ID3('U', '3', '2'): return GPU_COMP_U32;
case MAKE_ID3('F', '3', '2'): return GPU_COMP_F32;
case MAKE_ID3('I', '1', '0'): return GPU_COMP_I10;
default: break;
}
}
return -1;
}
static int bpygpu_parse_fetch_mode(const char *str, int length)
{
#define MATCH_ID(id) \
if (length == strlen(STRINGIFY(id))) { \
if (STREQ(str, STRINGIFY(id))) { \
return GPU_FETCH_##id; \
} \
} ((void)0)
MATCH_ID(FLOAT);
MATCH_ID(INT);
MATCH_ID(INT_TO_FLOAT_UNIT);
MATCH_ID(INT_TO_FLOAT);
#undef MATCH_ID
return -1;
}
static int bpygpu_ParseVertCompType(PyObject *o, void *p)
{
Py_ssize_t comp_type_id_len;
const char *comp_type_id = _PyUnicode_AsStringAndSize(o, &comp_type_id_len);
if (comp_type_id == NULL) {
Py_ssize_t length;
const char *str = _PyUnicode_AsStringAndSize(o, &length);
if (str == NULL) {
PyErr_Format(PyExc_ValueError,
"expected a string, got %s",
Py_TYPE(o)->tp_name);
return 0;
}
GPUVertCompType comp_type;
if (comp_type_id_len == 2) {
switch (*((ushort *)comp_type_id)) {
case MAKE_ID2('I', '8'): { comp_type = GPU_COMP_I8; goto success; }
case MAKE_ID2('U', '8'): { comp_type = GPU_COMP_U8; goto success; }
}
}
else if (comp_type_id_len == 3) {
switch (*((uint *)comp_type_id)) {
case MAKE_ID3('I', '1', '6'): { comp_type = GPU_COMP_I16; goto success; }
case MAKE_ID3('U', '1', '6'): { comp_type = GPU_COMP_U16; goto success; }
case MAKE_ID3('I', '3', '2'): { comp_type = GPU_COMP_I32; goto success; }
case MAKE_ID3('U', '3', '2'): { comp_type = GPU_COMP_U32; goto success; }
case MAKE_ID3('F', '3', '2'): { comp_type = GPU_COMP_F32; goto success; }
case MAKE_ID3('I', '1', '0'): { comp_type = GPU_COMP_I10; goto success; }
}
int comp_type = bpygpu_parse_component_type(str, length);
if (comp_type == -1) {
PyErr_Format(PyExc_ValueError,
"unkown component type: '%s",
str);
return 0;
}
PyErr_Format(PyExc_ValueError,
"unknown type literal: '%s'",
comp_type_id);
return 0;
success:
*((GPUVertCompType *)p) = comp_type;
return 1;
}
static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
{
Py_ssize_t mode_id_len;
const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
if (mode_id == NULL) {
Py_ssize_t length;
const char *str = _PyUnicode_AsStringAndSize(o, &length);
if (str == NULL) {
PyErr_Format(PyExc_ValueError,
"expected a string, got %s",
Py_TYPE(o)->tp_name);
return 0;
}
#define MATCH_ID(id) \
if (mode_id_len == strlen(STRINGIFY(id))) { \
if (STREQ(mode_id, STRINGIFY(id))) { \
mode = GPU_FETCH_##id; \
goto success; \
} \
} ((void)0)
GPUVertFetchMode mode;
MATCH_ID(FLOAT);
MATCH_ID(INT);
MATCH_ID(INT_TO_FLOAT_UNIT);
MATCH_ID(INT_TO_FLOAT);
#undef MATCH_ID
PyErr_Format(PyExc_ValueError,
"unknown type literal: '%s'",
mode_id);
return 0;
int fetch_mode = bpygpu_parse_fetch_mode(str, length);
if (fetch_mode == -1) {
PyErr_Format(PyExc_ValueError,
"unknown type literal: '%s'",
str);
return 0;
}
success:
(*(GPUVertFetchMode *)p) = mode;
(*(GPUVertFetchMode *)p) = fetch_mode;
return 1;
}
static int get_default_fetch_mode(GPUVertCompType type)
{
switch (type)
{
case GPU_COMP_F32: return GPU_FETCH_FLOAT;
default: return -1;
}
}
/** \} */
@ -136,16 +161,71 @@ success:
/** \name VertFormat Type
* \{ */
static int add_attribute_simple(GPUVertFormat *format, char *name, GPUVertCompType comp_type, int length)
{
if (length <= 0) {
PyErr_SetString(PyExc_ValueError,
"length of an attribute must greater than 0");
return 0;
}
int fetch_mode = get_default_fetch_mode(comp_type);
if (fetch_mode == -1) {
PyErr_SetString(PyExc_ValueError,
"no default fetch mode found");
return 0;
}
GPU_vertformat_attr_add(format, name, comp_type, length, fetch_mode);
return 1;
}
static int add_attribute_from_tuple(GPUVertFormat *format, PyObject *data)
{
char *name;
GPUVertCompType comp_type;
int length;
if (!PyArg_ParseTuple(data, "sO&i", &name, bpygpu_ParseVertCompType, &comp_type, &length)) {
return 0;
}
return add_attribute_simple(format, name, comp_type, length);
}
static int insert_attributes_from_list(GPUVertFormat *format, PyObject *list)
{
Py_ssize_t amount = PyList_Size(list);
for (Py_ssize_t i = 0; i < amount; i++) {
PyObject *element = PyList_GET_ITEM(list, i);
if (!PyTuple_Check(element)) {
PyErr_SetString(PyExc_TypeError, "expected a list of tuples");
return 0;
}
if (!add_attribute_from_tuple(format, element)) return 0;
}
return 1;
}
static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
PyErr_SetString(PyExc_TypeError,
"VertFormat(): takes no arguments");
PyObject *format_list;
static const char *keywords[] = {"format", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char**)keywords, &PyList_Type, &format_list)) {
return NULL;
}
BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL);
if (!insert_attributes_from_list(&ret->fmt, format_list)) {
Py_DecRef((PyObject *)ret);
return NULL;
}
return (PyObject *)ret;
}