Python API: new GPUVertFormat constructor and vbo.fill_attribute method
Reviewer: fclem Differential Revision: https://developer.blender.org/D3760
This commit is contained in:
@@ -87,6 +87,7 @@ uint GPU_vertformat_attr_add(
|
|||||||
GPUVertFormat *, const char *name,
|
GPUVertFormat *, const char *name,
|
||||||
GPUVertCompType, uint comp_len, GPUVertFetchMode);
|
GPUVertCompType, uint comp_len, GPUVertFetchMode);
|
||||||
void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias);
|
void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias);
|
||||||
|
int GPU_vertformat_attr_id_get(const GPUVertFormat *, const char *name);
|
||||||
|
|
||||||
/* format conversion */
|
/* format conversion */
|
||||||
|
|
||||||
|
@@ -34,6 +34,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
#define PACK_DEBUG 0
|
#define PACK_DEBUG 0
|
||||||
|
|
||||||
#if PACK_DEBUG
|
#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);
|
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)
|
uint padding(uint offset, uint alignment)
|
||||||
{
|
{
|
||||||
const uint mod = offset % alignment;
|
const uint mod = offset % alignment;
|
||||||
|
@@ -188,20 +188,28 @@ finally:
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handy, but not used just now */
|
static int bpygpu_fill_attribute(GPUVertBuf *buf, int id, PyObject *py_seq_data)
|
||||||
#if 0
|
|
||||||
static int bpygpu_find_id(const GPUVertFormat *fmt, const char *id)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < fmt->attr_len; i++) {
|
if (id < 0 || id >= buf->format.attr_len) {
|
||||||
for (uint j = 0; j < fmt->name_len; j++) {
|
PyErr_Format(PyExc_ValueError,
|
||||||
if (STREQ(fmt->attribs[i].name[j], id)) {
|
"Format id %d out of range",
|
||||||
return i;
|
id);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
@@ -218,12 +226,12 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
|
|||||||
uint len;
|
uint len;
|
||||||
} params;
|
} params;
|
||||||
|
|
||||||
static const char *_keywords[] = {"len", "format", NULL};
|
static const char *_keywords[] = {"format", "len", NULL};
|
||||||
static _PyArg_Parser _parser = {"$IO!:GPUVertBuf.__new__", _keywords, 0};
|
static _PyArg_Parser _parser = {"O!I:GPUVertBuf.__new__", _keywords, 0};
|
||||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||||
args, kwds, &_parser,
|
args, kwds, &_parser,
|
||||||
¶ms.len,
|
&BPyGPUVertFormat_Type, ¶ms.py_fmt,
|
||||||
&BPyGPUVertFormat_Type, ¶ms.py_fmt))
|
¶ms.len))
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -235,48 +243,62 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
|
|||||||
return BPyGPUVertBuf_CreatePyObject(vbo);
|
return BPyGPUVertBuf_CreatePyObject(vbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(bpygpu_VertBuf_fill_doc,
|
PyDoc_STRVAR(bpygpu_VertBuf_fill_attribute_doc,
|
||||||
"TODO"
|
"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 {
|
PyObject *data;
|
||||||
uint id;
|
PyObject *identifier;
|
||||||
PyObject *py_seq_data;
|
|
||||||
} params;
|
|
||||||
|
|
||||||
static const char *_keywords[] = {"id", "data", NULL};
|
static const char *_keywords[] = {"identifier", "data", NULL};
|
||||||
static _PyArg_Parser _parser = {"$IO:fill", _keywords, 0};
|
static _PyArg_Parser _parser = {"OO:fill_attribute", _keywords, 0};
|
||||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||||
args, kwds, &_parser,
|
args, kwds, &_parser,
|
||||||
¶ms.id,
|
&identifier, &data))
|
||||||
¶ms.py_seq_data))
|
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.id >= self->buf->format.attr_len) {
|
int id;
|
||||||
PyErr_Format(PyExc_ValueError,
|
|
||||||
"Format id %d out of range",
|
|
||||||
params.id);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->buf->data == NULL) {
|
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,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"Can't fill, static buffer already in use");
|
"Unknown attribute name");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"expected int or str type as identifier");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bpygpu_vertbuf_fill_impl(self->buf, params.id, params.py_seq_data)) {
|
|
||||||
|
if (!bpygpu_fill_attribute(self->buf, id, data)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct PyMethodDef bpygpu_VertBuf_methods[] = {
|
static struct PyMethodDef bpygpu_VertBuf_methods[] = {
|
||||||
{"fill", (PyCFunction) bpygpu_VertBuf_fill,
|
{"fill_attribute", (PyCFunction) bpygpu_VertBuf_fill_attribute,
|
||||||
METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_doc},
|
METH_VARARGS | METH_KEYWORDS, bpygpu_VertBuf_fill_attribute_doc},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -55,79 +55,104 @@
|
|||||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
* 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)
|
static int bpygpu_ParseVertCompType(PyObject *o, void *p)
|
||||||
{
|
{
|
||||||
Py_ssize_t comp_type_id_len;
|
Py_ssize_t length;
|
||||||
const char *comp_type_id = _PyUnicode_AsStringAndSize(o, &comp_type_id_len);
|
const char *str = _PyUnicode_AsStringAndSize(o, &length);
|
||||||
if (comp_type_id == NULL) {
|
|
||||||
|
if (str == NULL) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"expected a string, got %s",
|
"expected a string, got %s",
|
||||||
Py_TYPE(o)->tp_name);
|
Py_TYPE(o)->tp_name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUVertCompType comp_type;
|
int comp_type = bpygpu_parse_component_type(str, length);
|
||||||
if (comp_type_id_len == 2) {
|
if (comp_type == -1) {
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"unknown type literal: '%s'",
|
"unkown component type: '%s",
|
||||||
comp_type_id);
|
str);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
success:
|
|
||||||
*((GPUVertCompType *)p) = comp_type;
|
*((GPUVertCompType *)p) = comp_type;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
|
static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
|
||||||
{
|
{
|
||||||
Py_ssize_t mode_id_len;
|
Py_ssize_t length;
|
||||||
const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
|
const char *str = _PyUnicode_AsStringAndSize(o, &length);
|
||||||
if (mode_id == NULL) {
|
|
||||||
|
if (str == NULL) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"expected a string, got %s",
|
"expected a string, got %s",
|
||||||
Py_TYPE(o)->tp_name);
|
Py_TYPE(o)->tp_name);
|
||||||
return 0;
|
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;
|
int fetch_mode = bpygpu_parse_fetch_mode(str, length);
|
||||||
MATCH_ID(FLOAT);
|
if (fetch_mode == -1) {
|
||||||
MATCH_ID(INT);
|
|
||||||
MATCH_ID(INT_TO_FLOAT_UNIT);
|
|
||||||
MATCH_ID(INT_TO_FLOAT);
|
|
||||||
#undef MATCH_ID
|
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"unknown type literal: '%s'",
|
"unknown type literal: '%s'",
|
||||||
mode_id);
|
str);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
success:
|
(*(GPUVertFetchMode *)p) = fetch_mode;
|
||||||
(*(GPUVertFetchMode *)p) = mode;
|
|
||||||
return 1;
|
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
|
/** \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)
|
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,
|
PyObject *format_list;
|
||||||
"VertFormat(): takes no arguments");
|
|
||||||
|
static const char *keywords[] = {"format", NULL};
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char**)keywords, &PyList_Type, &format_list)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(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;
|
return (PyObject *)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user