Blender V5.0
gpu_py_element.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include <Python.h>
13
14#include "GPU_index_buffer.hh"
15
16#include "MEM_guardedalloc.h"
17
19#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
20
21#include "gpu_py.hh"
22#include "gpu_py_element.hh" /* own include */
23
24/* -------------------------------------------------------------------- */
27
28static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
29{
31
32 const char *error_prefix = "IndexBuf.__new__";
33 bool ok = true;
34
36 PyObject *seq;
37
38 uint verts_per_prim;
39 uint index_len;
40 GPUIndexBufBuilder builder;
41
42 static const char *_keywords[] = {"type", "seq", nullptr};
43 static _PyArg_Parser _parser = {
45 "$" /* Keyword only arguments. */
46 "O&" /* `type` */
47 "O" /* `seq` */
48 ":IndexBuf.__new__",
49 _keywords,
50 nullptr,
51 };
52 if (!_PyArg_ParseTupleAndKeywordsFast(
53 args, kwds, &_parser, PyC_ParseStringEnum, &prim_type, &seq))
54 {
55 return nullptr;
56 }
57
58 verts_per_prim = GPU_indexbuf_primitive_len(GPUPrimType(prim_type.value_found));
59 if (verts_per_prim == -1) {
60 PyErr_SetString(PyExc_ValueError,
61 "The argument 'type' must be "
62 "'POINTS', 'LINES', 'TRIS', 'LINES_ADJ' or 'TRIS_ADJ'");
63 return nullptr;
64 }
65
66 if (PyObject_CheckBuffer(seq)) {
67 Py_buffer pybuffer;
68
69 if (PyObject_GetBuffer(seq, &pybuffer, PyBUF_FORMAT | PyBUF_ND) == -1) {
70 /* PyObject_GetBuffer already handles error messages. */
71 return nullptr;
72 }
73
74 if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) {
75 PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim);
76 PyBuffer_Release(&pybuffer);
77 return nullptr;
78 }
79
80 if (pybuffer.itemsize != 4 ||
82 {
83 PyErr_SetString(PyExc_ValueError, "Each index must be an 4-bytes integer value");
84 PyBuffer_Release(&pybuffer);
85 return nullptr;
86 }
87
88 index_len = pybuffer.shape[0];
89 if (pybuffer.ndim != 1) {
90 index_len *= pybuffer.shape[1];
91 }
92
93 /* The `vertex_len` parameter is only used for asserts in the Debug build. */
94 /* Not very useful in python since scripts are often tested in Release build. */
95 /* Use `INT_MAX` instead of the actual number of vertices. */
96 GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
97
98 uint *buf = static_cast<uint *>(pybuffer.buf);
99 for (uint i = index_len; i--; buf++) {
100 GPU_indexbuf_add_generic_vert(&builder, *buf);
101 }
102
103 PyBuffer_Release(&pybuffer);
104 }
105 else {
106 PyObject *seq_fast = PySequence_Fast(seq, error_prefix);
107
108 if (seq_fast == nullptr) {
109 return nullptr;
110 }
111
112 const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
113
114 PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
115
116 index_len = seq_len * verts_per_prim;
117
118 /* The `vertex_len` parameter is only used for asserts in the Debug build. */
119 /* Not very useful in python since scripts are often tested in Release build. */
120 /* Use `INT_MAX` instead of the actual number of vertices. */
121 GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
122
123 if (verts_per_prim == 1) {
124 for (uint i = 0; i < seq_len; i++) {
125 GPU_indexbuf_add_generic_vert(&builder, PyC_Long_AsU32(seq_items[i]));
126 }
127 }
128 else {
129 int values[4];
130 for (uint i = 0; i < seq_len; i++) {
131 PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
132 if (seq_fast_item == nullptr) {
133 PyErr_Format(PyExc_TypeError,
134 "%s: expected a sequence, got %s",
135 error_prefix,
136 Py_TYPE(seq_items[i])->tp_name);
137 ok = false;
138 goto finally;
139 }
140
141 ok = PyC_AsArray_FAST(values,
142 sizeof(*values),
143 seq_fast_item,
144 verts_per_prim,
145 &PyLong_Type,
146 error_prefix) == 0;
147
148 if (ok) {
149 for (uint j = 0; j < verts_per_prim; j++) {
150 GPU_indexbuf_add_generic_vert(&builder, values[j]);
151 }
152 }
153 Py_DECREF(seq_fast_item);
154 }
155 }
156
157 if (PyErr_Occurred()) {
158 ok = false;
159 }
160
161 finally:
162
163 Py_DECREF(seq_fast);
164 }
165
166 if (ok == false) {
167 MEM_freeN(builder.data);
168 return nullptr;
169 }
170
172}
173
175{
177 Py_TYPE(self)->tp_free(self);
178}
179
181 /* Wrap. */
182 pygpu_IndexBuf__tp_doc,
183 ".. class:: GPUIndexBuf(type, seq)\n"
184 "\n"
185 " Contains an index buffer.\n"
186 "\n"
187 " :arg type: The primitive type this index buffer is composed of.\n"
188 " Possible values are [``POINTS``, ``LINES``, ``TRIS``, ``LINES_ADJ``, ``TRIS_ADJ``].\n"
189 " :type type: str\n"
190 " :arg seq: Indices this index buffer will contain.\n"
191 " Whether a 1D or 2D sequence is required depends on the type.\n"
192 " Optionally the sequence can support the buffer protocol.\n"
193 " :type seq: Buffer | Sequence[int] | Sequence[Sequence[int]]\n");
194PyTypeObject BPyGPUIndexBuf_Type = {
195 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
196 /*tp_name*/ "GPUIndexBuf",
197 /*tp_basicsize*/ sizeof(BPyGPUIndexBuf),
198 /*tp_itemsize*/ 0,
199 /*tp_dealloc*/ (destructor)pygpu_IndexBuf__tp_dealloc,
200 /*tp_vectorcall_offset*/ 0,
201 /*tp_getattr*/ nullptr,
202 /*tp_setattr*/ nullptr,
203 /*tp_as_async*/ nullptr,
204 /*tp_repr*/ nullptr,
205 /*tp_as_number*/ nullptr,
206 /*tp_as_sequence*/ nullptr,
207 /*tp_as_mapping*/ nullptr,
208 /*tp_hash*/ nullptr,
209 /*tp_call*/ nullptr,
210 /*tp_str*/ nullptr,
211 /*tp_getattro*/ nullptr,
212 /*tp_setattro*/ nullptr,
213 /*tp_as_buffer*/ nullptr,
214 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
215 /*tp_doc*/ pygpu_IndexBuf__tp_doc,
216 /*tp_traverse*/ nullptr,
217 /*tp_clear*/ nullptr,
218 /*tp_richcompare*/ nullptr,
219 /*tp_weaklistoffset*/ 0,
220 /*tp_iter*/ nullptr,
221 /*tp_iternext*/ nullptr,
222 /*tp_methods*/ nullptr,
223 /*tp_members*/ nullptr,
224 /*tp_getset*/ nullptr,
225 /*tp_base*/ nullptr,
226 /*tp_dict*/ nullptr,
227 /*tp_descr_get*/ nullptr,
228 /*tp_descr_set*/ nullptr,
229 /*tp_dictoffset*/ 0,
230 /*tp_init*/ nullptr,
231 /*tp_alloc*/ nullptr,
232 /*tp_new*/ pygpu_IndexBuf__tp_new,
233 /*tp_free*/ nullptr,
234 /*tp_is_gc*/ nullptr,
235 /*tp_bases*/ nullptr,
236 /*tp_mro*/ nullptr,
237 /*tp_cache*/ nullptr,
238 /*tp_subclasses*/ nullptr,
239 /*tp_weaklist*/ nullptr,
240 /*tp_del*/ nullptr,
241 /*tp_version_tag*/ 0,
242 /*tp_finalize*/ nullptr,
243 /*tp_vectorcall*/ nullptr,
244};
245
247
248/* -------------------------------------------------------------------- */
251
253{
255
256 self = PyObject_New(BPyGPUIndexBuf, &BPyGPUIndexBuf_Type);
257 self->elem = elem;
258
259 return (PyObject *)self;
260}
261
unsigned int uint
void GPU_indexbuf_discard(blender::gpu::IndexBuf *elem)
int GPU_indexbuf_primitive_len(GPUPrimType prim_type)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v)
GPUPrimType
@ GPU_PRIM_NONE
Read Guarded memory(de)allocation.
PyObject * self
PyC_StringEnumItems bpygpu_primtype_items[]
Definition gpu_py.cc:26
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:20
PyDoc_STRVAR(pygpu_IndexBuf__tp_doc, ".. class:: GPUIndexBuf(type, seq)\n" "\n" " Contains an index buffer.\n" "\n" " :arg type: The primitive type this index buffer is composed of.\n" " Possible values are [``POINTS``, ``LINES``, ``TRIS``, ``LINES_ADJ``, ``TRIS_ADJ``].\n" " :type type: str\n" " :arg seq: Indices this index buffer will contain.\n" " Whether a 1D or 2D sequence is required depends on the type.\n" " Optionally the sequence can support the buffer protocol.\n" " :type seq: Buffer | Sequence[int] | Sequence[Sequence[int]]\n")
static void pygpu_IndexBuf__tp_dealloc(BPyGPUIndexBuf *self)
PyObject * BPyGPUIndexBuf_CreatePyObject(blender::gpu::IndexBuf *elem)
static PyObject * pygpu_IndexBuf__tp_new(PyTypeObject *, PyObject *args, PyObject *kwds)
PyTypeObject BPyGPUIndexBuf_Type
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
char PyC_StructFmt_type_from_str(const char *typestr)
int PyC_ParseStringEnum(PyObject *o, void *p)
uint32_t PyC_Long_AsU32(PyObject *value)
int PyC_AsArray_FAST(void *array, const size_t array_item_size, PyObject *value_fast, const Py_ssize_t length, const PyTypeObject *type, const char *error_prefix)
bool PyC_StructFmt_type_is_float_any(char format)
header-only compatibility defines.
Py_DECREF(oname)
#define PY_ARG_PARSER_HEAD_COMPAT()
i
Definition text_draw.cc:230