Blender V4.3
gpu_py_framebuffer.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
15#include <Python.h>
16
17#include "GPU_context.hh"
18#include "GPU_framebuffer.hh"
19#include "GPU_init_exit.hh"
20
24
26
27#include "gpu_py.hh"
28#include "gpu_py_buffer.hh"
29#include "gpu_py_framebuffer.hh" /* own include */
30#include "gpu_py_texture.hh"
31
32/* -------------------------------------------------------------------- */
37{
38 if (UNLIKELY(bpygpu_fb->fb == nullptr)) {
39 PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
40 return -1;
41 }
42 return 0;
43}
44
45#define PYGPU_FRAMEBUFFER_CHECK_OBJ(bpygpu) \
46 { \
47 if (UNLIKELY(pygpu_framebuffer_valid_check(bpygpu) == -1)) { \
48 return nullptr; \
49 } \
50 } \
51 ((void)0)
52
53static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
54{
55 if (GPU_is_init()) {
57 }
58 else {
59 printf("PyFramebuffer freed after the context has been destroyed.\n");
60 }
61}
62
64{
65 if (self->fb) {
66#ifndef GPU_NO_USE_PY_REFERENCES
68 if (!self->shared_reference)
69#endif
70 {
72 }
73
74 self->fb = nullptr;
75 }
76}
77
78/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
79#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
80
82{
84 PyErr_SetString(
85 PyExc_RuntimeError,
86 "Maximum framebuffer stack depth " STRINGIFY(GPU_PY_FRAMEBUFFER_STACK_LEN) " reached");
87 return false;
88 }
91 return true;
92}
93
95{
97 PyErr_SetString(PyExc_RuntimeError, "Minimum framebuffer stack depth reached");
98 return false;
99 }
100
101 if (fb && !GPU_framebuffer_bound(fb)) {
102 PyErr_SetString(PyExc_RuntimeError, "Framebuffer is not bound");
103 return false;
104 }
105
106 GPUFrameBuffer *fb_prev = GPU_framebuffer_pop();
107 GPU_framebuffer_bind(fb_prev);
108 return true;
109}
110
113/* -------------------------------------------------------------------- */
121 PyObject_HEAD /* Required Python macro. */
123 int level;
124};
125
127{
128 Py_DECREF(self->py_fb);
129 PyObject_DEL(self);
130}
131
133{
135
136 /* sanity - should never happen */
137 if (self->level != -1) {
138 PyErr_SetString(PyExc_RuntimeError, "Already in use");
139 return nullptr;
140 }
141
143 return nullptr;
144 }
145
147 Py_RETURN_NONE;
148}
149
151 PyObject * /*args*/)
152{
154
155 /* sanity - should never happen */
156 if (self->level == -1) {
157 fprintf(stderr, "Not yet in use\n");
158 return nullptr;
159 }
160
161 const int level = GPU_framebuffer_stack_level_get();
162 if (level != self->level) {
163 fprintf(stderr, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
164 }
165
167 return nullptr;
168 }
169 Py_RETURN_NONE;
170}
171
172#if (defined(__GNUC__) && !defined(__clang__))
173# pragma GCC diagnostic push
174# pragma GCC diagnostic ignored "-Wcast-function-type"
175#endif
176
178 {"__enter__", (PyCFunction)pygpu_framebuffer_stack_context_enter, METH_NOARGS},
179 {"__exit__", (PyCFunction)pygpu_framebuffer_stack_context_exit, METH_VARARGS},
180 {nullptr},
181};
182
183#if (defined(__GNUC__) && !defined(__clang__))
184# pragma GCC diagnostic pop
185#endif
186
187static PyTypeObject FramebufferStackContext_Type = {
188 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
189 /*tp_name*/ "GPUFrameBufferStackContext",
190 /*tp_basicsize*/ sizeof(PyFrameBufferStackContext),
191 /*tp_itemsize*/ 0,
192 /*tp_dealloc*/ (destructor)pygpu_framebuffer_stack_context__tp_dealloc,
193 /*tp_vectorcall_offset*/ 0,
194 /*tp_getattr*/ nullptr,
195 /*tp_setattr*/ nullptr,
196 /*tp_as_async*/ nullptr,
197 /*tp_repr*/ nullptr,
198 /*tp_as_number*/ nullptr,
199 /*tp_as_sequence*/ nullptr,
200 /*tp_as_mapping*/ nullptr,
201 /*tp_hash*/ nullptr,
202 /*tp_call*/ nullptr,
203 /*tp_str*/ nullptr,
204 /*tp_getattro*/ nullptr,
205 /*tp_setattro*/ nullptr,
206 /*tp_as_buffer*/ nullptr,
207 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
208 /*tp_doc*/ nullptr,
209 /*tp_traverse*/ nullptr,
210 /*tp_clear*/ nullptr,
211 /*tp_richcompare*/ nullptr,
212 /*tp_weaklistoffset*/ 0,
213 /*tp_iter*/ nullptr,
214 /*tp_iternext*/ nullptr,
216 /*tp_members*/ nullptr,
217 /*tp_getset*/ nullptr,
218 /*tp_base*/ nullptr,
219 /*tp_dict*/ nullptr,
220 /*tp_descr_get*/ nullptr,
221 /*tp_descr_set*/ nullptr,
222 /*tp_dictoffset*/ 0,
223 /*tp_init*/ nullptr,
224 /*tp_alloc*/ nullptr,
225 /*tp_new*/ nullptr,
226 /*tp_free*/ nullptr,
227 /*tp_is_gc*/ nullptr,
228 /*tp_bases*/ nullptr,
229 /*tp_mro*/ nullptr,
230 /*tp_cache*/ nullptr,
231 /*tp_subclasses*/ nullptr,
232 /*tp_weaklist*/ nullptr,
233 /*tp_del*/ nullptr,
234 /*tp_version_tag*/ 0,
235 /*tp_finalize*/ nullptr,
236 /*tp_vectorcall*/ nullptr,
237};
238
240 /* Wrap. */
241 pygpu_framebuffer_bind_doc,
242 ".. function:: bind()\n"
243 "\n"
244 " Context manager to ensure balanced bind calls, even in the case of an error.\n");
246{
249 ret->py_fb = self;
250 ret->level = -1;
251 Py_INCREF(self);
252 return (PyObject *)ret;
253}
254
257/* -------------------------------------------------------------------- */
261/* Fill in the GPUAttachment according to the PyObject parameter.
262 * PyObject *o can be nullptr, Py_None, BPyGPUTexture or a dictionary containing the keyword
263 * "texture" and the optional keywords "layer" and "mip". Returns false on error. In this case, a
264 * python message will be raised and GPUAttachment will not be touched. */
265static bool pygpu_framebuffer_new_parse_arg(PyObject *o, GPUAttachment *r_attach)
266{
268
269 if (!o || o == Py_None) {
270 /* Pass. */;
271 }
272 else if (BPyGPUTexture_Check(o)) {
273 if (!bpygpu_ParseTexture(o, &tmp_attach.tex)) {
274 return false;
275 }
276 }
277 else {
278 const char *c_texture = "texture";
279 const char *c_layer = "layer";
280 const char *c_mip = "mip";
281 PyObject *key, *value;
282 Py_ssize_t pos = 0;
283 while (PyDict_Next(o, &pos, &key, &value)) {
284 if (!PyUnicode_Check(key)) {
285 PyErr_SetString(PyExc_TypeError, "keywords must be strings");
286 return false;
287 }
288
289 if (c_texture && _PyUnicode_EqualToASCIIString(key, c_texture)) {
290 /* Compare only once. */
291 c_texture = nullptr;
292 if (!bpygpu_ParseTexture(value, &tmp_attach.tex)) {
293 return false;
294 }
295 }
296 else if (c_layer && _PyUnicode_EqualToASCIIString(key, c_layer)) {
297 /* Compare only once. */
298 c_layer = nullptr;
299 tmp_attach.layer = PyLong_AsLong(value);
300 if (tmp_attach.layer == -1 && PyErr_Occurred()) {
301 return false;
302 }
303 }
304 else if (c_mip && _PyUnicode_EqualToASCIIString(key, c_mip)) {
305 /* Compare only once. */
306 c_mip = nullptr;
307 tmp_attach.mip = PyLong_AsLong(value);
308 if (tmp_attach.mip == -1 && PyErr_Occurred()) {
309 return false;
310 }
311 }
312 else {
313 PyErr_Format(
314 PyExc_TypeError, "'%U' is an invalid keyword argument for this attribute", key);
315 return false;
316 }
317 }
318 }
319
320 *r_attach = tmp_attach;
321 return true;
322}
323
324static PyObject *pygpu_framebuffer__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
325{
327
328 if (!GPU_context_active_get()) {
329 PyErr_SetString(PyExc_RuntimeError, "No active GPU context found");
330 return nullptr;
331 }
332
333 PyObject *depth_attachment = nullptr;
334 PyObject *color_attachements = nullptr;
335 static const char *_keywords[] = {"depth_slot", "color_slots", nullptr};
336 static _PyArg_Parser _parser = {
338 "|$" /* Optional keyword only arguments. */
339 "O" /* `depth_slot` */
340 "O" /* `color_slots` */
341 ":GPUFrameBuffer.__new__",
342 _keywords,
343 nullptr,
344 };
345 if (!_PyArg_ParseTupleAndKeywordsFast(
346 args, kwds, &_parser, &depth_attachment, &color_attachements))
347 {
348 return nullptr;
349 }
350
351/* Keep in sync with #GPU_FB_MAX_COLOR_ATTACHMENT.
352 * TODO: share the define. */
353#define BPYGPU_FB_MAX_COLOR_ATTACHMENT 6
354
356
357 if (!pygpu_framebuffer_new_parse_arg(depth_attachment, &config[0])) {
358 return nullptr;
359 }
360 if (config[0].tex && !GPU_texture_has_depth_format(config[0].tex)) {
361 PyErr_SetString(PyExc_ValueError, "Depth texture with incompatible format");
362 return nullptr;
363 }
364
365 int color_attachements_len = 0;
366 if (color_attachements && color_attachements != Py_None) {
367 if (PySequence_Check(color_attachements)) {
368 color_attachements_len = PySequence_Size(color_attachements);
369 if (color_attachements_len > BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
370 PyErr_SetString(PyExc_AttributeError,
371 "too many attachments, max is " STRINGIFY(BPYGPU_FB_MAX_COLOR_ATTACHMENT));
372 return nullptr;
373 }
374
375 for (int i = 0; i < color_attachements_len; i++) {
376 PyObject *o = PySequence_GetItem(color_attachements, i);
377 bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
378 Py_DECREF(o);
379 if (!ok) {
380 return nullptr;
381 }
382 }
383 }
384 else {
385 if (!pygpu_framebuffer_new_parse_arg(color_attachements, &config[1])) {
386 return nullptr;
387 }
388 color_attachements_len = 1;
389 }
390 }
391
392 GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
393 GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
394
395 return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
396}
397
399 /* Wrap. */
400 pygpu_framebuffer_is_bound_doc,
401 "Checks if this is the active framebuffer in the context.");
402static PyObject *pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void * /*type*/)
403{
405 return PyBool_FromLong(GPU_framebuffer_bound(self->fb));
406}
407
409 /* Wrap. */
410 pygpu_framebuffer_clear_doc,
411 ".. method:: clear(color=None, depth=None, stencil=None)\n"
412 "\n"
413 " Fill color, depth and stencil textures with specific value.\n"
414 " Common values: color=(0.0, 0.0, 0.0, 1.0), depth=1.0, stencil=0.\n"
415 "\n"
416 " :arg color: Sequence of 3 or 4 floats representing ``(r, g, b, a)``.\n"
417 " :type color: Sequence[float]\n"
418 " :arg depth: depth value.\n"
419 " :type depth: float\n"
420 " :arg stencil: stencil value.\n"
421 " :type stencil: int\n");
422static PyObject *pygpu_framebuffer_clear(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
423{
425
426 if (!GPU_framebuffer_bound(self->fb)) {
427 return nullptr;
428 }
429
430 PyObject *py_col = nullptr;
431 PyObject *py_depth = nullptr;
432 PyObject *py_stencil = nullptr;
433
434 static const char *_keywords[] = {"color", "depth", "stencil", nullptr};
435 static _PyArg_Parser _parser = {
437 "|$" /* Optional keyword only arguments. */
438 "O" /* `color` */
439 "O" /* `depth` */
440 "O" /* `stencil` */
441 ":clear",
442 _keywords,
443 nullptr,
444 };
445 if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &py_col, &py_depth, &py_stencil)) {
446 return nullptr;
447 }
448
450 float col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
451 float depth = 1.0f;
452 uint stencil = 0;
453
454 if (py_col && py_col != Py_None) {
455 if (mathutils_array_parse(col, 3, 4, py_col, "GPUFrameBuffer.clear(), invalid 'color' arg") ==
456 -1)
457 {
458 return nullptr;
459 }
461 }
462
463 if (py_depth && py_depth != Py_None) {
464 depth = PyFloat_AsDouble(py_depth);
465 if (PyErr_Occurred()) {
466 return nullptr;
467 }
469 }
470
471 if (py_stencil && py_stencil != Py_None) {
472 if ((stencil = PyC_Long_AsU32(py_stencil)) == uint(-1)) {
473 return nullptr;
474 }
476 }
477
478 GPU_framebuffer_clear(self->fb, buffers, col, depth, stencil);
479 Py_RETURN_NONE;
480}
481
483 /* Wrap. */
484 pygpu_framebuffer_viewport_set_doc,
485 ".. function:: viewport_set(x, y, xsize, ysize)\n"
486 "\n"
487 " Set the viewport for this framebuffer object.\n"
488 " Note: The viewport state is not saved upon framebuffer rebind.\n"
489 "\n"
490 " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
491 " :type x, y: int\n"
492 " :arg xsize, ysize: width and height of the viewport_set.\n"
493 " :type xsize, ysize: int\n");
495 PyObject *args,
496 void * /*type*/)
497{
498 int x, y, xsize, ysize;
499 if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
500 return nullptr;
501 }
502
503 GPU_framebuffer_viewport_set(self->fb, x, y, xsize, ysize);
504 Py_RETURN_NONE;
505}
506
508 /* Wrap. */
509 pygpu_framebuffer_viewport_get_doc,
510 ".. function:: viewport_get()\n"
511 "\n"
512 " Returns position and dimension to current viewport.\n");
513static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void * /*type*/)
514{
516 int viewport[4];
517 GPU_framebuffer_viewport_get(self->fb, viewport);
518
519 PyObject *ret = PyTuple_New(4);
521 PyLong_FromLong(viewport[0]),
522 PyLong_FromLong(viewport[1]),
523 PyLong_FromLong(viewport[2]),
524 PyLong_FromLong(viewport[3]));
525 return ret;
526}
527
529 /* Wrap. */
530 pygpu_framebuffer_read_color_doc,
531 ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
532 "\n"
533 " Read a block of pixels from the frame buffer.\n"
534 "\n"
535 " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
536 " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
537 " :type x, y, xsize, ysize: int\n"
538 " :arg channels: Number of components to read.\n"
539 " :type channels: int\n"
540 " :arg slot: The framebuffer slot to read data from.\n"
541 " :type slot: int\n"
542 " :arg format: The format that describes the content of a single channel.\n"
543 " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
544 " :type format: str\n"
545 " :arg data: Optional Buffer object to fill with the pixels values.\n"
546 " :type data: :class:`gpu.types.Buffer`\n"
547 " :return: The Buffer with the read pixels.\n"
548 " :rtype: :class:`gpu.types.Buffer`\n");
550 PyObject *args,
551 PyObject *kwds)
552{
554 int x, y, w, h, channels;
555 uint slot;
557 BPyGPUBuffer *py_buffer = nullptr;
558
559 static const char *_keywords[] = {
560 "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", nullptr};
561 static _PyArg_Parser _parser = {
563 "i" /* `x` */
564 "i" /* `y` */
565 "i" /* `xsize` */
566 "i" /* `ysize` */
567 "i" /* `channels` */
568 "I" /* `slot` */
569 "O&" /* `format` */
570 "|$" /* Optional keyword only arguments. */
571 "O!" /* `data` */
572 ":read_color",
573 _keywords,
574 nullptr,
575 };
576 if (!_PyArg_ParseTupleAndKeywordsFast(args,
577 kwds,
578 &_parser,
579 &x,
580 &y,
581 &w,
582 &h,
583 &channels,
584 &slot,
586 &pygpu_dataformat,
588 &py_buffer))
589 {
590 return nullptr;
591 }
592
593 if (!IN_RANGE_INCL(channels, 1, 4)) {
594 PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
595 return nullptr;
596 }
597
598 if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
599 PyErr_SetString(PyExc_ValueError, "slot overflow");
600 return nullptr;
601 }
602
603 if (py_buffer) {
604 if (pygpu_dataformat.value_found != py_buffer->format) {
605 PyErr_SetString(PyExc_AttributeError,
606 "the format of the buffer is different from that specified");
607 return nullptr;
608 }
609
610 size_t size_curr = bpygpu_Buffer_size(py_buffer);
611 size_t size_expected = w * h * channels *
613 eGPUDataFormat(pygpu_dataformat.value_found));
614 if (size_curr < size_expected) {
615 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
616 return nullptr;
617 }
618 Py_INCREF(py_buffer);
619 }
620 else {
621 const Py_ssize_t shape[3] = {h, w, channels};
622 py_buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, 3, nullptr);
623 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
624 w * h * channels *
626 }
627
629 x,
630 y,
631 w,
632 h,
633 channels,
634 int(slot),
635 eGPUDataFormat(pygpu_dataformat.value_found),
636 py_buffer->buf.as_void);
637
638 return (PyObject *)py_buffer;
639}
640
642 /* Wrap. */
643 pygpu_framebuffer_read_depth_doc,
644 ".. function:: read_depth(x, y, xsize, ysize, data=data)\n"
645 "\n"
646 " Read a pixel depth block from the frame buffer.\n"
647 "\n"
648 " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
649 " :type x, y: int\n"
650 " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
651 " :type xsize, ysize: int\n"
652 " :arg data: Optional Buffer object to fill with the pixels values.\n"
653 " :type data: :class:`gpu.types.Buffer`\n"
654 " :return: The Buffer with the read pixels.\n"
655 " :rtype: :class:`gpu.types.Buffer`\n");
657 PyObject *args,
658 PyObject *kwds)
659{
661 int x, y, w, h;
662 BPyGPUBuffer *py_buffer = nullptr;
663
664 static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", nullptr};
665 static _PyArg_Parser _parser = {
667 "i" /* `x` */
668 "i" /* `y` */
669 "i" /* `xsize` */
670 "i" /* `ysize` */
671 "|$" /* Optional keyword only arguments. */
672 "O!" /* `data` */
673 ":read_depth",
674 _keywords,
675 nullptr,
676 };
677 if (!_PyArg_ParseTupleAndKeywordsFast(
678 args, kwds, &_parser, &x, &y, &w, &h, &BPyGPU_BufferType, &py_buffer))
679 {
680 return nullptr;
681 }
682
683 if (py_buffer) {
684 if (py_buffer->format != GPU_DATA_FLOAT) {
685 PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
686 return nullptr;
687 }
688
689 size_t size_curr = bpygpu_Buffer_size(py_buffer);
690 size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
691 if (size_curr < size_expected) {
692 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
693 return nullptr;
694 }
695 Py_INCREF(py_buffer);
696 }
697 else {
698 const Py_ssize_t shape[2] = {h, w};
699 py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, shape, 2, nullptr);
700 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
702 }
703
704 GPU_framebuffer_read_depth(self->fb, x, y, w, h, GPU_DATA_FLOAT, py_buffer->buf.as_void);
705
706 return (PyObject *)py_buffer;
707}
708
709#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
711 /* Wrap. */
712 pygpu_framebuffer_free_doc,
713 ".. method:: free()\n"
714 "\n"
715 " Free the framebuffer object.\n"
716 " The framebuffer will no longer be accessible.\n");
717static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
718{
721 Py_RETURN_NONE;
722}
723#endif
724
726{
728 Py_TYPE(self)->tp_free((PyObject *)self);
729}
730
731static PyGetSetDef pygpu_framebuffer__tp_getseters[] = {
732 {"is_bound",
734 (setter) nullptr,
735 pygpu_framebuffer_is_bound_doc,
736 nullptr},
737 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
738};
739
740#if (defined(__GNUC__) && !defined(__clang__))
741# pragma GCC diagnostic push
742# pragma GCC diagnostic ignored "-Wcast-function-type"
743#endif
744
745static PyMethodDef pygpu_framebuffer__tp_methods[] = {
746 {"bind", (PyCFunction)pygpu_framebuffer_bind, METH_NOARGS, pygpu_framebuffer_bind_doc},
747 {"clear",
748 (PyCFunction)pygpu_framebuffer_clear,
749 METH_VARARGS | METH_KEYWORDS,
750 pygpu_framebuffer_clear_doc},
751 {"viewport_set",
753 METH_NOARGS,
754 pygpu_framebuffer_viewport_set_doc},
755 {"viewport_get",
757 METH_NOARGS,
758 pygpu_framebuffer_viewport_get_doc},
759 {"read_color",
760 (PyCFunction)pygpu_framebuffer_read_color,
761 METH_VARARGS | METH_KEYWORDS,
762 pygpu_framebuffer_read_color_doc},
763 {"read_depth",
764 (PyCFunction)pygpu_framebuffer_read_depth,
765 METH_VARARGS | METH_KEYWORDS,
766 pygpu_framebuffer_read_depth_doc},
767#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
768 {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
769#endif
770 {nullptr, nullptr, 0, nullptr},
771};
772
773#if (defined(__GNUC__) && !defined(__clang__))
774# pragma GCC diagnostic pop
775#endif
776
777/* Ideally type aliases would de-duplicate: `GPUTexture | dict[str, int | GPUTexture]`
778 * in this doc-string. */
780 /* Wrap. */
781 pygpu_framebuffer__tp_doc,
782 ".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n"
783 "\n"
784 " This object gives access to framebuffer functionalities.\n"
785 " When a 'layer' is specified in a argument, a single layer of a 3D or array "
786 "texture is attached to the frame-buffer.\n"
787 " For cube map textures, layer is translated into a cube map face.\n"
788 "\n"
789 " :arg depth_slot: GPUTexture to attach or a `dict` containing keywords: "
790 "'texture', 'layer' and 'mip'.\n"
791 " :type depth_slot: :class:`gpu.types.GPUTexture` | dict[] | None\n"
792 " :arg color_slots: Tuple where each item can be a GPUTexture or a `dict` "
793 "containing keywords: 'texture', 'layer' and 'mip'.\n"
794 " :type color_slots: :class:`gpu.types.GPUTexture` | "
795 "dict[str, int | :class:`gpu.types.GPUTexture`] | "
796 "Sequence[:class:`gpu.types.GPUTexture` | dict[str, int | :class:`gpu.types.GPUTexture`]] | "
797 "None\n");
799 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
800 /*tp_name*/ "GPUFrameBuffer",
801 /*tp_basicsize*/ sizeof(BPyGPUFrameBuffer),
802 /*tp_itemsize*/ 0,
803 /*tp_dealloc*/ (destructor)BPyGPUFrameBuffer__tp_dealloc,
804 /*tp_vectorcall_offset*/ 0,
805 /*tp_getattr*/ nullptr,
806 /*tp_setattr*/ nullptr,
807 /*tp_as_async*/ nullptr,
808 /*tp_repr*/ nullptr,
809 /*tp_as_number*/ nullptr,
810 /*tp_as_sequence*/ nullptr,
811 /*tp_as_mapping*/ nullptr,
812 /*tp_hash*/ nullptr,
813 /*tp_call*/ nullptr,
814 /*tp_str*/ nullptr,
815 /*tp_getattro*/ nullptr,
816 /*tp_setattro*/ nullptr,
817 /*tp_as_buffer*/ nullptr,
818 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
819 /*tp_doc*/ pygpu_framebuffer__tp_doc,
820 /*tp_traverse*/ nullptr,
821 /*tp_clear*/ nullptr,
822 /*tp_richcompare*/ nullptr,
823 /*tp_weaklistoffset*/ 0,
824 /*tp_iter*/ nullptr,
825 /*tp_iternext*/ nullptr,
826 /*tp_methods*/ pygpu_framebuffer__tp_methods,
827 /*tp_members*/ nullptr,
829 /*tp_base*/ nullptr,
830 /*tp_dict*/ nullptr,
831 /*tp_descr_get*/ nullptr,
832 /*tp_descr_set*/ nullptr,
833 /*tp_dictoffset*/ 0,
834 /*tp_init*/ nullptr,
835 /*tp_alloc*/ nullptr,
836 /*tp_new*/ pygpu_framebuffer__tp_new,
837 /*tp_free*/ nullptr,
838 /*tp_is_gc*/ nullptr,
839 /*tp_bases*/ nullptr,
840 /*tp_mro*/ nullptr,
841 /*tp_cache*/ nullptr,
842 /*tp_subclasses*/ nullptr,
843 /*tp_weaklist*/ nullptr,
844 /*tp_del*/ nullptr,
845 /*tp_version_tag*/ 0,
846 /*tp_finalize*/ nullptr,
847 /*tp_vectorcall*/ nullptr,
848};
849
852/* -------------------------------------------------------------------- */
856PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
857{
859
860#ifndef GPU_NO_USE_PY_REFERENCES
861 if (shared_reference) {
863 if (ref) {
864 /* Retrieve BPyGPUFrameBuffer reference. */
866 BLI_assert(self->fb == fb);
867 Py_INCREF(self);
868 return (PyObject *)self;
869 }
870 }
871#else
872 UNUSED_VARS(shared_reference);
873#endif
874
876 self->fb = fb;
877
878#ifndef GPU_NO_USE_PY_REFERENCES
879 self->shared_reference = shared_reference;
880
883#endif
884
885 return (PyObject *)self;
886}
887
890#undef PYGPU_FRAMEBUFFER_CHECK_OBJ
#define BLI_assert(a)
Definition BLI_assert.h:50
unsigned int uint
#define UNUSED_VARS(...)
#define STRINGIFY(x)
#define UNLIKELY(x)
#define POINTER_OFFSET(v, ofs)
#define IN_RANGE_INCL(a, b, c)
GPUContext * GPU_context_active_get()
GPUFrameBuffer * GPU_framebuffer_create(const char *name)
void GPU_framebuffer_push(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_config_array(GPUFrameBuffer *framebuffer, const GPUAttachment *config, int config_len)
GPUFrameBuffer * GPU_framebuffer_active_get()
void GPU_framebuffer_viewport_get(GPUFrameBuffer *framebuffer, int r_viewport[4])
eGPUFrameBufferBits
@ GPU_DEPTH_BIT
@ GPU_STENCIL_BIT
@ GPU_COLOR_BIT
bool GPU_framebuffer_bound(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_py_reference_set(GPUFrameBuffer *framebuffer, void **py_ref)
void ** GPU_framebuffer_py_reference_get(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_read_depth(GPUFrameBuffer *framebuffer, int x, int y, int width, int height, eGPUDataFormat data_format, void *r_data)
#define GPU_ATTACHMENT_NONE
uint GPU_framebuffer_stack_level_get()
void GPU_framebuffer_clear(GPUFrameBuffer *framebuffer, eGPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, unsigned int clear_stencil)
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_read_color(GPUFrameBuffer *framebuffer, int x, int y, int width, int height, int channels, int slot, eGPUDataFormat data_format, void *r_data)
void GPU_framebuffer_viewport_set(GPUFrameBuffer *framebuffer, int x, int y, int width, int height)
GPUFrameBuffer * GPU_framebuffer_pop()
void GPU_framebuffer_free(GPUFrameBuffer *framebuffer)
bool GPU_is_init()
size_t GPU_texture_dataformat_size(eGPUDataFormat data_format)
eGPUDataFormat
@ GPU_DATA_FLOAT
bool GPU_texture_has_depth_format(const GPUTexture *texture)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
PyObject * self
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define printf
#define offsetof(t, d)
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
uint col
PyC_StringEnumItems bpygpu_dataformat_items[]
Definition gpu_py.cc:40
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:18
PyTypeObject BPyGPU_BufferType
BPyGPUBuffer * BPyGPU_Buffer_CreatePyObject(const int format, const Py_ssize_t *shape, const int shape_len, void *buffer)
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
static bool pygpu_framebuffer_stack_pop_and_restore_or_error(GPUFrameBuffer *fb)
static PyObject * pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *)
static PyTypeObject FramebufferStackContext_Type
#define PYGPU_FRAMEBUFFER_CHECK_OBJ(bpygpu)
static void pygpu_framebuffer_stack_context__tp_dealloc(PyFrameBufferStackContext *self)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
#define BPYGPU_FB_MAX_COLOR_ATTACHMENT
static PyObject * pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void *)
static bool pygpu_framebuffer_stack_push_and_bind_or_error(GPUFrameBuffer *fb)
static PyObject * pygpu_framebuffer_stack_context_enter(PyFrameBufferStackContext *self)
PyObject * BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
static PyObject * pygpu_framebuffer_clear(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
static PyMethodDef pygpu_framebuffer__tp_methods[]
#define GPU_PY_FRAMEBUFFER_STACK_LEN
static PyObject * pygpu_framebuffer_stack_context_exit(PyFrameBufferStackContext *self, PyObject *)
static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
static PyObject * pygpu_framebuffer_bind(BPyGPUFrameBuffer *self)
static PyObject * pygpu_framebuffer__tp_new(PyTypeObject *, PyObject *args, PyObject *kwds)
static PyObject * pygpu_framebuffer_viewport_set(BPyGPUFrameBuffer *self, PyObject *args, void *)
static bool pygpu_framebuffer_new_parse_arg(PyObject *o, GPUAttachment *r_attach)
static PyGetSetDef pygpu_framebuffer__tp_getseters[]
PyDoc_STRVAR(pygpu_framebuffer_bind_doc, ".. function:: bind()\n" "\n" " Context manager to ensure balanced bind calls, even in the case of an error.\n")
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
static PyMethodDef pygpu_framebuffer_stack_context__tp_methods[]
static PyObject * pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
PyTypeObject BPyGPUFrameBuffer_Type
static PyObject * pygpu_framebuffer_read_depth(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
int bpygpu_ParseTexture(PyObject *o, void *p)
#define BPyGPUTexture_Check(v)
BLI_INLINE float fb(float length, float L)
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:97
int PyC_ParseStringEnum(PyObject *o, void *p)
uint32_t PyC_Long_AsU32(PyObject *value)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
union BPyGPUBuffer::@1342 buf
PyObject_HEAD GPUFrameBuffer * fb
GPUTexture * tex
PyObject_HEAD BPyGPUFrameBuffer * py_fb
char * buffers[2]