Blender V4.5
gpu_py_shader.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 "BLI_utildefines.h"
15
16#include "GPU_context.hh"
17#include "GPU_shader.hh"
18#include "GPU_texture.hh"
19#include "GPU_uniform_buffer.hh"
20
22#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
24
26
27#include "gpu_py.hh"
28#include "gpu_py_texture.hh"
31
32#include "gpu_py_shader.hh" /* own include */
33
34/* -------------------------------------------------------------------- */
37
38#define PYDOC_BUILTIN_SHADER_DESCRIPTION \
39 "``FLAT_COLOR``\n" \
40 " :Attributes: vec3 pos, vec4 color\n" \
41 " :Uniforms: none\n" \
42 "``IMAGE``\n" \
43 " :Attributes: vec3 pos, vec2 texCoord\n" \
44 " :Uniforms: sampler2D image\n" \
45 "``IMAGE_COLOR``\n" \
46 " :Attributes: vec3 pos, vec2 texCoord\n" \
47 " :Uniforms: sampler2D image, vec4 color\n" \
48 "``SMOOTH_COLOR``\n" \
49 " :Attributes: vec3 pos, vec4 color\n" \
50 " :Uniforms: none\n" \
51 "``UNIFORM_COLOR``\n" \
52 " :Attributes: vec3 pos\n" \
53 " :Uniforms: vec4 color\n" \
54 "``POLYLINE_FLAT_COLOR``\n" \
55 " :Attributes: vec3 pos, vec4 color\n" \
56 " :Uniforms: vec2 viewportSize, float lineWidth\n" \
57 "``POLYLINE_SMOOTH_COLOR``\n" \
58 " :Attributes: vec3 pos, vec4 color\n" \
59 " :Uniforms: vec2 viewportSize, float lineWidth\n" \
60 "``POLYLINE_UNIFORM_COLOR``\n" \
61 " :Attributes: vec3 pos\n" \
62 " :Uniforms: vec2 viewportSize, float lineWidth\n" \
63 "``POINT_FLAT_COLOR``\n" \
64 " :Attributes: vec3 pos, vec4 color\n" \
65 " :Uniforms: float size\n" \
66 "``POINT_UNIFORM_COLOR``\n" \
67 " :Attributes: vec3 pos\n" \
68 " :Uniforms: vec4 color, float size\n"
69
71 {GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"},
72 {GPU_SHADER_3D_IMAGE, "IMAGE"},
73 {GPU_SHADER_3D_IMAGE_COLOR, "IMAGE_COLOR"},
74 {GPU_SHADER_3D_SMOOTH_COLOR, "SMOOTH_COLOR"},
75 {GPU_SHADER_3D_UNIFORM_COLOR, "UNIFORM_COLOR"},
76 {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"},
77 {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "POLYLINE_SMOOTH_COLOR"},
78 {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "POLYLINE_UNIFORM_COLOR"},
79 {GPU_SHADER_3D_POINT_FLAT_COLOR, "POINT_FLAT_COLOR"},
80 {GPU_SHADER_3D_POINT_UNIFORM_COLOR, "POINT_UNIFORM_COLOR"},
81 {0, nullptr},
82};
83
85 {GPU_SHADER_CFG_DEFAULT, "DEFAULT"},
86 {GPU_SHADER_CFG_CLIPPED, "CLIPPED"},
87 {0, nullptr},
88};
89
90static int pygpu_shader_uniform_location_get(GPUShader *shader,
91 const char *name,
92 const char *error_prefix)
93{
94 const int uniform = GPU_shader_get_uniform(shader, name);
95
96 if (uniform == -1) {
97 PyErr_Format(PyExc_ValueError, "%s: uniform %.32s not found", error_prefix, name);
98 }
99
100 return uniform;
101}
102
104
105/* -------------------------------------------------------------------- */
108
109static std::optional<blender::StringRefNull> c_str_to_stringref_opt(const char *str)
110{
111 if (!str) {
112 return std::nullopt;
113 }
115}
116
117static PyObject *pygpu_shader__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
118{
120
122 PyErr_SetString(PyExc_Exception,
123 "Direct shader creation is not supported on Vulkan. "
124 "Use gpu.shader.create_from_info(shader_info) instead.");
125 return nullptr;
126 }
127
129 PyErr_SetString(PyExc_Exception,
130 "Direct shader creation is not supported on Metal. "
131 "Use gpu.shader.create_from_info(shader_info) instead.");
132 return nullptr;
133 }
134
135 PyErr_WarnEx(PyExc_DeprecationWarning,
136 "Direct shader creation is deprecated. "
137 "Use gpu.shader.create_from_info(shader_info) instead.",
138 1);
139
140 struct {
141 const char *vertexcode;
142 const char *fragcode;
143 const char *geocode;
144 const char *libcode;
145 const char *defines;
146 const char *name;
147 } params = {nullptr};
148
149 static const char *_keywords[] = {
150 "vertexcode", "fragcode", "geocode", "libcode", "defines", "name", nullptr};
151 static _PyArg_Parser _parser = {
153 "s" /* `vertexcode` */
154 "s" /* `fragcode` */
155 "|$" /* Optional keyword only arguments. */
156 "s" /* `geocode` */
157 "s" /* `libcode` */
158 "s" /* `defines` */
159 "s" /* `name` */
160 ":GPUShader.__new__",
161 _keywords,
162 nullptr,
163 };
164 if (!_PyArg_ParseTupleAndKeywordsFast(args,
165 kwds,
166 &_parser,
167 &params.vertexcode,
168 &params.fragcode,
169 &params.geocode,
170 &params.libcode,
171 &params.defines,
172 &params.name))
173 {
174 return nullptr;
175 }
176
177 GPUShader *shader = GPU_shader_create_from_python(c_str_to_stringref_opt(params.vertexcode),
183
184 if (shader == nullptr) {
185 PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details");
186 return nullptr;
187 }
188
189 return BPyGPUShader_CreatePyObject(shader, false);
190}
191
193 /* Wrap. */
194 pygpu_shader_bind_doc,
195 ".. method:: bind()\n"
196 "\n"
197 " Bind the shader object. Required to be able to change uniforms of this shader.\n");
199{
200 GPU_shader_bind(self->shader);
201 Py_RETURN_NONE;
202}
203
205 /* Wrap. */
206 pygpu_shader_uniform_from_name_doc,
207 ".. method:: uniform_from_name(name)\n"
208 "\n"
209 " Get uniform location by name.\n"
210 "\n"
211 " :arg name: Name of the uniform variable whose location is to be queried.\n"
212 " :type name: str\n"
213 " :return: Location of the uniform variable.\n"
214 " :rtype: int\n");
215static PyObject *pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
216{
217 const char *name = PyUnicode_AsUTF8(arg);
218 if (name == nullptr) {
219 return nullptr;
220 }
221
222 const int uniform = pygpu_shader_uniform_location_get(
223 self->shader, name, "GPUShader.get_uniform");
224
225 if (uniform == -1) {
226 return nullptr;
227 }
228
229 return PyLong_FromLong(uniform);
230}
231
233 /* Wrap. */
234 pygpu_shader_uniform_block_from_name_doc,
235 ".. method:: uniform_block_from_name(name)\n"
236 "\n"
237 " Get uniform block location by name.\n"
238 "\n"
239 " :arg name: Name of the uniform block variable whose location is to be queried.\n"
240 " :type name: str\n"
241 " :return: The location of the uniform block variable.\n"
242 " :rtype: int\n");
244{
245 const char *name = PyUnicode_AsUTF8(arg);
246 if (name == nullptr) {
247 return nullptr;
248 }
249
250 const int uniform = GPU_shader_get_uniform_block(self->shader, name);
251
252 if (uniform == -1) {
253 PyErr_Format(PyExc_ValueError, "GPUShader.get_uniform_block: uniform %.32s not found", name);
254 return nullptr;
255 }
256
257 return PyLong_FromLong(uniform);
258}
259
260static bool pygpu_shader_uniform_vector_impl(PyObject *args,
261 int elem_size,
262 int *r_location,
263 int *r_length,
264 int *r_count,
265 Py_buffer *r_pybuffer)
266{
267 PyObject *buffer;
268
269 *r_count = 1;
270 if (!PyArg_ParseTuple(
271 args, "iOi|i:GPUShader.uniform_vector_*", r_location, &buffer, r_length, r_count))
272 {
273 return false;
274 }
275
276 if (PyObject_GetBuffer(buffer, r_pybuffer, PyBUF_SIMPLE) == -1) {
277 /* PyObject_GetBuffer raise a PyExc_BufferError */
278 return false;
279 }
280
281 if (r_pybuffer->len < (*r_length * *r_count * elem_size)) {
282 PyErr_SetString(PyExc_OverflowError,
283 "GPUShader.uniform_vector_*: buffer size smaller than required.");
284 return false;
285 }
286
287 return true;
288}
289
291 /* Wrap. */
292 pygpu_shader_uniform_vector_float_doc,
293 ".. method:: uniform_vector_float(location, buffer, length, count)\n"
294 "\n"
295 " Set the buffer to fill the uniform.\n"
296 "\n"
297 " :arg location: Location of the uniform variable to be modified.\n"
298 " :type location: int\n"
299 " :arg buffer: The data that should be set. Can support the buffer protocol.\n"
300 " :type buffer: Sequence[float]\n"
301 " :arg length: Size of the uniform data type:\n"
302 "\n"
303 " - 1: float\n"
304 " - 2: vec2 or float[2]\n"
305 " - 3: vec3 or float[3]\n"
306 " - 4: vec4 or float[4]\n"
307 " - 9: mat3\n"
308 " - 16: mat4\n"
309 " :type length: int\n"
310 " :arg count: Specifies the number of elements, vector or matrices that are to "
311 "be modified.\n"
312 " :type count: int\n");
313static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
314{
315 int location, length, count;
316
317 Py_buffer pybuffer;
318
320 args, sizeof(float), &location, &length, &count, &pybuffer))
321 {
322 return nullptr;
323 }
324
325 GPU_shader_bind(self->shader);
327 self->shader, location, length, count, static_cast<const float *>(pybuffer.buf));
328
329 PyBuffer_Release(&pybuffer);
330
331 Py_RETURN_NONE;
332}
333
335 /* Wrap. */
336 pygpu_shader_uniform_vector_int_doc,
337 ".. method:: uniform_vector_int(location, buffer, length, count)\n"
338 "\n"
339 " See GPUShader.uniform_vector_float(...) description.\n");
340static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args)
341{
342 int location, length, count;
343
344 Py_buffer pybuffer;
345
346 if (!pygpu_shader_uniform_vector_impl(args, sizeof(int), &location, &length, &count, &pybuffer))
347 {
348 return nullptr;
349 }
350
351 GPU_shader_bind(self->shader);
353 self->shader, location, length, count, static_cast<const int *>(pybuffer.buf));
354
355 PyBuffer_Release(&pybuffer);
356
357 Py_RETURN_NONE;
358}
359
361 /* Wrap. */
362 pygpu_shader_uniform_bool_doc,
363 ".. method:: uniform_bool(name, value)\n"
364 "\n"
365 " Specify the value of a uniform variable for the current program object.\n"
366 "\n"
367 " :arg name: Name of the uniform variable whose value is to be changed.\n"
368 " :type name: str\n"
369 " :arg value: Value that will be used to update the specified uniform variable.\n"
370 " :type value: bool | Sequence[bool]\n");
371static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
372{
373 const char *error_prefix = "GPUShader.uniform_bool";
374
375 struct {
376 const char *id;
377 PyObject *seq;
378 } params;
379
380 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_bool", &params.id, &params.seq)) {
381 return nullptr;
382 }
383
384 int values[4];
385 int length;
386 int ret = -1;
387 if (PySequence_Check(params.seq)) {
388 PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
389 if (seq_fast == nullptr) {
390 PyErr_Format(PyExc_TypeError,
391 "%s: expected a sequence, got %s",
392 error_prefix,
393 Py_TYPE(params.seq)->tp_name);
394 }
395 else {
396 length = PySequence_Fast_GET_SIZE(seq_fast);
397 if (length == 0 || length > 4) {
398 PyErr_Format(PyExc_TypeError,
399 "%s: invalid sequence length. expected 1..4, got %d",
400 error_prefix,
401 length);
402 }
403 else {
405 values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
406 }
407 Py_DECREF(seq_fast);
408 }
409 }
410 else if (((values[0] = int(PyLong_AsLong(params.seq))) != -1) && ELEM(values[0], 0, 1)) {
411 length = 1;
412 ret = 0;
413 }
414 else {
415 PyErr_Format(
416 PyExc_ValueError, "expected a bool or sequence, got %s", Py_TYPE(params.seq)->tp_name);
417 }
418
419 if (ret == -1) {
420 return nullptr;
421 }
422
423 const int location = pygpu_shader_uniform_location_get(self->shader, params.id, error_prefix);
424
425 if (location == -1) {
426 return nullptr;
427 }
428
429 GPU_shader_bind(self->shader);
430 GPU_shader_uniform_int_ex(self->shader, location, length, 1, values);
431
432 Py_RETURN_NONE;
433}
434
436 /* Wrap. */
437 pygpu_shader_uniform_float_doc,
438 ".. method:: uniform_float(name, value)\n"
439 "\n"
440 " Specify the value of a uniform variable for the current program object.\n"
441 "\n"
442 " :arg name: Name of the uniform variable whose value is to be changed.\n"
443 " :type name: str\n"
444 " :arg value: Value that will be used to update the specified uniform variable.\n"
445 " :type value: float | Sequence[float]\n");
446static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
447{
448 const char *error_prefix = "GPUShader.uniform_float";
449
450 struct {
451 const char *id;
452 PyObject *seq;
453 } params;
454
455 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_float", &params.id, &params.seq)) {
456 return nullptr;
457 }
458
459 float values[16];
460 int length;
461
462 if (PyFloat_Check(params.seq)) {
463 values[0] = float(PyFloat_AsDouble(params.seq));
464 length = 1;
465 }
466 else if (PyLong_Check(params.seq)) {
467 values[0] = float(PyLong_AsDouble(params.seq));
468 length = 1;
469 }
470 else if (MatrixObject_Check(params.seq)) {
471 MatrixObject *mat = (MatrixObject *)params.seq;
472 if (BaseMath_ReadCallback(mat) == -1) {
473 return nullptr;
474 }
475 if ((mat->row_num != mat->col_num) || !ELEM(mat->row_num, 3, 4)) {
476 PyErr_SetString(PyExc_ValueError, "Expected 3x3 or 4x4 matrix");
477 return nullptr;
478 }
479 length = mat->row_num * mat->col_num;
480 memcpy(values, mat->matrix, sizeof(float) * length);
481 }
482 else {
483 length = mathutils_array_parse(values, 2, 16, params.seq, "");
484 if (length == -1) {
485 return nullptr;
486 }
487 }
488
489 if (!ELEM(length, 1, 2, 3, 4, 9, 16)) {
490 PyErr_SetString(PyExc_TypeError,
491 "Expected a single float or a sequence of floats of length 1..4, 9 or 16.");
492 return nullptr;
493 }
494
495 const int location = pygpu_shader_uniform_location_get(self->shader, params.id, error_prefix);
496
497 if (location == -1) {
498 return nullptr;
499 }
500
501 GPU_shader_bind(self->shader);
502 GPU_shader_uniform_float_ex(self->shader, location, length, 1, values);
503
504 Py_RETURN_NONE;
505}
506
508 /* Wrap. */
509 pygpu_shader_uniform_int_doc,
510 ".. method:: uniform_int(name, seq)\n"
511 "\n"
512 " Specify the value of a uniform variable for the current program object.\n"
513 "\n"
514 " :arg name: name of the uniform variable whose value is to be changed.\n"
515 " :type name: str\n"
516 " :arg seq: Value that will be used to update the specified uniform variable.\n"
517 " :type seq: Sequence[int]\n");
518static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
519{
520 const char *error_prefix = "GPUShader.uniform_int";
521
522 struct {
523 const char *id;
524 PyObject *seq;
525 } params;
526
527 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_int", &params.id, &params.seq)) {
528 return nullptr;
529 }
530
531 int values[4];
532 int length;
533 int ret;
534
535 if (PyLong_Check(params.seq)) {
536 values[0] = PyC_Long_AsI32(params.seq);
537 length = 1;
538 ret = 0;
539 }
540 else {
541 PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
542 if (seq_fast == nullptr) {
543 PyErr_Format(PyExc_TypeError,
544 "%s: expected a sequence, got %s",
545 error_prefix,
546 Py_TYPE(params.seq)->tp_name);
547 ret = -1;
548 }
549 else {
550 length = PySequence_Fast_GET_SIZE(seq_fast);
551 if (length == 0 || length > 4) {
552 PyErr_Format(PyExc_TypeError,
553 "%s: invalid sequence length. expected 1..4, got %d",
554 error_prefix,
555 length);
556 ret = -1;
557 }
558 else {
560 values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
561 }
562 Py_DECREF(seq_fast);
563 }
564 }
565 if (ret == -1) {
566 return nullptr;
567 }
568
569 const int location = pygpu_shader_uniform_location_get(self->shader, params.id, error_prefix);
570
571 if (location == -1) {
572 return nullptr;
573 }
574
575 GPU_shader_bind(self->shader);
576 GPU_shader_uniform_int_ex(self->shader, location, length, 1, values);
577
578 Py_RETURN_NONE;
579}
580
582 /* Wrap. */
583 pygpu_shader_uniform_sampler_doc,
584 ".. method:: uniform_sampler(name, texture)\n"
585 "\n"
586 " Specify the value of a texture uniform variable for the current GPUShader.\n"
587 "\n"
588 " :arg name: name of the uniform variable whose texture is to be specified.\n"
589 " :type name: str\n"
590 " :arg texture: Texture to attach.\n"
591 " :type texture: :class:`gpu.types.GPUTexture`\n");
592static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args)
593{
594 const char *name;
595 BPyGPUTexture *py_texture;
596 if (!PyArg_ParseTuple(
597 args, "sO!:GPUShader.uniform_sampler", &name, &BPyGPUTexture_Type, &py_texture))
598 {
599 return nullptr;
600 }
601
602 GPU_shader_bind(self->shader);
603 int slot = GPU_shader_get_sampler_binding(self->shader, name);
604 GPU_texture_bind(py_texture->tex, slot);
605 GPU_shader_uniform_1i(self->shader, name, slot);
606
607 Py_RETURN_NONE;
608}
609
611 /* Wrap. */
612 pygpu_shader_image_doc,
613 ".. method:: image(name, texture)\n"
614 "\n"
615 " Specify the value of an image variable for the current GPUShader.\n"
616 "\n"
617 " :arg name: Name of the image variable to which the texture is to be bound.\n"
618 " :type name: str\n"
619 " :arg texture: Texture to attach.\n"
620 " :type texture: :class:`gpu.types.GPUTexture`\n");
621static PyObject *pygpu_shader_image(BPyGPUShader *self, PyObject *args)
622{
623 const char *name;
624 BPyGPUTexture *py_texture;
625 if (!PyArg_ParseTuple(args, "sO!:GPUShader.image", &name, &BPyGPUTexture_Type, &py_texture)) {
626 return nullptr;
627 }
628
629 GPU_shader_bind(self->shader);
630 int image_unit = GPU_shader_get_sampler_binding(self->shader, name);
631 if (image_unit == -1) {
632 PyErr_Format(PyExc_ValueError, "Image '%s' not found in shader", name);
633 return nullptr;
634 }
635
636 GPU_texture_image_bind(py_texture->tex, image_unit);
637
638 Py_RETURN_NONE;
639}
640
642 /* Wrap. */
643 pygpu_shader_uniform_block_doc,
644 ".. method:: uniform_block(name, ubo)\n"
645 "\n"
646 " Specify the value of an uniform buffer object variable for the current GPUShader.\n"
647 "\n"
648 " :arg name: name of the uniform variable whose UBO is to be specified.\n"
649 " :type name: str\n"
650 " :arg ubo: Uniform Buffer to attach.\n"
651 " :type texture: :class:`gpu.types.GPUUniformBuf`\n");
652static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
653{
654 const char *name;
655 BPyGPUUniformBuf *py_ubo;
656 if (!PyArg_ParseTuple(
657 args, "sO!:GPUShader.uniform_block", &name, &BPyGPUUniformBuf_Type, &py_ubo))
658 {
659 return nullptr;
660 }
661
662 int binding = GPU_shader_get_ubo_binding(self->shader, name);
663 if (binding == -1) {
664 PyErr_SetString(
665 PyExc_BufferError,
666 "GPUShader.uniform_block: uniform block not found, make sure the name is correct");
667 return nullptr;
668 }
669
670 GPU_shader_bind(self->shader);
671 GPU_uniformbuf_bind(py_ubo->ubo, binding);
672
673 Py_RETURN_NONE;
674}
675
677 /* Wrap. */
678 pygpu_shader_attr_from_name_doc,
679 ".. method:: attr_from_name(name)\n"
680 "\n"
681 " Get attribute location by name.\n"
682 "\n"
683 " :arg name: The name of the attribute variable whose location is to be queried.\n"
684 " :type name: str\n"
685 " :return: The location of an attribute variable.\n"
686 " :rtype: int\n");
687static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
688{
689 const char *name = PyUnicode_AsUTF8(arg);
690 if (name == nullptr) {
691 return nullptr;
692 }
693
694 const int attr = GPU_shader_get_attribute(self->shader, name);
695
696 if (attr == -1) {
697 PyErr_Format(PyExc_ValueError, "GPUShader.attr_from_name: attribute %.32s not found", name);
698 return nullptr;
699 }
700
701 return PyLong_FromLong(attr);
702}
703
705 /* Wrap. */
706 pygpu_shader_format_calc_doc,
707 ".. method:: format_calc()\n"
708 "\n"
709 " Build a new format based on the attributes of the shader.\n"
710 "\n"
711 " :return: vertex attribute format for the shader\n"
712 " :rtype: :class:`gpu.types.GPUVertFormat`\n");
713static PyObject *pygpu_shader_format_calc(BPyGPUShader *self, PyObject * /*arg*/)
714{
716 if (bpygpu_shader_is_polyline(self->shader)) {
718
719 /* WORKAROUND: Special case for POLYLINE shader. */
720 if (GPU_shader_get_ssbo_binding(self->shader, "pos") >= 0) {
722 }
723 if (GPU_shader_get_ssbo_binding(self->shader, "color") >= 0) {
725 }
726 }
727 else {
728 GPU_vertformat_from_shader(&ret->fmt, self->shader);
729 }
730 return (PyObject *)ret;
731}
732
734 /* Wrap. */
735 pygpu_shader_attrs_info_get_doc,
736 ".. method:: attrs_info_get()\n"
737 "\n"
738 " Information about the attributes used in the Shader.\n"
739 "\n"
740 " :return: tuples containing information about the attributes in order (name, type)\n"
741 " :rtype: tuple[tuple[str, str | None], ...]\n");
742static PyObject *pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject * /*arg*/)
743{
744 using namespace blender::gpu::shader;
745 PyObject *ret;
746 int type;
747 int location_test = 0, attrs_added = 0;
748 char name[256];
749
750 if (bpygpu_shader_is_polyline(self->shader)) {
751 /* WORKAROUND: Special case for POLYLINE shader. Check the SSBO inputs as attributes. */
752 uint input_len = GPU_shader_get_ssbo_input_len(self->shader);
753
754 /* Skip "gpu_index_buf". */
755 input_len -= 1;
756 ret = PyTuple_New(input_len);
757 while (attrs_added < input_len) {
758 if (!GPU_shader_get_ssbo_input_info(self->shader, location_test++, name)) {
759 continue;
760 }
761 if (STREQ(name, "gpu_index_buf")) {
762 continue;
763 }
764
765 type = STREQ(name, "pos") ? int(Type::float3_t) :
766 STREQ(name, "color") ? int(Type::float4_t) :
767 -1;
768 PyObject *py_type;
769 if (type != -1) {
770 py_type = PyUnicode_InternFromString(
772 }
773 else {
774 py_type = Py_None;
775 Py_INCREF(py_type);
776 }
777
778 PyObject *attr_info = PyTuple_New(2);
779 PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
780 PyTuple_SetItem(ret, attrs_added, attr_info);
781 attrs_added++;
782 }
783 }
784 else {
785 uint attr_len = GPU_shader_get_attribute_len(self->shader);
786
787 ret = PyTuple_New(attr_len);
788 while (attrs_added < attr_len) {
789 if (!GPU_shader_get_attribute_info(self->shader, location_test++, name, &type)) {
790 continue;
791 }
792 PyObject *py_type;
793 if (type != -1) {
794 py_type = PyUnicode_InternFromString(
796 }
797 else {
798 py_type = Py_None;
799 Py_INCREF(py_type);
800 }
801
802 PyObject *attr_info = PyTuple_New(2);
803 PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
804 PyTuple_SetItem(ret, attrs_added, attr_info);
805 attrs_added++;
806 }
807 }
808 return ret;
809}
810
811#ifdef __GNUC__
812# ifdef __clang__
813# pragma clang diagnostic push
814# pragma clang diagnostic ignored "-Wcast-function-type"
815# else
816# pragma GCC diagnostic push
817# pragma GCC diagnostic ignored "-Wcast-function-type"
818# endif
819#endif
820
821static PyMethodDef pygpu_shader__tp_methods[] = {
822 {"bind", (PyCFunction)pygpu_shader_bind, METH_NOARGS, pygpu_shader_bind_doc},
823 {"uniform_from_name",
825 METH_O,
826 pygpu_shader_uniform_from_name_doc},
827 {"uniform_block_from_name",
829 METH_O,
830 pygpu_shader_uniform_block_from_name_doc},
831 {"uniform_vector_float",
833 METH_VARARGS,
834 pygpu_shader_uniform_vector_float_doc},
835 {"uniform_vector_int",
837 METH_VARARGS,
838 pygpu_shader_uniform_vector_int_doc},
839 {"uniform_bool",
840 (PyCFunction)pygpu_shader_uniform_bool,
841 METH_VARARGS,
842 pygpu_shader_uniform_bool_doc},
843 {"uniform_float",
844 (PyCFunction)pygpu_shader_uniform_float,
845 METH_VARARGS,
846 pygpu_shader_uniform_float_doc},
847 {"uniform_int",
848 (PyCFunction)pygpu_shader_uniform_int,
849 METH_VARARGS,
850 pygpu_shader_uniform_int_doc},
851 {"uniform_sampler",
852 (PyCFunction)pygpu_shader_uniform_sampler,
853 METH_VARARGS,
854 pygpu_shader_uniform_sampler_doc},
855 {"image", (PyCFunction)pygpu_shader_image, METH_VARARGS, pygpu_shader_image_doc},
856 {"uniform_block",
857 (PyCFunction)pygpu_shader_uniform_block,
858 METH_VARARGS,
859 pygpu_shader_uniform_block_doc},
860 {"attr_from_name",
861 (PyCFunction)pygpu_shader_attr_from_name,
862 METH_O,
863 pygpu_shader_attr_from_name_doc},
864 {"format_calc",
865 (PyCFunction)pygpu_shader_format_calc,
866 METH_NOARGS,
867 pygpu_shader_format_calc_doc},
868 {"attrs_info_get",
869 (PyCFunction)pygpu_shader_attrs_info_get,
870 METH_NOARGS,
871 pygpu_shader_attrs_info_get_doc},
872 {nullptr, nullptr, 0, nullptr},
873};
874
875#ifdef __GNUC__
876# ifdef __clang__
877# pragma clang diagnostic pop
878# else
879# pragma GCC diagnostic pop
880# endif
881#endif
882
884 /* Wrap. */
885 pygpu_shader_name_doc,
886 "The name of the shader object for debugging purposes (read-only).\n"
887 "\n"
888 ":type: str");
889static PyObject *pygpu_shader_name(BPyGPUShader *self, void * /*closure*/)
890{
891 return PyUnicode_FromString(GPU_shader_get_name(self->shader));
892}
893
895 /* Wrap. */
896 pygpu_shader_program_doc,
897 "The name of the program object for use by the OpenGL API (read-only).\n"
898 "This is deprecated and will always return -1.\n"
899 "\n"
900 ":type: int");
901static PyObject *pygpu_shader_program_get(BPyGPUShader * /*self*/, void * /*closure*/)
902{
903 PyErr_WarnEx(
904 PyExc_DeprecationWarning, "'program' is deprecated. No valid handle will be returned.", 1);
905 return PyLong_FromLong(-1);
906}
907
908static PyGetSetDef pygpu_shader__tp_getseters[] = {
909 {"program",
911 (setter) nullptr,
912 pygpu_shader_program_doc,
913 nullptr},
914 {"name", (getter)pygpu_shader_name, (setter) nullptr, pygpu_shader_name_doc, nullptr},
915 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
916};
917
919{
920 if (self->is_builtin == false) {
921 GPU_shader_free(self->shader);
922 }
923 Py_TYPE(self)->tp_free((PyObject *)self);
924}
925
927 /* Wrap. */
928 pygpu_shader__tp_doc,
929 ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None, "
930 "name='pyGPUShader')\n"
931 "\n"
932 " Constructor is deprecated and will be removed in Blender 5.0, "
933 "use :func:`gpu.shader.create_from_info` instead.\n"
934 "\n"
935 " GPUShader combines multiple GLSL shaders into a program used for drawing.\n"
936 " It must contain at least a vertex and fragment shaders.\n"
937 "\n"
938 " The GLSL ``#version`` directive is automatically included at the top of shaders,\n"
939 " and set to 330. Some preprocessor directives are automatically added according to\n"
940 " the Operating System or availability: ``GPU_ATI``, ``GPU_NVIDIA`` and ``GPU_INTEL``.\n"
941 "\n"
942 " The following extensions are enabled by default if supported by the GPU:\n"
943 " ``GL_ARB_texture_gather``, ``GL_ARB_texture_cube_map_array``\n"
944 " and ``GL_ARB_shader_draw_parameters``.\n"
945 "\n"
946 " For drawing user interface elements and gizmos, use\n"
947 " ``fragOutput = blender_srgb_to_framebuffer_space(fragOutput)``\n"
948 " to transform the output sRGB colors to the frame-buffer color-space.\n"
949 "\n"
950 " :arg vertexcode: Vertex shader code.\n"
951 " :type vertexcode: str\n"
952 " :arg fragcode: Fragment shader code.\n"
953 " :type value: str\n"
954 " :arg geocode: Geometry shader code.\n"
955 " :type value: str\n"
956 " :arg libcode: Code with functions and presets to be shared between shaders.\n"
957 " :type value: str\n"
958 " :arg defines: Preprocessor directives.\n"
959 " :type value: str\n"
960 " :arg name: Name of shader code, for debugging purposes.\n"
961 " :type value: str\n");
962PyTypeObject BPyGPUShader_Type = {
963 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
964 /*tp_name*/ "GPUShader",
965 /*tp_basicsize*/ sizeof(BPyGPUShader),
966 /*tp_itemsize*/ 0,
967 /*tp_dealloc*/ (destructor)pygpu_shader__tp_dealloc,
968 /*tp_vectorcall_offset*/ 0,
969 /*tp_getattr*/ nullptr,
970 /*tp_setattr*/ nullptr,
971 /*tp_as_async*/ nullptr,
972 /*tp_repr*/ nullptr,
973 /*tp_as_number*/ nullptr,
974 /*tp_as_sequence*/ nullptr,
975 /*tp_as_mapping*/ nullptr,
976 /*tp_hash*/ nullptr,
977 /*tp_call*/ nullptr,
978 /*tp_str*/ nullptr,
979 /*tp_getattro*/ nullptr,
980 /*tp_setattro*/ nullptr,
981 /*tp_as_buffer*/ nullptr,
982 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
983 /*tp_doc*/ pygpu_shader__tp_doc,
984 /*tp_traverse*/ nullptr,
985 /*tp_clear*/ nullptr,
986 /*tp_richcompare*/ nullptr,
987 /*tp_weaklistoffset*/ 0,
988 /*tp_iter*/ nullptr,
989 /*tp_iternext*/ nullptr,
990 /*tp_methods*/ pygpu_shader__tp_methods,
991 /*tp_members*/ nullptr,
992 /*tp_getset*/ pygpu_shader__tp_getseters,
993 /*tp_base*/ nullptr,
994 /*tp_dict*/ nullptr,
995 /*tp_descr_get*/ nullptr,
996 /*tp_descr_set*/ nullptr,
997 /*tp_dictoffset*/ 0,
998 /*tp_init*/ nullptr,
999 /*tp_alloc*/ nullptr,
1000 /*tp_new*/ pygpu_shader__tp_new,
1001 /*tp_free*/ nullptr,
1002 /*tp_is_gc*/ nullptr,
1003 /*tp_bases*/ nullptr,
1004 /*tp_mro*/ nullptr,
1005 /*tp_cache*/ nullptr,
1006 /*tp_subclasses*/ nullptr,
1007 /*tp_weaklist*/ nullptr,
1008 /*tp_del*/ nullptr,
1009 /*tp_version_tag*/ 0,
1010 /*tp_finalize*/ nullptr,
1011 /*tp_vectorcall*/ nullptr,
1012};
1013
1015
1016/* -------------------------------------------------------------------- */
1019
1021 /* Wrap. */
1022 pygpu_shader_unbind_doc,
1023 ".. function:: unbind()\n"
1024 "\n"
1025 " Unbind the bound shader object.\n");
1026static PyObject *pygpu_shader_unbind(BPyGPUShader * /*self*/)
1027{
1029 Py_RETURN_NONE;
1030}
1031
1033 /* Wrap. */
1034 pygpu_shader_from_builtin_doc,
1035 ".. function:: from_builtin(shader_name, config='DEFAULT')\n"
1036 "\n"
1037 " Shaders that are embedded in the blender internal code (see :ref:`built-in-shaders`).\n"
1038 " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n"
1039 " which can be edited by the :mod:`gpu.matrix` module.\n"
1040 "\n"
1041 " You can also choose a shader configuration that uses clip_planes by setting the "
1042 "``CLIPPED`` value to the config parameter. Note that in this case you also need to "
1043 "manually set the value of ``mat4 ModelMatrix``.\n"
1044 "\n"
1045 " :arg shader_name: One of the builtin shader names.\n"
1046 " :type shader_name: str\n"
1047 " :arg config: One of these types of shader configuration:\n"
1048 "\n"
1049 " - ``DEFAULT``\n"
1050 " - ``CLIPPED``\n"
1051 " :type config: str\n"
1052 " :return: Shader object corresponding to the given name.\n"
1053 " :rtype: :class:`gpu.types.GPUShader`\n");
1054static PyObject *pygpu_shader_from_builtin(PyObject * /*self*/, PyObject *args, PyObject *kwds)
1055{
1057
1058 PyC_StringEnum pygpu_bultinshader = {pygpu_shader_builtin_items};
1060
1061 static const char *_keywords[] = {"shader_name", "config", nullptr};
1062 static _PyArg_Parser _parser = {
1064 "O&" /* `shader_name` */
1065 "|$" /* Optional keyword only arguments. */
1066 "O&" /* `config` */
1067 ":from_builtin",
1068 _keywords,
1069 nullptr,
1070 };
1071 if (!_PyArg_ParseTupleAndKeywordsFast(args,
1072 kwds,
1073 &_parser,
1075 &pygpu_bultinshader,
1077 &pygpu_config))
1078 {
1079 return nullptr;
1080 }
1081
1083 eGPUBuiltinShader(pygpu_bultinshader.value_found),
1084 eGPUShaderConfig(pygpu_config.value_found));
1085
1086 if (shader == nullptr) {
1087 PyErr_Format(PyExc_ValueError, "Builtin shader doesn't exist in the requested config");
1088 return nullptr;
1089 }
1090
1091 return BPyGPUShader_CreatePyObject(shader, true);
1092}
1093
1095 /* Wrap. */
1096 pygpu_shader_create_from_info_doc,
1097 ".. function:: create_from_info(shader_info)\n"
1098 "\n"
1099 " Create shader from a GPUShaderCreateInfo.\n"
1100 "\n"
1101 " :arg shader_info: GPUShaderCreateInfo\n"
1102 " :type shader_info: :class:`gpu.types.GPUShaderCreateInfo`\n"
1103 " :return: Shader object corresponding to the given name.\n"
1104 " :rtype: :class:`gpu.types.GPUShader`\n");
1106{
1108
1110 PyErr_Format(PyExc_TypeError, "Expected a GPUShaderCreateInfo, got %s", Py_TYPE(o)->tp_name);
1111 return nullptr;
1112 }
1113
1114 char error[128];
1116 PyErr_SetString(PyExc_Exception, error);
1117 return nullptr;
1118 }
1119
1121 if (!shader) {
1122 PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details");
1123 return nullptr;
1124 }
1125
1126 return BPyGPUShader_CreatePyObject(shader, false);
1127}
1128
1129#ifdef __GNUC__
1130# ifdef __clang__
1131# pragma clang diagnostic push
1132# pragma clang diagnostic ignored "-Wcast-function-type"
1133# else
1134# pragma GCC diagnostic push
1135# pragma GCC diagnostic ignored "-Wcast-function-type"
1136# endif
1137#endif
1138
1139static PyMethodDef pygpu_shader_module__tp_methods[] = {
1140 {"unbind", (PyCFunction)pygpu_shader_unbind, METH_NOARGS, pygpu_shader_unbind_doc},
1141 {"from_builtin",
1142 (PyCFunction)pygpu_shader_from_builtin,
1143 METH_VARARGS | METH_KEYWORDS,
1144 pygpu_shader_from_builtin_doc},
1145 {"create_from_info",
1146 (PyCFunction)pygpu_shader_create_from_info,
1147 METH_O,
1148 pygpu_shader_create_from_info_doc},
1149 {nullptr, nullptr, 0, nullptr},
1150};
1151
1152#ifdef __GNUC__
1153# ifdef __clang__
1154# pragma clang diagnostic pop
1155# else
1156# pragma GCC diagnostic pop
1157# endif
1158#endif
1159
1161 /* Wrap. */
1162 pygpu_shader_module__tp_doc,
1163 "This module provides access to GPUShader internal functions.\n"
1164 "\n"
1165 ".. _built-in-shaders:\n"
1166 "\n"
1167 ".. rubric:: Built-in shaders\n"
1168 "\n"
1169 "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n"
1170 "\n"
1171 "Its value must be modified using the :class:`gpu.matrix` module.\n"
1173static PyModuleDef pygpu_shader_module_def = {
1174 /*m_base*/ PyModuleDef_HEAD_INIT,
1175 /*m_name*/ "gpu.shader",
1176 /*m_doc*/ pygpu_shader_module__tp_doc,
1177 /*m_size*/ 0,
1178 /*m_methods*/ pygpu_shader_module__tp_methods,
1179 /*m_slots*/ nullptr,
1180 /*m_traverse*/ nullptr,
1181 /*m_clear*/ nullptr,
1182 /*m_free*/ nullptr,
1183};
1184
1186
1187/* -------------------------------------------------------------------- */
1190
1191PyObject *BPyGPUShader_CreatePyObject(GPUShader *shader, bool is_builtin)
1192{
1194
1195 self = PyObject_New(BPyGPUShader, &BPyGPUShader_Type);
1196 self->shader = shader;
1197 self->is_builtin = is_builtin;
1198
1199 return (PyObject *)self;
1200}
1201
1203{
1204 PyObject *submodule;
1205
1206 submodule = PyModule_Create(&pygpu_shader_module_def);
1207
1208 return submodule;
1209}
1210
1218
unsigned int uint
#define ELEM(...)
#define STREQ(a, b)
eGPUBackendType GPU_backend_get_type()
int GPU_shader_get_sampler_binding(GPUShader *shader, const char *name)
int GPU_shader_get_uniform(GPUShader *shader, const char *name)
GPUShader * GPU_shader_create_from_info_python(const GPUShaderCreateInfo *_info)
bool GPU_shader_get_attribute_info(const GPUShader *shader, int attr_location, char r_name[256], int *r_type)
const char * GPU_shader_get_name(GPUShader *shader)
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
int GPU_shader_get_ubo_binding(GPUShader *shader, const char *name)
uint GPU_shader_get_ssbo_input_len(const GPUShader *shader)
void GPU_shader_uniform_int_ex(GPUShader *shader, int location, int length, int array_size, const int *value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
uint GPU_shader_get_attribute_len(const GPUShader *shader)
int GPU_shader_get_ssbo_binding(GPUShader *shader, const char *name)
int GPU_shader_get_attribute(const GPUShader *shader, const char *name)
GPUShader * GPU_shader_create_from_python(std::optional< blender::StringRefNull > vertcode, std::optional< blender::StringRefNull > fragcode, std::optional< blender::StringRefNull > geomcode, std::optional< blender::StringRefNull > libcode, std::optional< blender::StringRefNull > defines, std::optional< blender::StringRefNull > name)
void GPU_shader_uniform_float_ex(GPUShader *shader, int location, int length, int array_size, const float *value)
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
void GPU_shader_free(GPUShader *shader)
bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128])
void GPU_shader_unbind()
bool GPU_shader_get_ssbo_input_info(const GPUShader *shader, int ssbo_location, char r_name[256])
@ GPU_SHADER_CFG_DEFAULT
@ GPU_SHADER_CFG_CLIPPED
GPUShader * GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, eGPUShaderConfig sh_cfg)
GPUShader * GPU_shader_get_builtin_shader(eGPUBuiltinShader shader)
eGPUBuiltinShader
@ GPU_SHADER_3D_SMOOTH_COLOR
@ GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_3D_POINT_FLAT_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_FLAT_COLOR
@ GPU_SHADER_3D_IMAGE
@ GPU_SHADER_3D_POLYLINE_FLAT_COLOR
@ GPU_SHADER_3D_POINT_UNIFORM_COLOR
@ GPU_SHADER_3D_IMAGE_COLOR
void GPU_texture_bind(GPUTexture *texture, int unit)
void GPU_texture_image_bind(GPUTexture *texture, int unit)
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
@ GPU_FETCH_FLOAT
void GPU_vertformat_clear(GPUVertFormat *)
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
@ GPU_COMP_F32
PyObject * self
#define str(s)
float length(VecOp< float, D >) RET
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:20
#define PYDOC_BUILTIN_SHADER_DESCRIPTION
PyObject * BPyGPUShader_CreatePyObject(GPUShader *shader, bool is_builtin)
static PyObject * pygpu_shader_program_get(BPyGPUShader *, void *)
static const PyC_StringEnumItems pygpu_shader_builtin_items[]
static PyMethodDef pygpu_shader__tp_methods[]
static PyObject * pygpu_shader_unbind(BPyGPUShader *)
static PyObject * pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
static PyObject * pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args)
bool bpygpu_shader_is_polyline(GPUShader *shader)
static PyObject * pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
static PyObject * pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
static PyObject * pygpu_shader_create_from_info(BPyGPUShader *, BPyGPUShaderCreateInfo *o)
static PyObject * pygpu_shader_name(BPyGPUShader *self, void *)
PyTypeObject BPyGPUShader_Type
static PyObject * pygpu_shader_bind(BPyGPUShader *self)
static bool pygpu_shader_uniform_vector_impl(PyObject *args, int elem_size, int *r_location, int *r_length, int *r_count, Py_buffer *r_pybuffer)
PyObject * bpygpu_shader_init()
static PyObject * pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
static PyObject * pygpu_shader_image(BPyGPUShader *self, PyObject *args)
static const PyC_StringEnumItems pygpu_shader_config_items[]
static PyObject * pygpu_shader__tp_new(PyTypeObject *, PyObject *args, PyObject *kwds)
static void pygpu_shader__tp_dealloc(BPyGPUShader *self)
static PyObject * pygpu_shader_from_builtin(PyObject *, PyObject *args, PyObject *kwds)
static PyObject * pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
static PyObject * pygpu_shader_format_calc(BPyGPUShader *self, PyObject *)
static PyObject * pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args)
static PyMethodDef pygpu_shader_module__tp_methods[]
static PyObject * pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
static std::optional< blender::StringRefNull > c_str_to_stringref_opt(const char *str)
static PyObject * pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
static PyObject * pygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg)
static PyObject * pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject *)
static PyModuleDef pygpu_shader_module_def
PyDoc_STRVAR(pygpu_shader_bind_doc, ".. method:: bind()\n" "\n" " Bind the shader object. Required to be able to change uniforms of this shader.\n")
static PyGetSetDef pygpu_shader__tp_getseters[]
static int pygpu_shader_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix)
const struct PyC_StringEnumItems pygpu_attrtype_items[]
#define BPyGPUShaderCreateInfo_Check(v)
PyTypeObject BPyGPUTexture_Type
PyTypeObject BPyGPUUniformBuf_Type
PyObject * BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:96
#define BaseMath_ReadCallback(_self)
Definition mathutils.hh:126
#define MatrixObject_Check(v)
static void error(const char *str)
int PyC_ParseStringEnum(PyObject *o, void *p)
const char * PyC_StringEnum_FindIDFromValue(const PyC_StringEnumItems *items, const int 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)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
PyObject_VAR_HEAD GPUShaderCreateInfo * info
PyObject_HEAD GPUTexture * tex
PyObject_HEAD GPUUniformBuf * ubo