From da96336e5ffa146cc240d1e9fe3c3d98e41387d1 Mon Sep 17 00:00:00 2001 From: mano-wii Date: Thu, 27 Sep 2018 00:53:45 -0300 Subject: Python GPU module: Wrap GPUIndexBuf Differential Revision D3714 --- source/blender/python/gpu/CMakeLists.txt | 4 + source/blender/python/gpu/gpu_py_batch.c | 110 ++++++------ source/blender/python/gpu/gpu_py_element.c | 251 +++++++++++++++++++++++++++ source/blender/python/gpu/gpu_py_element.h | 39 +++++ source/blender/python/gpu/gpu_py_primitive.c | 81 +++++++++ source/blender/python/gpu/gpu_py_primitive.h | 30 ++++ source/blender/python/gpu/gpu_py_types.c | 3 + source/blender/python/gpu/gpu_py_types.h | 1 + 8 files changed, 459 insertions(+), 60 deletions(-) create mode 100644 source/blender/python/gpu/gpu_py_element.c create mode 100644 source/blender/python/gpu/gpu_py_element.h create mode 100644 source/blender/python/gpu/gpu_py_primitive.c create mode 100644 source/blender/python/gpu/gpu_py_primitive.h (limited to 'source/blender/python') diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index bdd8fa1b996..0d4dedf7c76 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -36,8 +36,10 @@ set(INC_SYS set(SRC gpu_py_api.c gpu_py_batch.c + gpu_py_element.c gpu_py_matrix.c gpu_py_offscreen.c + gpu_py_primitive.c gpu_py_select.c gpu_py_shader.c gpu_py_types.c @@ -46,8 +48,10 @@ set(SRC gpu_py_api.h gpu_py_batch.h + gpu_py_element.h gpu_py_matrix.h gpu_py_offscreen.h + gpu_py_primitive.h gpu_py_select.h gpu_py_shader.h gpu_py_types.h diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 4e7804f382f..df7d305300c 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -45,8 +45,10 @@ #include "../generic/py_capi_utils.h" +#include "gpu_py_primitive.h" #include "gpu_py_shader.h" #include "gpu_py_vertex_buffer.h" +#include "gpu_py_element.h" #include "gpu_py_batch.h" /* own include */ @@ -55,69 +57,56 @@ /** \name VertBatch Type * \{ */ -static int bpygpu_ParsePrimType(PyObject *o, void *p) -{ - Py_ssize_t mode_id_len; - const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len); - if (mode_id == 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_PRIM_##id; \ - goto success; \ - } \ - } ((void)0) - - GPUPrimType mode; - MATCH_ID(POINTS); - MATCH_ID(LINES); - MATCH_ID(TRIS); - MATCH_ID(LINE_STRIP); - MATCH_ID(LINE_LOOP); - MATCH_ID(TRI_STRIP); - MATCH_ID(TRI_FAN); - MATCH_ID(LINE_STRIP_ADJ); - -#undef MATCH_ID - PyErr_Format(PyExc_ValueError, - "unknown type literal: '%s'", - mode_id); - return 0; - -success: - (*(GPUPrimType *)p) = mode; - return 1; -} - static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { + const char *exc_str_missing_arg = "GPUBatch.__new__() missing required argument '%s' (pos %d)"; + struct { GPUPrimType type_id; - BPyGPUVertBuf *py_buf; - } params; + BPyGPUVertBuf *py_vertbuf; + BPyGPUIndexBuf *py_indexbuf; + } params = {GPU_PRIM_NONE, NULL, NULL}; - static const char *_keywords[] = {"type", "buf", NULL}; - static _PyArg_Parser _parser = {"$O&O!:GPUBatch.__new__", _keywords, 0}; + static const char *_keywords[] = {"type", "buf", "elem", NULL}; + static _PyArg_Parser _parser = {"|$O&O!O!:GPUBatch.__new__", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast( args, kwds, &_parser, bpygpu_ParsePrimType, ¶ms.type_id, - &BPyGPUVertBuf_Type, ¶ms.py_buf)) + &BPyGPUVertBuf_Type, ¶ms.py_vertbuf, + &BPyGPUIndexBuf_Type, ¶ms.py_indexbuf)) { return NULL; } - GPUBatch *batch = GPU_batch_create(params.type_id, params.py_buf->buf, NULL); + if (params.type_id == GPU_PRIM_NONE) { + PyErr_Format(PyExc_TypeError, + exc_str_missing_arg, _keywords[0], 1); + return NULL; + } + + if (params.py_vertbuf == NULL) { + PyErr_Format(PyExc_TypeError, + exc_str_missing_arg, _keywords[1], 2); + return NULL; + } + + GPUBatch *batch = GPU_batch_create( + params.type_id, + params.py_vertbuf->buf, + params.py_indexbuf ? params.py_indexbuf->elem : NULL); + BPyGPUBatch *ret = (BPyGPUBatch *)BPyGPUBatch_CreatePyObject(batch); #ifdef USE_GPU_PY_REFERENCES - ret->references = PyList_New(1); - PyList_SET_ITEM(ret->references, 0, (PyObject *)params.py_buf); - Py_INCREF(params.py_buf); + ret->references = PyList_New(params.py_indexbuf ? 2 : 1); + PyList_SET_ITEM(ret->references, 0, (PyObject *)params.py_vertbuf); + Py_INCREF(params.py_vertbuf); + + if (params.py_indexbuf != NULL) { + PyList_SET_ITEM(ret->references, 1, (PyObject *)params.py_indexbuf); + Py_INCREF(params.py_indexbuf); + } + PyObject_GC_Track(ret); #endif @@ -373,25 +362,26 @@ static void bpygpu_Batch_dealloc(BPyGPUBatch *self) } PyDoc_STRVAR(py_gpu_batch_doc, -"GPUBatch(type, buf)\n" +"GPUBatch(type, buf, elem=None)\n" "\n" "Contains VAOs + VBOs + Shader representing a drawable entity." "\n" " :param type: One of these primitive types: {\n" -" \"POINTS\",\n" -" \"LINES\",\n" -" \"TRIS\",\n" -" \"LINE_STRIP\",\n" -" \"LINE_LOOP\",\n" -" \"TRI_STRIP\",\n" -" \"TRI_FAN\",\n" -" \"LINES_ADJ\",\n" -" \"TRIS_ADJ\",\n" -" \"LINE_STRIP_ADJ\",\n" -" \"NONE\"}\n" +" 'POINTS',\n" +" 'LINES',\n" +" 'TRIS',\n" +" 'LINE_STRIP',\n" +" 'LINE_LOOP',\n" +" 'TRI_STRIP',\n" +" 'TRI_FAN',\n" +" 'LINES_ADJ',\n" +" 'TRIS_ADJ',\n" +" 'LINE_STRIP_ADJ'}\n" " :type type: `str`\n" " :param buf: Vertex buffer.\n" " :type buf: :class: `gpu.types.GPUVertBuf`\n" +" :param elem: Optional Index buffer.\n" +" :type elem: :class: `gpu.types.GPUIndexBuf`\n" ); PyTypeObject BPyGPUBatch_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c new file mode 100644 index 00000000000..1683def7ec8 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_element.c @@ -0,0 +1,251 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/gpu/gpu_py_element.c + * \ingroup bpygpu + * + * - Use ``bpygpu_`` for local API. + * - Use ``BPyGPU`` for public API. + */ + +#include + +#include "GPU_element.h" + +#include "BLI_math.h" + +#include "MEM_guardedalloc.h" + +#include "../generic/py_capi_utils.h" +#include "../generic/python_utildefines.h" + +#include "gpu_py_primitive.h" +#include "gpu_py_element.h" /* own include */ + + +/* -------------------------------------------------------------------- */ + +/** \name IndexBuf Type + * \{ */ + +static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +{ + bool ok = true; + + struct { + GPUPrimType type_id; + PyObject *seq; + } params; + + uint verts_per_prim; + uint index_len; + GPUIndexBufBuilder builder; + + static const char *_keywords[] = {"type", "seq", NULL}; + static _PyArg_Parser _parser = {"$O&O:IndexBuf.__new__", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kwds, &_parser, + bpygpu_ParsePrimType, ¶ms.type_id, + ¶ms.seq)) + { + return NULL; + } + + verts_per_prim = GPU_indexbuf_primitive_len(params.type_id); + if (verts_per_prim == -1) { + PyErr_Format(PyExc_ValueError, + "The argument 'type' must be " + "'POINTS', 'LINES', 'TRIS' or 'LINES_ADJ'"); + return NULL; + } + + if (PyObject_CheckBuffer(params.seq)) { + Py_buffer pybuffer; + + if (PyObject_GetBuffer(params.seq, &pybuffer, PyBUF_FORMAT | PyBUF_ND) == -1) { + /* PyObject_GetBuffer already handles error messages. */ + return NULL; + } + + if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) { + PyErr_Format(PyExc_ValueError, + "Each primitive must exactly %d indices", + verts_per_prim); + return NULL; + } + + bool format_error = pybuffer.itemsize != 4; + { + char *typestr = pybuffer.format; + if (ELEM(typestr[0], '<', '>', '|')) { + typestr += 1; + } + if (ELEM(typestr[0], 'f', 'd')) { + format_error = true; + } + } + + if (format_error) { + PyErr_Format(PyExc_ValueError, + "Each index must be an integer value with 4 bytes in size"); + return NULL; + } + + index_len = pybuffer.shape[0]; + if (pybuffer.ndim != 1) { + index_len *= pybuffer.shape[1]; + } + + /* The `vertex_len` parameter is only used for asserts in the Debug build. + /* Not very useful in python since scripts are often tested in Release build. + /* Use `INT_MAX` instead of the actual number of vertices. */ + GPU_indexbuf_init( + &builder, params.type_id, index_len, INT_MAX); + +#if 0 + uint *buf = pybuffer.buf; + for (uint i = index_len; i--; buf++) { + GPU_indexbuf_add_generic_vert(&builder, *buf); + } +#else + memcpy(builder.data, pybuffer.buf, index_len * sizeof(builder.data)); + builder.index_len = index_len; +#endif + PyBuffer_Release(&pybuffer); + } + else { + PyObject *seq_fast = PySequence_Fast( + params.seq, "Index Buffer Initialization"); + + if (seq_fast == NULL) { + return false; + } + + const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast); + + PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast); + + index_len = seq_len * verts_per_prim; + + /* The `vertex_len` parameter is only used for asserts in the Debug build. + /* Not very useful in python since scripts are often tested in Release build. + /* Use `INT_MAX` instead of the actual number of vertices. */ + GPU_indexbuf_init( + &builder, params.type_id, index_len, INT_MAX); + + if (verts_per_prim == 1) { + for (uint i = 0; i < seq_len; i++) { + GPU_indexbuf_add_generic_vert( + &builder, PyC_Long_AsU32(seq_items[i])); + } + } + else { + for (uint i = 0; i < seq_len; i++) { + PyObject *item = seq_items[i]; + if (!PyTuple_CheckExact(item)) { + PyErr_Format(PyExc_ValueError, + "expected a tuple, got %s", + Py_TYPE(item)->tp_name); + ok = false; + goto finally; + } + if (PyTuple_GET_SIZE(item) != verts_per_prim) { + PyErr_Format(PyExc_ValueError, + "Expected a Tuple of size %d, got %d", + PyTuple_GET_SIZE(item)); + ok = false; + goto finally; + } + + for (uint j = 0; j < verts_per_prim; j++) { + GPU_indexbuf_add_generic_vert( + &builder, + PyC_Long_AsU32(PyTuple_GET_ITEM(item, j))); + } + } + } + + if (PyErr_Occurred()) { + ok = false; + } + +finally: + + Py_DECREF(seq_fast); + } + + if (ok == false) { + MEM_freeN(builder.data); + return NULL; + } + + return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder)); +} + +static void bpygpu_IndexBuf_dealloc(BPyGPUIndexBuf *self) +{ + GPU_indexbuf_discard(self->elem); + Py_TYPE(self)->tp_free(self); +} + +PyDoc_STRVAR(py_gpu_element_doc, +"GPUIndexBuf(type, seq)\n" +"\n" +"Contains a VBO." +"\n" +" :param prim_type:\n" +" One of these primitive types: {\n" +" 'POINTS',\n" +" 'LINES',\n" +" 'TRIS',\n" +" 'LINE_STRIP_ADJ'}\n" +" :type type: `str`\n" +" :param seq: Sequence of integers.\n" +" :type buf: `Any 1D or 2D Sequence`\n" +); +PyTypeObject BPyGPUIndexBuf_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "GPUIndexBuf", + .tp_basicsize = sizeof(BPyGPUIndexBuf), + .tp_dealloc = (destructor)bpygpu_IndexBuf_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = py_gpu_element_doc, + .tp_new = bpygpu_IndexBuf_new, +}; + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Public API + * \{ */ + +PyObject *BPyGPUIndexBuf_CreatePyObject(GPUIndexBuf *elem) +{ + BPyGPUIndexBuf *self; + + self = PyObject_New(BPyGPUIndexBuf, &BPyGPUIndexBuf_Type); + self->elem = elem; + + return (PyObject *)self; +} + +/** \} */ diff --git a/source/blender/python/gpu/gpu_py_element.h b/source/blender/python/gpu/gpu_py_element.h new file mode 100644 index 00000000000..e201a767582 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_element.h @@ -0,0 +1,39 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/gpu/gpu_py_element.h + * \ingroup bpygpu + */ + +#ifndef __GPU_PY_ELEMENT_H__ +#define __GPU_PY_ELEMENT_H__ + +extern PyTypeObject BPyGPUIndexBuf_Type; + +#define BPyGPUIndexBuf_Check(v) (Py_TYPE(v) == &BPyGPUIndexBuf_Type) + +typedef struct BPyGPUIndexBuf { + PyObject_VAR_HEAD + struct GPUIndexBuf *elem; +} BPyGPUIndexBuf; + +PyObject *BPyGPUIndexBuf_CreatePyObject(struct GPUIndexBuf *elem); + +#endif /* __GPU_PY_ELEMENT_H__ */ diff --git a/source/blender/python/gpu/gpu_py_primitive.c b/source/blender/python/gpu/gpu_py_primitive.c new file mode 100644 index 00000000000..798dfc050f6 --- /dev/null +++ b/source/blender/python/gpu/gpu_py_primitive.c @@ -0,0 +1,81 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2015, Blender Foundation. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/gpu/gpu_py_primitive.c + * \ingroup bpygpu + * + * - Use ``bpygpu_`` for local API. + * - Use ``BPyGPU`` for public API. + */ + +#include + +#include "BLI_utildefines.h" + +#include "GPU_primitive.h" + +#include "gpu_py_primitive.h" /* own include */ + + +/* -------------------------------------------------------------------- */ + +/** \name Primitive Utils + * \{ */ + +int bpygpu_ParsePrimType(PyObject *o, void *p) +{ + Py_ssize_t mode_id_len; + const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len); + if (mode_id == 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_PRIM_##id; \ + goto success; \ + } \ + } ((void)0) + + GPUPrimType mode; + MATCH_ID(POINTS); + MATCH_ID(LINES); + MATCH_ID(TRIS); + MATCH_ID(LINE_STRIP); + MATCH_ID(LINE_LOOP); + MATCH_ID(TRI_STRIP); + MATCH_ID(TRI_FAN); + MATCH_ID(LINE_STRIP_ADJ); + +#undef MATCH_ID + PyErr_Format(PyExc_ValueError, + "unknown type literal: '%s'", + mode_id); + return 0; + +success: + (*(GPUPrimType *)p) = mode; + return 1; +} diff --git a/source/blender/python/gpu/gpu_py_primitive.h b/source/blender/python/gpu/gpu_py_primitive.h new file mode 100644 index 00000000000..d10ee01c8ad --- /dev/null +++ b/source/blender/python/gpu/gpu_py_primitive.h @@ -0,0 +1,30 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/gpu/gpu_py_primitive.h + * \ingroup bpygpu + */ + +#ifndef __GPU_PY_PRIMITIVE_H__ +#define __GPU_PY_PRIMITIVE_H__ + +int bpygpu_ParsePrimType(PyObject *o, void *p); + +#endif /* __GPU_PY_PRIMITIVE_H__ */ diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c index 876d56ad761..d9ef0736f8e 100644 --- a/source/blender/python/gpu/gpu_py_types.c +++ b/source/blender/python/gpu/gpu_py_types.c @@ -53,6 +53,8 @@ PyObject *BPyInit_gpu_types(void) return NULL; if (PyType_Ready(&BPyGPUVertBuf_Type) < 0) return NULL; + if (PyType_Ready(&BPyGPUIndexBuf_Type) < 0) + return NULL; if (PyType_Ready(&BPyGPUBatch_Type) < 0) return NULL; if (PyType_Ready(&BPyGPUOffScreen_Type) < 0) @@ -65,6 +67,7 @@ PyObject *BPyInit_gpu_types(void) MODULE_TYPE_ADD(submodule, BPyGPUVertFormat_Type); MODULE_TYPE_ADD(submodule, BPyGPUVertBuf_Type); + MODULE_TYPE_ADD(submodule, BPyGPUIndexBuf_Type); MODULE_TYPE_ADD(submodule, BPyGPUBatch_Type); MODULE_TYPE_ADD(submodule, BPyGPUOffScreen_Type); MODULE_TYPE_ADD(submodule, BPyGPUShader_Type); diff --git a/source/blender/python/gpu/gpu_py_types.h b/source/blender/python/gpu/gpu_py_types.h index 0a60b58822e..dc91c579aaf 100644 --- a/source/blender/python/gpu/gpu_py_types.h +++ b/source/blender/python/gpu/gpu_py_types.h @@ -27,6 +27,7 @@ #include "gpu_py_vertex_format.h" #include "gpu_py_vertex_buffer.h" +#include "gpu_py_element.h" #include "gpu_py_batch.h" #include "gpu_py_offscreen.h" #include "gpu_py_shader.h" -- cgit v1.2.3