Blender V5.0
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_SCENE_LINEAR_TO_REC709_SRGB``\n" \
46 " :Attributes: vec3 pos, vec2 texCoord\n" \
47 " :Uniforms: sampler2D image\n" \
48 " :Note: Expect texture to be in scene linear color space\n" \
49 "``IMAGE_COLOR``\n" \
50 " :Attributes: vec3 pos, vec2 texCoord\n" \
51 " :Uniforms: sampler2D image, vec4 color\n" \
52 "``IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB``\n" \
53 " :Attributes: vec3 pos, vec2 texCoord\n" \
54 " :Uniforms: sampler2D image, vec4 color\n" \
55 " :Note: Expect texture to be in scene linear color space\n" \
56 "``SMOOTH_COLOR``\n" \
57 " :Attributes: vec3 pos, vec4 color\n" \
58 " :Uniforms: none\n" \
59 "``UNIFORM_COLOR``\n" \
60 " :Attributes: vec3 pos\n" \
61 " :Uniforms: vec4 color\n" \
62 "``POLYLINE_FLAT_COLOR``\n" \
63 " :Attributes: vec3 pos, vec4 color\n" \
64 " :Uniforms: vec2 viewportSize, float lineWidth\n" \
65 "``POLYLINE_SMOOTH_COLOR``\n" \
66 " :Attributes: vec3 pos, vec4 color\n" \
67 " :Uniforms: vec2 viewportSize, float lineWidth\n" \
68 "``POLYLINE_UNIFORM_COLOR``\n" \
69 " :Attributes: vec3 pos\n" \
70 " :Uniforms: vec2 viewportSize, float lineWidth, vec4 color\n" \
71 "``POINT_FLAT_COLOR``\n" \
72 " :Attributes: vec3 pos, vec4 color\n" \
73 " :Uniforms: float size\n" \
74 "``POINT_UNIFORM_COLOR``\n" \
75 " :Attributes: vec3 pos\n" \
76 " :Uniforms: vec4 color, float size\n"
77
79 {GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"},
80 {GPU_SHADER_3D_IMAGE, "IMAGE"},
81 {GPU_SHADER_3D_IMAGE_SCENE_LINEAR_TO_REC709_SRGB, "IMAGE_SCENE_LINEAR_TO_REC709_SRGB"},
82 {GPU_SHADER_3D_IMAGE_COLOR, "IMAGE_COLOR"},
84 "IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB"},
85 {GPU_SHADER_3D_SMOOTH_COLOR, "SMOOTH_COLOR"},
86 {GPU_SHADER_3D_UNIFORM_COLOR, "UNIFORM_COLOR"},
87 {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"},
88 {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "POLYLINE_SMOOTH_COLOR"},
89 {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "POLYLINE_UNIFORM_COLOR"},
90 {GPU_SHADER_3D_POINT_FLAT_COLOR, "POINT_FLAT_COLOR"},
91 {GPU_SHADER_3D_POINT_UNIFORM_COLOR, "POINT_UNIFORM_COLOR"},
92 {0, nullptr},
93};
94
96 {GPU_SHADER_CFG_DEFAULT, "DEFAULT"},
97 {GPU_SHADER_CFG_CLIPPED, "CLIPPED"},
98 {0, nullptr},
99};
100
102 const char *name,
103 const char *error_prefix)
104{
105 const int uniform = GPU_shader_get_uniform(shader, name);
106
107 if (uniform == -1) {
108 PyErr_Format(PyExc_ValueError, "%s: uniform %.32s not found", error_prefix, name);
109 }
110
111 return uniform;
112}
113
115
116/* -------------------------------------------------------------------- */
119
121 /* Wrap. */
122 pygpu_shader_bind_doc,
123 ".. method:: bind()\n"
124 "\n"
125 " Bind the shader object. Required to be able to change uniforms of this shader.\n");
127{
128 GPU_shader_bind(self->shader);
129 Py_RETURN_NONE;
130}
131
133 /* Wrap. */
134 pygpu_shader_uniform_from_name_doc,
135 ".. method:: uniform_from_name(name)\n"
136 "\n"
137 " Get uniform location by name.\n"
138 "\n"
139 " :arg name: Name of the uniform variable whose location is to be queried.\n"
140 " :type name: str\n"
141 " :return: Location of the uniform variable.\n"
142 " :rtype: int\n");
143static PyObject *pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
144{
145 const char *name = PyUnicode_AsUTF8(arg);
146 if (name == nullptr) {
147 return nullptr;
148 }
149
150 const int uniform = pygpu_shader_uniform_location_get(
151 self->shader, name, "GPUShader.get_uniform");
152
153 if (uniform == -1) {
154 return nullptr;
155 }
156
157 return PyLong_FromLong(uniform);
158}
159
161 /* Wrap. */
162 pygpu_shader_uniform_block_from_name_doc,
163 ".. method:: uniform_block_from_name(name)\n"
164 "\n"
165 " Get uniform block location by name.\n"
166 "\n"
167 " :arg name: Name of the uniform block variable whose location is to be queried.\n"
168 " :type name: str\n"
169 " :return: The location of the uniform block variable.\n"
170 " :rtype: int\n");
172{
173 const char *name = PyUnicode_AsUTF8(arg);
174 if (name == nullptr) {
175 return nullptr;
176 }
177
178 const int uniform = GPU_shader_get_uniform_block(self->shader, name);
179
180 if (uniform == -1) {
181 PyErr_Format(PyExc_ValueError, "GPUShader.get_uniform_block: uniform %.32s not found", name);
182 return nullptr;
183 }
184
185 return PyLong_FromLong(uniform);
186}
187
188static bool pygpu_shader_uniform_vector_impl(PyObject *args,
189 int elem_size,
190 int *r_location,
191 int *r_length,
192 int *r_count,
193 Py_buffer *r_pybuffer)
194{
195 PyObject *buffer;
196
197 *r_count = 1;
198 if (!PyArg_ParseTuple(
199 args, "iOi|i:GPUShader.uniform_vector_*", r_location, &buffer, r_length, r_count))
200 {
201 return false;
202 }
203
204 if (PyObject_GetBuffer(buffer, r_pybuffer, PyBUF_SIMPLE) == -1) {
205 /* PyObject_GetBuffer raise a PyExc_BufferError */
206 return false;
207 }
208
209 if (r_pybuffer->len < (*r_length * *r_count * elem_size)) {
210 PyErr_SetString(PyExc_OverflowError,
211 "GPUShader.uniform_vector_*: buffer size smaller than required.");
212 return false;
213 }
214
215 return true;
216}
217
219 /* Wrap. */
220 pygpu_shader_uniform_vector_float_doc,
221 ".. method:: uniform_vector_float(location, buffer, length, count)\n"
222 "\n"
223 " Set the buffer to fill the uniform.\n"
224 "\n"
225 " :arg location: Location of the uniform variable to be modified.\n"
226 " :type location: int\n"
227 " :arg buffer: The data that should be set. Can support the buffer protocol.\n"
228 " :type buffer: Sequence[float]\n"
229 " :arg length: Size of the uniform data type:\n"
230 "\n"
231 " - 1: float\n"
232 " - 2: vec2 or float[2]\n"
233 " - 3: vec3 or float[3]\n"
234 " - 4: vec4 or float[4]\n"
235 " - 9: mat3\n"
236 " - 16: mat4\n"
237 " :type length: int\n"
238 " :arg count: Specifies the number of elements, vector or matrices that are to "
239 "be modified.\n"
240 " :type count: int\n");
241static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
242{
243 int location, length, count;
244
245 Py_buffer pybuffer;
246
248 args, sizeof(float), &location, &length, &count, &pybuffer))
249 {
250 return nullptr;
251 }
252
253 GPU_shader_bind(self->shader);
255 self->shader, location, length, count, static_cast<const float *>(pybuffer.buf));
256
257 PyBuffer_Release(&pybuffer);
258
259 Py_RETURN_NONE;
260}
261
263 /* Wrap. */
264 pygpu_shader_uniform_vector_int_doc,
265 ".. method:: uniform_vector_int(location, buffer, length, count)\n"
266 "\n"
267 " See GPUShader.uniform_vector_float(...) description.\n");
268static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args)
269{
270 int location, length, count;
271
272 Py_buffer pybuffer;
273
274 if (!pygpu_shader_uniform_vector_impl(args, sizeof(int), &location, &length, &count, &pybuffer))
275 {
276 return nullptr;
277 }
278
279 GPU_shader_bind(self->shader);
281 self->shader, location, length, count, static_cast<const int *>(pybuffer.buf));
282
283 PyBuffer_Release(&pybuffer);
284
285 Py_RETURN_NONE;
286}
287
289 /* Wrap. */
290 pygpu_shader_uniform_bool_doc,
291 ".. method:: uniform_bool(name, value)\n"
292 "\n"
293 " Specify the value of a uniform variable for the current program object.\n"
294 "\n"
295 " :arg name: Name of the uniform variable whose value is to be changed.\n"
296 " :type name: str\n"
297 " :arg value: Value that will be used to update the specified uniform variable.\n"
298 " :type value: bool | Sequence[bool]\n");
299static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
300{
301 const char *error_prefix = "GPUShader.uniform_bool";
302
303 struct {
304 const char *id;
305 PyObject *seq;
306 } params;
307
308 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_bool", &params.id, &params.seq)) {
309 return nullptr;
310 }
311
312 int values[4];
313 int length;
314 int ret = -1;
315 if (PySequence_Check(params.seq)) {
316 PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
317 if (seq_fast == nullptr) {
318 PyErr_Format(PyExc_TypeError,
319 "%s: expected a sequence, got %s",
320 error_prefix,
321 Py_TYPE(params.seq)->tp_name);
322 }
323 else {
324 length = PySequence_Fast_GET_SIZE(seq_fast);
325 if (length == 0 || length > 4) {
326 PyErr_Format(PyExc_TypeError,
327 "%s: invalid sequence length. expected 1..4, got %d",
328 error_prefix,
329 length);
330 }
331 else {
333 values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
334 }
335 Py_DECREF(seq_fast);
336 }
337 }
338 else if (((values[0] = int(PyLong_AsLong(params.seq))) != -1) && ELEM(values[0], 0, 1)) {
339 length = 1;
340 ret = 0;
341 }
342 else {
343 PyErr_Format(
344 PyExc_ValueError, "expected a bool or sequence, got %s", Py_TYPE(params.seq)->tp_name);
345 }
346
347 if (ret == -1) {
348 return nullptr;
349 }
350
351 const int location = pygpu_shader_uniform_location_get(self->shader, params.id, error_prefix);
352
353 if (location == -1) {
354 return nullptr;
355 }
356
357 GPU_shader_bind(self->shader);
358 GPU_shader_uniform_int_ex(self->shader, location, length, 1, values);
359
360 Py_RETURN_NONE;
361}
362
364 /* Wrap. */
365 pygpu_shader_uniform_float_doc,
366 ".. method:: uniform_float(name, value)\n"
367 "\n"
368 " Specify the value of a uniform variable for the current program object.\n"
369 "\n"
370 " :arg name: Name of the uniform variable whose value is to be changed.\n"
371 " :type name: str\n"
372 " :arg value: Value that will be used to update the specified uniform variable.\n"
373 " :type value: float | Sequence[float]\n");
374static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
375{
376 const char *error_prefix = "GPUShader.uniform_float";
377
378 struct {
379 const char *id;
380 PyObject *seq;
381 } params;
382
383 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_float", &params.id, &params.seq)) {
384 return nullptr;
385 }
386
387 float values[16];
388 int length;
389
390 if (PyFloat_Check(params.seq)) {
391 values[0] = float(PyFloat_AsDouble(params.seq));
392 length = 1;
393 }
394 else if (PyLong_Check(params.seq)) {
395 values[0] = float(PyLong_AsDouble(params.seq));
396 length = 1;
397 }
398 else if (MatrixObject_Check(params.seq)) {
399 MatrixObject *mat = (MatrixObject *)params.seq;
400 if (BaseMath_ReadCallback(mat) == -1) {
401 return nullptr;
402 }
403 if ((mat->row_num != mat->col_num) || !ELEM(mat->row_num, 3, 4)) {
404 PyErr_SetString(PyExc_ValueError, "Expected 3x3 or 4x4 matrix");
405 return nullptr;
406 }
407 length = mat->row_num * mat->col_num;
408 memcpy(values, mat->matrix, sizeof(float) * length);
409 }
410 else {
411 length = mathutils_array_parse(values, 2, 16, params.seq, "");
412 if (length == -1) {
413 return nullptr;
414 }
415 }
416
417 if (!ELEM(length, 1, 2, 3, 4, 9, 16)) {
418 PyErr_SetString(PyExc_TypeError,
419 "Expected a single float or a sequence of floats of length 1..4, 9 or 16.");
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_float_ex(self->shader, location, length, 1, values);
431
432 Py_RETURN_NONE;
433}
434
436 /* Wrap. */
437 pygpu_shader_uniform_int_doc,
438 ".. method:: uniform_int(name, seq)\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 seq: Value that will be used to update the specified uniform variable.\n"
445 " :type seq: Sequence[int]\n");
446static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
447{
448 const char *error_prefix = "GPUShader.uniform_int";
449
450 struct {
451 const char *id;
452 PyObject *seq;
453 } params;
454
455 if (!PyArg_ParseTuple(args, "sO:GPUShader.uniform_int", &params.id, &params.seq)) {
456 return nullptr;
457 }
458
459 int values[4];
460 int length;
461 int ret;
462
463 if (PyLong_Check(params.seq)) {
464 values[0] = PyC_Long_AsI32(params.seq);
465 length = 1;
466 ret = 0;
467 }
468 else {
469 PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
470 if (seq_fast == nullptr) {
471 PyErr_Format(PyExc_TypeError,
472 "%s: expected a sequence, got %s",
473 error_prefix,
474 Py_TYPE(params.seq)->tp_name);
475 ret = -1;
476 }
477 else {
478 length = PySequence_Fast_GET_SIZE(seq_fast);
479 if (length == 0 || length > 4) {
480 PyErr_Format(PyExc_TypeError,
481 "%s: invalid sequence length. expected 1..4, got %d",
482 error_prefix,
483 length);
484 ret = -1;
485 }
486 else {
488 values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix);
489 }
490 Py_DECREF(seq_fast);
491 }
492 }
493 if (ret == -1) {
494 return nullptr;
495 }
496
497 const int location = pygpu_shader_uniform_location_get(self->shader, params.id, error_prefix);
498
499 if (location == -1) {
500 return nullptr;
501 }
502
503 GPU_shader_bind(self->shader);
504 GPU_shader_uniform_int_ex(self->shader, location, length, 1, values);
505
506 Py_RETURN_NONE;
507}
508
510 /* Wrap. */
511 pygpu_shader_uniform_sampler_doc,
512 ".. method:: uniform_sampler(name, texture)\n"
513 "\n"
514 " Specify the value of a texture uniform variable for the current GPUShader.\n"
515 "\n"
516 " :arg name: name of the uniform variable whose texture is to be specified.\n"
517 " :type name: str\n"
518 " :arg texture: Texture to attach.\n"
519 " :type texture: :class:`gpu.types.GPUTexture`\n");
520static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args)
521{
522 const char *name;
523 BPyGPUTexture *py_texture;
524 if (!PyArg_ParseTuple(
525 args, "sO!:GPUShader.uniform_sampler", &name, &BPyGPUTexture_Type, &py_texture))
526 {
527 return nullptr;
528 }
529
530 GPU_shader_bind(self->shader);
531 int slot = GPU_shader_get_sampler_binding(self->shader, name);
532 GPU_texture_bind(py_texture->tex, slot);
533 GPU_shader_uniform_1i(self->shader, name, slot);
534
535 Py_RETURN_NONE;
536}
537
539 /* Wrap. */
540 pygpu_shader_image_doc,
541 ".. method:: image(name, texture)\n"
542 "\n"
543 " Specify the value of an image variable for the current GPUShader.\n"
544 "\n"
545 " :arg name: Name of the image variable to which the texture is to be bound.\n"
546 " :type name: str\n"
547 " :arg texture: Texture to attach.\n"
548 " :type texture: :class:`gpu.types.GPUTexture`\n");
549static PyObject *pygpu_shader_image(BPyGPUShader *self, PyObject *args)
550{
551 const char *name;
552 BPyGPUTexture *py_texture;
553 if (!PyArg_ParseTuple(args, "sO!:GPUShader.image", &name, &BPyGPUTexture_Type, &py_texture)) {
554 return nullptr;
555 }
556
557 GPU_shader_bind(self->shader);
558 int image_unit = GPU_shader_get_sampler_binding(self->shader, name);
559 if (image_unit == -1) {
560 PyErr_Format(PyExc_ValueError, "Image '%s' not found in shader", name);
561 return nullptr;
562 }
563
564 GPU_texture_image_bind(py_texture->tex, image_unit);
565
566 Py_RETURN_NONE;
567}
568
570 /* Wrap. */
571 pygpu_shader_uniform_block_doc,
572 ".. method:: uniform_block(name, ubo)\n"
573 "\n"
574 " Specify the value of an uniform buffer object variable for the current GPUShader.\n"
575 "\n"
576 " :arg name: name of the uniform variable whose UBO is to be specified.\n"
577 " :type name: str\n"
578 " :arg ubo: Uniform Buffer to attach.\n"
579 " :type texture: :class:`gpu.types.GPUUniformBuf`\n");
580static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
581{
582 const char *name;
583 BPyGPUUniformBuf *py_ubo;
584 if (!PyArg_ParseTuple(
585 args, "sO!:GPUShader.uniform_block", &name, &BPyGPUUniformBuf_Type, &py_ubo))
586 {
587 return nullptr;
588 }
589
590 int binding = GPU_shader_get_ubo_binding(self->shader, name);
591 if (binding == -1) {
592 PyErr_SetString(
593 PyExc_BufferError,
594 "GPUShader.uniform_block: uniform block not found, make sure the name is correct");
595 return nullptr;
596 }
597
598 GPU_shader_bind(self->shader);
599 GPU_uniformbuf_bind(py_ubo->ubo, binding);
600
601 Py_RETURN_NONE;
602}
603
605 /* Wrap. */
606 pygpu_shader_attr_from_name_doc,
607 ".. method:: attr_from_name(name)\n"
608 "\n"
609 " Get attribute location by name.\n"
610 "\n"
611 " :arg name: The name of the attribute variable whose location is to be queried.\n"
612 " :type name: str\n"
613 " :return: The location of an attribute variable.\n"
614 " :rtype: int\n");
615static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
616{
617 const char *name = PyUnicode_AsUTF8(arg);
618 if (name == nullptr) {
619 return nullptr;
620 }
621
622 const int attr = GPU_shader_get_attribute(self->shader, name);
623
624 if (attr == -1) {
625 PyErr_Format(PyExc_ValueError, "GPUShader.attr_from_name: attribute %.32s not found", name);
626 return nullptr;
627 }
628
629 return PyLong_FromLong(attr);
630}
631
633 /* Wrap. */
634 pygpu_shader_format_calc_doc,
635 ".. method:: format_calc()\n"
636 "\n"
637 " Build a new format based on the attributes of the shader.\n"
638 "\n"
639 " :return: vertex attribute format for the shader\n"
640 " :rtype: :class:`gpu.types.GPUVertFormat`\n");
641static PyObject *pygpu_shader_format_calc(BPyGPUShader *self, PyObject * /*arg*/)
642{
644 if (bpygpu_shader_is_polyline(self->shader)) {
646
647 /* WORKAROUND: Special case for POLYLINE shader. */
648 if (GPU_shader_get_ssbo_binding(self->shader, "pos") >= 0) {
649 GPU_vertformat_attr_add(&ret->fmt, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
650 }
651 if (GPU_shader_get_ssbo_binding(self->shader, "color") >= 0) {
652 GPU_vertformat_attr_add(&ret->fmt, "color", blender::gpu::VertAttrType::SFLOAT_32_32_32_32);
653 }
654 }
655 else {
656 GPU_vertformat_from_shader(&ret->fmt, self->shader);
657 }
658 return (PyObject *)ret;
659}
660
662 /* Wrap. */
663 pygpu_shader_attrs_info_get_doc,
664 ".. method:: attrs_info_get()\n"
665 "\n"
666 " Information about the attributes used in the Shader.\n"
667 "\n"
668 " :return: tuples containing information about the attributes in order (name, type)\n"
669 " :rtype: tuple[tuple[str, str | None], ...]\n");
670static PyObject *pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject * /*arg*/)
671{
672 using namespace blender::gpu::shader;
673 PyObject *ret;
674 int type;
675 int location_test = 0, attrs_added = 0;
676 char name[256];
677
678 if (bpygpu_shader_is_polyline(self->shader)) {
679 /* WORKAROUND: Special case for POLYLINE shader. Check the SSBO inputs as attributes. */
680 uint input_len = GPU_shader_get_ssbo_input_len(self->shader);
681
682 /* Skip "gpu_index_buf". */
683 input_len -= 1;
684 ret = PyTuple_New(input_len);
685 while (attrs_added < input_len) {
686 if (!GPU_shader_get_ssbo_input_info(self->shader, location_test++, name)) {
687 continue;
688 }
689 if (STREQ(name, "gpu_index_buf")) {
690 continue;
691 }
692
693 type = STREQ(name, "pos") ? int(Type::float3_t) :
694 STREQ(name, "color") ? int(Type::float4_t) :
695 -1;
696 PyObject *py_type;
697 if (type != -1) {
698 py_type = PyUnicode_InternFromString(
700 }
701 else {
702 py_type = Py_None;
703 Py_INCREF(py_type);
704 }
705
706 PyObject *attr_info = PyTuple_New(2);
707 PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
708 PyTuple_SetItem(ret, attrs_added, attr_info);
709 attrs_added++;
710 }
711 }
712 else {
713 uint attr_len = GPU_shader_get_attribute_len(self->shader);
714
715 ret = PyTuple_New(attr_len);
716 while (attrs_added < attr_len) {
717 if (!GPU_shader_get_attribute_info(self->shader, location_test++, name, &type)) {
718 continue;
719 }
720 PyObject *py_type;
721 if (type != -1) {
722 py_type = PyUnicode_InternFromString(
724 }
725 else {
726 py_type = Py_None;
727 Py_INCREF(py_type);
728 }
729
730 PyObject *attr_info = PyTuple_New(2);
731 PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
732 PyTuple_SetItem(ret, attrs_added, attr_info);
733 attrs_added++;
734 }
735 }
736 return ret;
737}
738
739#ifdef __GNUC__
740# ifdef __clang__
741# pragma clang diagnostic push
742# pragma clang diagnostic ignored "-Wcast-function-type"
743# else
744# pragma GCC diagnostic push
745# pragma GCC diagnostic ignored "-Wcast-function-type"
746# endif
747#endif
748
749static PyMethodDef pygpu_shader__tp_methods[] = {
750 {"bind", (PyCFunction)pygpu_shader_bind, METH_NOARGS, pygpu_shader_bind_doc},
751 {"uniform_from_name",
753 METH_O,
754 pygpu_shader_uniform_from_name_doc},
755 {"uniform_block_from_name",
757 METH_O,
758 pygpu_shader_uniform_block_from_name_doc},
759 {"uniform_vector_float",
761 METH_VARARGS,
762 pygpu_shader_uniform_vector_float_doc},
763 {"uniform_vector_int",
765 METH_VARARGS,
766 pygpu_shader_uniform_vector_int_doc},
767 {"uniform_bool",
768 (PyCFunction)pygpu_shader_uniform_bool,
769 METH_VARARGS,
770 pygpu_shader_uniform_bool_doc},
771 {"uniform_float",
772 (PyCFunction)pygpu_shader_uniform_float,
773 METH_VARARGS,
774 pygpu_shader_uniform_float_doc},
775 {"uniform_int",
776 (PyCFunction)pygpu_shader_uniform_int,
777 METH_VARARGS,
778 pygpu_shader_uniform_int_doc},
779 {"uniform_sampler",
780 (PyCFunction)pygpu_shader_uniform_sampler,
781 METH_VARARGS,
782 pygpu_shader_uniform_sampler_doc},
783 {"image", (PyCFunction)pygpu_shader_image, METH_VARARGS, pygpu_shader_image_doc},
784 {"uniform_block",
785 (PyCFunction)pygpu_shader_uniform_block,
786 METH_VARARGS,
787 pygpu_shader_uniform_block_doc},
788 {"attr_from_name",
789 (PyCFunction)pygpu_shader_attr_from_name,
790 METH_O,
791 pygpu_shader_attr_from_name_doc},
792 {"format_calc",
793 (PyCFunction)pygpu_shader_format_calc,
794 METH_NOARGS,
795 pygpu_shader_format_calc_doc},
796 {"attrs_info_get",
797 (PyCFunction)pygpu_shader_attrs_info_get,
798 METH_NOARGS,
799 pygpu_shader_attrs_info_get_doc},
800 {nullptr, nullptr, 0, nullptr},
801};
802
803#ifdef __GNUC__
804# ifdef __clang__
805# pragma clang diagnostic pop
806# else
807# pragma GCC diagnostic pop
808# endif
809#endif
810
812 /* Wrap. */
813 pygpu_shader_name_doc,
814 "The name of the shader object for debugging purposes (read-only).\n"
815 "\n"
816 ":type: str\n");
817static PyObject *pygpu_shader_name(BPyGPUShader *self, void * /*closure*/)
818{
819 return PyUnicode_FromString(GPU_shader_get_name(self->shader));
820}
821
823 /* Wrap. */
824 pygpu_shader_program_doc,
825 "The name of the program object for use by the OpenGL API (read-only).\n"
826 "This is deprecated and will always return -1.\n"
827 "\n"
828 ":type: int\n");
829static PyObject *pygpu_shader_program_get(BPyGPUShader * /*self*/, void * /*closure*/)
830{
831 PyErr_WarnEx(
832 PyExc_DeprecationWarning, "'program' is deprecated. No valid handle will be returned.", 1);
833 return PyLong_FromLong(-1);
834}
835
836static PyGetSetDef pygpu_shader__tp_getseters[] = {
837 {"program",
839 (setter) nullptr,
840 pygpu_shader_program_doc,
841 nullptr},
842 {"name", (getter)pygpu_shader_name, (setter) nullptr, pygpu_shader_name_doc, nullptr},
843 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
844};
845
847{
848 if (self->is_builtin == false) {
849 GPU_shader_free(self->shader);
850 }
851 Py_TYPE(self)->tp_free((PyObject *)self);
852}
853
854PyTypeObject BPyGPUShader_Type = {
855 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
856 /*tp_name*/ "GPUShader",
857 /*tp_basicsize*/ sizeof(BPyGPUShader),
858 /*tp_itemsize*/ 0,
859 /*tp_dealloc*/ (destructor)pygpu_shader__tp_dealloc,
860 /*tp_vectorcall_offset*/ 0,
861 /*tp_getattr*/ nullptr,
862 /*tp_setattr*/ nullptr,
863 /*tp_as_async*/ nullptr,
864 /*tp_repr*/ nullptr,
865 /*tp_as_number*/ nullptr,
866 /*tp_as_sequence*/ nullptr,
867 /*tp_as_mapping*/ nullptr,
868 /*tp_hash*/ nullptr,
869 /*tp_call*/ nullptr,
870 /*tp_str*/ nullptr,
871 /*tp_getattro*/ nullptr,
872 /*tp_setattro*/ nullptr,
873 /*tp_as_buffer*/ nullptr,
874 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
875 /*tp_doc*/ nullptr,
876 /*tp_traverse*/ nullptr,
877 /*tp_clear*/ nullptr,
878 /*tp_richcompare*/ nullptr,
879 /*tp_weaklistoffset*/ 0,
880 /*tp_iter*/ nullptr,
881 /*tp_iternext*/ nullptr,
882 /*tp_methods*/ pygpu_shader__tp_methods,
883 /*tp_members*/ nullptr,
884 /*tp_getset*/ pygpu_shader__tp_getseters,
885 /*tp_base*/ nullptr,
886 /*tp_dict*/ nullptr,
887 /*tp_descr_get*/ nullptr,
888 /*tp_descr_set*/ nullptr,
889 /*tp_dictoffset*/ 0,
890 /*tp_init*/ nullptr,
891 /*tp_alloc*/ nullptr,
892 /*tp_new*/ nullptr,
893 /*tp_free*/ nullptr,
894 /*tp_is_gc*/ nullptr,
895 /*tp_bases*/ nullptr,
896 /*tp_mro*/ nullptr,
897 /*tp_cache*/ nullptr,
898 /*tp_subclasses*/ nullptr,
899 /*tp_weaklist*/ nullptr,
900 /*tp_del*/ nullptr,
901 /*tp_version_tag*/ 0,
902 /*tp_finalize*/ nullptr,
903 /*tp_vectorcall*/ nullptr,
904};
905
907
908/* -------------------------------------------------------------------- */
911
913 /* Wrap. */
914 pygpu_shader_unbind_doc,
915 ".. function:: unbind()\n"
916 "\n"
917 " Unbind the bound shader object.\n");
918static PyObject *pygpu_shader_unbind(BPyGPUShader * /*self*/)
919{
921 Py_RETURN_NONE;
922}
923
925 /* Wrap. */
926 pygpu_shader_from_builtin_doc,
927 ".. function:: from_builtin(shader_name, *, config='DEFAULT')\n"
928 "\n"
929 " Shaders that are embedded in the blender internal code (see :ref:`built-in-shaders`).\n"
930 " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n"
931 " which can be edited by the :mod:`gpu.matrix` module.\n"
932 "\n"
933 " You can also choose a shader configuration that uses clip_planes by setting the "
934 "``CLIPPED`` value to the config parameter. Note that in this case you also need to "
935 "manually set the value of ``mat4 ModelMatrix``.\n"
936 "\n"
937 " :arg shader_name: One of the builtin shader names.\n"
938 " :type shader_name: str\n"
939 " :arg config: One of these types of shader configuration:\n"
940 "\n"
941 " - ``DEFAULT``\n"
942 " - ``CLIPPED``\n"
943 " :type config: str\n"
944 " :return: Shader object corresponding to the given name.\n"
945 " :rtype: :class:`gpu.types.GPUShader`\n");
946static PyObject *pygpu_shader_from_builtin(PyObject * /*self*/, PyObject *args, PyObject *kwds)
947{
949
950 PyC_StringEnum pygpu_bultinshader = {pygpu_shader_builtin_items};
952
953 static const char *_keywords[] = {"shader_name", "config", nullptr};
954 static _PyArg_Parser _parser = {
956 "O&" /* `shader_name` */
957 "|$" /* Optional keyword only arguments. */
958 "O&" /* `config` */
959 ":from_builtin",
960 _keywords,
961 nullptr,
962 };
963 if (!_PyArg_ParseTupleAndKeywordsFast(args,
964 kwds,
965 &_parser,
967 &pygpu_bultinshader,
969 &pygpu_config))
970 {
971 return nullptr;
972 }
973
975 GPUBuiltinShader(pygpu_bultinshader.value_found), GPUShaderConfig(pygpu_config.value_found));
976
977 if (shader == nullptr) {
978 PyErr_SetString(PyExc_ValueError, "Builtin shader doesn't exist in the requested config");
979 return nullptr;
980 }
981
983}
984
986 /* Wrap. */
987 pygpu_shader_create_from_info_doc,
988 ".. function:: create_from_info(shader_info)\n"
989 "\n"
990 " Create shader from a GPUShaderCreateInfo.\n"
991 "\n"
992 " :arg shader_info: GPUShaderCreateInfo\n"
993 " :type shader_info: :class:`gpu.types.GPUShaderCreateInfo`\n"
994 " :return: Shader object corresponding to the given name.\n"
995 " :rtype: :class:`gpu.types.GPUShader`\n");
997{
999
1001 PyErr_Format(PyExc_TypeError, "Expected a GPUShaderCreateInfo, got %s", Py_TYPE(o)->tp_name);
1002 return nullptr;
1003 }
1004
1005 char error[128];
1007 PyErr_SetString(PyExc_Exception, error);
1008 return nullptr;
1009 }
1010
1012 if (!shader) {
1013 PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details");
1014 return nullptr;
1015 }
1016
1017 return BPyGPUShader_CreatePyObject(shader, false);
1018}
1019
1020#ifdef __GNUC__
1021# ifdef __clang__
1022# pragma clang diagnostic push
1023# pragma clang diagnostic ignored "-Wcast-function-type"
1024# else
1025# pragma GCC diagnostic push
1026# pragma GCC diagnostic ignored "-Wcast-function-type"
1027# endif
1028#endif
1029
1030static PyMethodDef pygpu_shader_module__tp_methods[] = {
1031 {"unbind", (PyCFunction)pygpu_shader_unbind, METH_NOARGS, pygpu_shader_unbind_doc},
1032 {"from_builtin",
1033 (PyCFunction)pygpu_shader_from_builtin,
1034 METH_VARARGS | METH_KEYWORDS,
1035 pygpu_shader_from_builtin_doc},
1036 {"create_from_info",
1037 (PyCFunction)pygpu_shader_create_from_info,
1038 METH_O,
1039 pygpu_shader_create_from_info_doc},
1040 {nullptr, nullptr, 0, nullptr},
1041};
1042
1043#ifdef __GNUC__
1044# ifdef __clang__
1045# pragma clang diagnostic pop
1046# else
1047# pragma GCC diagnostic pop
1048# endif
1049#endif
1050
1052 /* Wrap. */
1053 pygpu_shader_module__tp_doc,
1054 "This module provides access to GPUShader internal functions.\n"
1055 "\n"
1056 ".. _built-in-shaders:\n"
1057 "\n"
1058 ".. rubric:: Built-in shaders\n"
1059 "\n"
1060 "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n"
1061 "\n"
1062 "Its value must be modified using the :class:`gpu.matrix` module.\n"
1063 "\n"
1064 ".. important::\n"
1065 "\n"
1066 " Shader uniforms must be explicitly initialized to avoid retaining values from previous "
1067 "executions.\n"
1069static PyModuleDef pygpu_shader_module_def = {
1070 /*m_base*/ PyModuleDef_HEAD_INIT,
1071 /*m_name*/ "gpu.shader",
1072 /*m_doc*/ pygpu_shader_module__tp_doc,
1073 /*m_size*/ 0,
1074 /*m_methods*/ pygpu_shader_module__tp_methods,
1075 /*m_slots*/ nullptr,
1076 /*m_traverse*/ nullptr,
1077 /*m_clear*/ nullptr,
1078 /*m_free*/ nullptr,
1079};
1080
1082
1083/* -------------------------------------------------------------------- */
1086
1088{
1090
1091 self = PyObject_New(BPyGPUShader, &BPyGPUShader_Type);
1092 self->shader = shader;
1093 self->is_builtin = is_builtin;
1094
1095 return (PyObject *)self;
1096}
1097
1099{
1100 PyObject *submodule;
1101
1102 submodule = PyModule_Create(&pygpu_shader_module_def);
1103
1104 return submodule;
1105}
1106
1114
unsigned int uint
#define ELEM(...)
#define STREQ(a, b)
blender::gpu::Shader * GPU_shader_create_from_info_python(const GPUShaderCreateInfo *_info)
int GPU_shader_get_ubo_binding(blender::gpu::Shader *shader, const char *name)
int GPU_shader_get_uniform_block(blender::gpu::Shader *shader, const char *name)
void GPU_shader_free(blender::gpu::Shader *shader)
bool GPU_shader_get_attribute_info(const blender::gpu::Shader *shader, int attr_location, char r_name[256], int *r_type)
bool GPU_shader_get_ssbo_input_info(const blender::gpu::Shader *shader, int ssbo_location, char r_name[256])
int GPU_shader_get_sampler_binding(blender::gpu::Shader *shader, const char *name)
uint GPU_shader_get_attribute_len(const blender::gpu::Shader *shader)
uint GPU_shader_get_ssbo_input_len(const blender::gpu::Shader *shader)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_uniform_int_ex(blender::gpu::Shader *shader, int location, int length, int array_size, const int *value)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
const char * GPU_shader_get_name(blender::gpu::Shader *shader)
int GPU_shader_get_attribute(const blender::gpu::Shader *shader, const char *name)
int GPU_shader_get_uniform(blender::gpu::Shader *shader, const char *name)
bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128])
void GPU_shader_unbind()
void GPU_shader_uniform_float_ex(blender::gpu::Shader *shader, int location, int length, int array_size, const float *value)
blender::gpu::Shader * GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
GPUBuiltinShader
@ 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_IMAGE_SCENE_LINEAR_TO_REC709_SRGB
@ GPU_SHADER_3D_POINT_UNIFORM_COLOR
@ GPU_SHADER_3D_IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB
@ GPU_SHADER_3D_IMAGE_COLOR
@ GPU_SHADER_CFG_DEFAULT
@ GPU_SHADER_CFG_CLIPPED
blender::gpu::Shader * GPU_shader_get_builtin_shader_with_config(GPUBuiltinShader shader, GPUShaderConfig sh_cfg)
void GPU_texture_image_bind(blender::gpu::Texture *texture, int unit)
void GPU_texture_bind(blender::gpu::Texture *texture, int unit)
void GPU_uniformbuf_bind(blender::gpu::UniformBuf *ubo, int slot)
void GPU_vertformat_from_shader(GPUVertFormat *format, const blender::gpu::Shader *shader)
void GPU_vertformat_clear(GPUVertFormat *)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
PyObject * self
nullptr float
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:20
#define PYDOC_BUILTIN_SHADER_DESCRIPTION
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(blender::gpu::Shader *shader)
static PyObject * pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
static PyObject * pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
static int pygpu_shader_uniform_location_get(blender::gpu::Shader *shader, const char *name, const char *error_prefix)
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 void pygpu_shader__tp_dealloc(BPyGPUShader *self)
PyObject * BPyGPUShader_CreatePyObject(blender::gpu::Shader *shader, bool is_builtin)
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 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[]
const struct PyC_StringEnumItems pygpu_attrtype_items[]
#define BPyGPUShaderCreateInfo_Check(v)
PyTypeObject BPyGPUTexture_Type
PyTypeObject BPyGPUUniformBuf_Type
PyObject * BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt)
float length(VecOp< float, D >) RET
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:141
#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.
Py_DECREF(oname)
#define PY_ARG_PARSER_HEAD_COMPAT()
const char * name
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
PyObject_VAR_HEAD GPUShaderCreateInfo * info
PyObject_HEAD blender::gpu::Texture * tex
PyObject_HEAD blender::gpu::UniformBuf * ubo