Blender V4.5
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
14
15#include <Python.h>
16
17#include "GPU_context.hh"
18#include "GPU_framebuffer.hh"
19#include "GPU_init_exit.hh"
20
22#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
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/* -------------------------------------------------------------------- */
35
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
112
113/* -------------------------------------------------------------------- */
119
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#ifdef __GNUC__
173# ifdef __clang__
174# pragma clang diagnostic push
175# pragma clang diagnostic ignored "-Wcast-function-type"
176# else
177# pragma GCC diagnostic push
178# pragma GCC diagnostic ignored "-Wcast-function-type"
179# endif
180#endif
181
183 {"__enter__", (PyCFunction)pygpu_framebuffer_stack_context_enter, METH_NOARGS},
184 {"__exit__", (PyCFunction)pygpu_framebuffer_stack_context_exit, METH_VARARGS},
185 {nullptr},
186};
187
188#ifdef __GNUC__
189# ifdef __clang__
190# pragma clang diagnostic pop
191# else
192# pragma GCC diagnostic pop
193# endif
194#endif
195
196static PyTypeObject FramebufferStackContext_Type = {
197 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
198 /*tp_name*/ "GPUFrameBufferStackContext",
199 /*tp_basicsize*/ sizeof(PyFrameBufferStackContext),
200 /*tp_itemsize*/ 0,
201 /*tp_dealloc*/ (destructor)pygpu_framebuffer_stack_context__tp_dealloc,
202 /*tp_vectorcall_offset*/ 0,
203 /*tp_getattr*/ nullptr,
204 /*tp_setattr*/ nullptr,
205 /*tp_as_async*/ nullptr,
206 /*tp_repr*/ nullptr,
207 /*tp_as_number*/ nullptr,
208 /*tp_as_sequence*/ nullptr,
209 /*tp_as_mapping*/ nullptr,
210 /*tp_hash*/ nullptr,
211 /*tp_call*/ nullptr,
212 /*tp_str*/ nullptr,
213 /*tp_getattro*/ nullptr,
214 /*tp_setattro*/ nullptr,
215 /*tp_as_buffer*/ nullptr,
216 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
217 /*tp_doc*/ nullptr,
218 /*tp_traverse*/ nullptr,
219 /*tp_clear*/ nullptr,
220 /*tp_richcompare*/ nullptr,
221 /*tp_weaklistoffset*/ 0,
222 /*tp_iter*/ nullptr,
223 /*tp_iternext*/ nullptr,
225 /*tp_members*/ nullptr,
226 /*tp_getset*/ nullptr,
227 /*tp_base*/ nullptr,
228 /*tp_dict*/ nullptr,
229 /*tp_descr_get*/ nullptr,
230 /*tp_descr_set*/ nullptr,
231 /*tp_dictoffset*/ 0,
232 /*tp_init*/ nullptr,
233 /*tp_alloc*/ nullptr,
234 /*tp_new*/ nullptr,
235 /*tp_free*/ nullptr,
236 /*tp_is_gc*/ nullptr,
237 /*tp_bases*/ nullptr,
238 /*tp_mro*/ nullptr,
239 /*tp_cache*/ nullptr,
240 /*tp_subclasses*/ nullptr,
241 /*tp_weaklist*/ nullptr,
242 /*tp_del*/ nullptr,
243 /*tp_version_tag*/ 0,
244 /*tp_finalize*/ nullptr,
245 /*tp_vectorcall*/ nullptr,
246};
247
249 /* Wrap. */
250 pygpu_framebuffer_bind_doc,
251 ".. function:: bind()\n"
252 "\n"
253 " Context manager to ensure balanced bind calls, even in the case of an error.\n");
255{
258 ret->py_fb = self;
259 ret->level = -1;
260 Py_INCREF(self);
261 return (PyObject *)ret;
262}
263
265
266/* -------------------------------------------------------------------- */
269
270/* Fill in the GPUAttachment according to the PyObject parameter.
271 * PyObject *o can be nullptr, Py_None, BPyGPUTexture or a dictionary containing the keyword
272 * "texture" and the optional keywords "layer" and "mip". Returns false on error. In this case, a
273 * python message will be raised and GPUAttachment will not be touched. */
274static bool pygpu_framebuffer_new_parse_arg(PyObject *o, GPUAttachment *r_attach)
275{
277
278 if (!o || o == Py_None) {
279 /* Pass. */;
280 }
281 else if (BPyGPUTexture_Check(o)) {
282 if (!bpygpu_ParseTexture(o, &tmp_attach.tex)) {
283 return false;
284 }
285 }
286 else {
287 const char *c_texture = "texture";
288 const char *c_layer = "layer";
289 const char *c_mip = "mip";
290 PyObject *key, *value;
291 Py_ssize_t pos = 0;
292 while (PyDict_Next(o, &pos, &key, &value)) {
293 if (!PyUnicode_Check(key)) {
294 PyErr_SetString(PyExc_TypeError, "keywords must be strings");
295 return false;
296 }
297
298 if (c_texture && PyUnicode_CompareWithASCIIString(key, c_texture)) {
299 /* Compare only once. */
300 c_texture = nullptr;
301 if (!bpygpu_ParseTexture(value, &tmp_attach.tex)) {
302 return false;
303 }
304 }
305 else if (c_layer && PyUnicode_CompareWithASCIIString(key, c_layer)) {
306 /* Compare only once. */
307 c_layer = nullptr;
308 tmp_attach.layer = PyLong_AsLong(value);
309 if (tmp_attach.layer == -1 && PyErr_Occurred()) {
310 return false;
311 }
312 }
313 else if (c_mip && PyUnicode_CompareWithASCIIString(key, c_mip)) {
314 /* Compare only once. */
315 c_mip = nullptr;
316 tmp_attach.mip = PyLong_AsLong(value);
317 if (tmp_attach.mip == -1 && PyErr_Occurred()) {
318 return false;
319 }
320 }
321 else {
322 PyErr_Format(
323 PyExc_TypeError, "'%U' is an invalid keyword argument for this attribute", key);
324 return false;
325 }
326 }
327 }
328
329 *r_attach = tmp_attach;
330 return true;
331}
332
333static PyObject *pygpu_framebuffer__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
334{
336
337 if (!GPU_context_active_get()) {
338 PyErr_SetString(PyExc_RuntimeError, "No active GPU context found");
339 return nullptr;
340 }
341
342 PyObject *depth_attachment = nullptr;
343 PyObject *color_attachements = nullptr;
344 static const char *_keywords[] = {"depth_slot", "color_slots", nullptr};
345 static _PyArg_Parser _parser = {
347 "|$" /* Optional keyword only arguments. */
348 "O" /* `depth_slot` */
349 "O" /* `color_slots` */
350 ":GPUFrameBuffer.__new__",
351 _keywords,
352 nullptr,
353 };
354 if (!_PyArg_ParseTupleAndKeywordsFast(
355 args, kwds, &_parser, &depth_attachment, &color_attachements))
356 {
357 return nullptr;
358 }
359
360/* Keep in sync with #GPU_FB_MAX_COLOR_ATTACHMENT.
361 * TODO: share the define. */
362#define BPYGPU_FB_MAX_COLOR_ATTACHMENT 6
363
365
366 if (!pygpu_framebuffer_new_parse_arg(depth_attachment, &config[0])) {
367 return nullptr;
368 }
369 if (config[0].tex && !GPU_texture_has_depth_format(config[0].tex)) {
370 PyErr_SetString(PyExc_ValueError, "Depth texture with incompatible format");
371 return nullptr;
372 }
373
374 int color_attachements_len = 0;
375 if (color_attachements && color_attachements != Py_None) {
376 if (PySequence_Check(color_attachements)) {
377 color_attachements_len = PySequence_Size(color_attachements);
378 if (color_attachements_len > BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
379 PyErr_SetString(PyExc_AttributeError,
380 "too many attachments, max is " STRINGIFY(BPYGPU_FB_MAX_COLOR_ATTACHMENT));
381 return nullptr;
382 }
383
384 for (int i = 0; i < color_attachements_len; i++) {
385 PyObject *o = PySequence_GetItem(color_attachements, i);
386 bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
387 Py_DECREF(o);
388 if (!ok) {
389 return nullptr;
390 }
391 }
392 }
393 else {
394 if (!pygpu_framebuffer_new_parse_arg(color_attachements, &config[1])) {
395 return nullptr;
396 }
397 color_attachements_len = 1;
398 }
399 }
400
401 GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
402 GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
403
404 return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
405}
406
408 /* Wrap. */
409 pygpu_framebuffer_is_bound_doc,
410 "Checks if this is the active framebuffer in the context.");
411static PyObject *pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void * /*type*/)
412{
414 return PyBool_FromLong(GPU_framebuffer_bound(self->fb));
415}
416
418 /* Wrap. */
419 pygpu_framebuffer_clear_doc,
420 ".. method:: clear(color=None, depth=None, stencil=None)\n"
421 "\n"
422 " Fill color, depth and stencil textures with specific value.\n"
423 " Common values: color=(0.0, 0.0, 0.0, 1.0), depth=1.0, stencil=0.\n"
424 "\n"
425 " :arg color: Sequence of 3 or 4 floats representing ``(r, g, b, a)``.\n"
426 " :type color: Sequence[float]\n"
427 " :arg depth: depth value.\n"
428 " :type depth: float\n"
429 " :arg stencil: stencil value.\n"
430 " :type stencil: int\n");
431static PyObject *pygpu_framebuffer_clear(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
432{
434
435 if (!GPU_framebuffer_bound(self->fb)) {
436 return nullptr;
437 }
438
439 PyObject *py_col = nullptr;
440 PyObject *py_depth = nullptr;
441 PyObject *py_stencil = nullptr;
442
443 static const char *_keywords[] = {"color", "depth", "stencil", nullptr};
444 static _PyArg_Parser _parser = {
446 "|$" /* Optional keyword only arguments. */
447 "O" /* `color` */
448 "O" /* `depth` */
449 "O" /* `stencil` */
450 ":clear",
451 _keywords,
452 nullptr,
453 };
454 if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &py_col, &py_depth, &py_stencil)) {
455 return nullptr;
456 }
457
459 float col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
460 float depth = 1.0f;
461 uint stencil = 0;
462
463 if (py_col && py_col != Py_None) {
464 if (mathutils_array_parse(col, 3, 4, py_col, "GPUFrameBuffer.clear(), invalid 'color' arg") ==
465 -1)
466 {
467 return nullptr;
468 }
470 }
471
472 if (py_depth && py_depth != Py_None) {
473 depth = PyFloat_AsDouble(py_depth);
474 if (PyErr_Occurred()) {
475 return nullptr;
476 }
478 }
479
480 if (py_stencil && py_stencil != Py_None) {
481 if ((stencil = PyC_Long_AsU32(py_stencil)) == uint(-1)) {
482 return nullptr;
483 }
485 }
486
487 GPU_framebuffer_clear(self->fb, buffers, col, depth, stencil);
488 Py_RETURN_NONE;
489}
490
492 /* Wrap. */
493 pygpu_framebuffer_viewport_set_doc,
494 ".. function:: viewport_set(x, y, xsize, ysize)\n"
495 "\n"
496 " Set the viewport for this framebuffer object.\n"
497 " Note: The viewport state is not saved upon framebuffer rebind.\n"
498 "\n"
499 " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
500 " :type x, y: int\n"
501 " :arg xsize, ysize: width and height of the viewport_set.\n"
502 " :type xsize, ysize: int\n");
504 PyObject *args,
505 void * /*type*/)
506{
507 int x, y, xsize, ysize;
508 if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
509 return nullptr;
510 }
511
512 GPU_framebuffer_viewport_set(self->fb, x, y, xsize, ysize);
513 Py_RETURN_NONE;
514}
515
517 /* Wrap. */
518 pygpu_framebuffer_viewport_get_doc,
519 ".. function:: viewport_get()\n"
520 "\n"
521 " Returns position and dimension to current viewport.\n");
522static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void * /*type*/)
523{
525 int viewport[4];
526 GPU_framebuffer_viewport_get(self->fb, viewport);
527
528 PyObject *ret = PyTuple_New(4);
530 PyLong_FromLong(viewport[0]),
531 PyLong_FromLong(viewport[1]),
532 PyLong_FromLong(viewport[2]),
533 PyLong_FromLong(viewport[3]));
534 return ret;
535}
536
538 /* Wrap. */
539 pygpu_framebuffer_read_color_doc,
540 ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
541 "\n"
542 " Read a block of pixels from the frame buffer.\n"
543 "\n"
544 " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
545 " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
546 " :type x, y, xsize, ysize: int\n"
547 " :arg channels: Number of components to read.\n"
548 " :type channels: int\n"
549 " :arg slot: The framebuffer slot to read data from.\n"
550 " :type slot: int\n"
551 " :arg format: The format that describes the content of a single channel.\n"
552 " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
553 " :type format: str\n"
554 " :arg data: Optional Buffer object to fill with the pixels values.\n"
555 " :type data: :class:`gpu.types.Buffer`\n"
556 " :return: The Buffer with the read pixels.\n"
557 " :rtype: :class:`gpu.types.Buffer`\n");
559 PyObject *args,
560 PyObject *kwds)
561{
563 int x, y, w, h, channels;
564 uint slot;
566 BPyGPUBuffer *py_buffer = nullptr;
567
568 static const char *_keywords[] = {
569 "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", nullptr};
570 static _PyArg_Parser _parser = {
572 "i" /* `x` */
573 "i" /* `y` */
574 "i" /* `xsize` */
575 "i" /* `ysize` */
576 "i" /* `channels` */
577 "I" /* `slot` */
578 "O&" /* `format` */
579 "|$" /* Optional keyword only arguments. */
580 "O!" /* `data` */
581 ":read_color",
582 _keywords,
583 nullptr,
584 };
585 if (!_PyArg_ParseTupleAndKeywordsFast(args,
586 kwds,
587 &_parser,
588 &x,
589 &y,
590 &w,
591 &h,
592 &channels,
593 &slot,
595 &pygpu_dataformat,
597 &py_buffer))
598 {
599 return nullptr;
600 }
601
602 if (!IN_RANGE_INCL(channels, 1, 4)) {
603 PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
604 return nullptr;
605 }
606
607 if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
608 PyErr_SetString(PyExc_ValueError, "slot overflow");
609 return nullptr;
610 }
611
612 if (py_buffer) {
613 if (pygpu_dataformat.value_found != py_buffer->format) {
614 PyErr_SetString(PyExc_AttributeError,
615 "the format of the buffer is different from that specified");
616 return nullptr;
617 }
618
619 size_t size_curr = bpygpu_Buffer_size(py_buffer);
620 size_t size_expected = w * h * channels *
622 eGPUDataFormat(pygpu_dataformat.value_found));
623 if (size_curr < size_expected) {
624 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
625 return nullptr;
626 }
627 Py_INCREF(py_buffer);
628 }
629 else {
630 const Py_ssize_t shape[3] = {h, w, channels};
631 py_buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, 3, nullptr);
632 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
633 w * h * channels *
635 }
636
638 x,
639 y,
640 w,
641 h,
642 channels,
643 int(slot),
644 eGPUDataFormat(pygpu_dataformat.value_found),
645 py_buffer->buf.as_void);
646
647 return (PyObject *)py_buffer;
648}
649
651 /* Wrap. */
652 pygpu_framebuffer_read_depth_doc,
653 ".. function:: read_depth(x, y, xsize, ysize, data=data)\n"
654 "\n"
655 " Read a pixel depth block from the frame buffer.\n"
656 "\n"
657 " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
658 " :type x, y: int\n"
659 " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
660 " :type xsize, ysize: int\n"
661 " :arg data: Optional Buffer object to fill with the pixels values.\n"
662 " :type data: :class:`gpu.types.Buffer`\n"
663 " :return: The Buffer with the read pixels.\n"
664 " :rtype: :class:`gpu.types.Buffer`\n");
666 PyObject *args,
667 PyObject *kwds)
668{
670 int x, y, w, h;
671 BPyGPUBuffer *py_buffer = nullptr;
672
673 static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", nullptr};
674 static _PyArg_Parser _parser = {
676 "i" /* `x` */
677 "i" /* `y` */
678 "i" /* `xsize` */
679 "i" /* `ysize` */
680 "|$" /* Optional keyword only arguments. */
681 "O!" /* `data` */
682 ":read_depth",
683 _keywords,
684 nullptr,
685 };
686 if (!_PyArg_ParseTupleAndKeywordsFast(
687 args, kwds, &_parser, &x, &y, &w, &h, &BPyGPU_BufferType, &py_buffer))
688 {
689 return nullptr;
690 }
691
692 if (py_buffer) {
693 if (py_buffer->format != GPU_DATA_FLOAT) {
694 PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
695 return nullptr;
696 }
697
698 size_t size_curr = bpygpu_Buffer_size(py_buffer);
699 size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
700 if (size_curr < size_expected) {
701 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
702 return nullptr;
703 }
704 Py_INCREF(py_buffer);
705 }
706 else {
707 const Py_ssize_t shape[2] = {h, w};
708 py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, shape, 2, nullptr);
709 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
711 }
712
714
715 return (PyObject *)py_buffer;
716}
717
718#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
720 /* Wrap. */
721 pygpu_framebuffer_free_doc,
722 ".. method:: free()\n"
723 "\n"
724 " Free the framebuffer object.\n"
725 " The framebuffer will no longer be accessible.\n");
726static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
727{
730 Py_RETURN_NONE;
731}
732#endif
733
735{
737 Py_TYPE(self)->tp_free((PyObject *)self);
738}
739
740static PyGetSetDef pygpu_framebuffer__tp_getseters[] = {
741 {"is_bound",
743 (setter) nullptr,
744 pygpu_framebuffer_is_bound_doc,
745 nullptr},
746 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
747};
748
749#ifdef __GNUC__
750# ifdef __clang__
751# pragma clang diagnostic push
752# pragma clang diagnostic ignored "-Wcast-function-type"
753# else
754# pragma GCC diagnostic push
755# pragma GCC diagnostic ignored "-Wcast-function-type"
756# endif
757#endif
758
759static PyMethodDef pygpu_framebuffer__tp_methods[] = {
760 {"bind", (PyCFunction)pygpu_framebuffer_bind, METH_NOARGS, pygpu_framebuffer_bind_doc},
761 {"clear",
762 (PyCFunction)pygpu_framebuffer_clear,
763 METH_VARARGS | METH_KEYWORDS,
764 pygpu_framebuffer_clear_doc},
765 {"viewport_set",
767 METH_NOARGS,
768 pygpu_framebuffer_viewport_set_doc},
769 {"viewport_get",
771 METH_NOARGS,
772 pygpu_framebuffer_viewport_get_doc},
773 {"read_color",
774 (PyCFunction)pygpu_framebuffer_read_color,
775 METH_VARARGS | METH_KEYWORDS,
776 pygpu_framebuffer_read_color_doc},
777 {"read_depth",
778 (PyCFunction)pygpu_framebuffer_read_depth,
779 METH_VARARGS | METH_KEYWORDS,
780 pygpu_framebuffer_read_depth_doc},
781#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
782 {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
783#endif
784 {nullptr, nullptr, 0, nullptr},
785};
786
787#ifdef __GNUC__
788# ifdef __clang__
789# pragma clang diagnostic pop
790# else
791# pragma GCC diagnostic pop
792# endif
793#endif
794
795/* Ideally type aliases would de-duplicate: `GPUTexture | dict[str, int | GPUTexture]`
796 * in this doc-string. */
798 /* Wrap. */
799 pygpu_framebuffer__tp_doc,
800 ".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n"
801 "\n"
802 " This object gives access to framebuffer functionalities.\n"
803 " When a 'layer' is specified in a argument, a single layer of a 3D or array "
804 "texture is attached to the frame-buffer.\n"
805 " For cube map textures, layer is translated into a cube map face.\n"
806 "\n"
807 " :arg depth_slot: GPUTexture to attach or a `dict` containing keywords: "
808 "'texture', 'layer' and 'mip'.\n"
809 " :type depth_slot: :class:`gpu.types.GPUTexture` | dict[] | None\n"
810 " :arg color_slots: Tuple where each item can be a GPUTexture or a `dict` "
811 "containing keywords: 'texture', 'layer' and 'mip'.\n"
812 " :type color_slots: :class:`gpu.types.GPUTexture` | "
813 "dict[str, int | :class:`gpu.types.GPUTexture`] | "
814 "Sequence[:class:`gpu.types.GPUTexture` | dict[str, int | :class:`gpu.types.GPUTexture`]] | "
815 "None\n");
817 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
818 /*tp_name*/ "GPUFrameBuffer",
819 /*tp_basicsize*/ sizeof(BPyGPUFrameBuffer),
820 /*tp_itemsize*/ 0,
821 /*tp_dealloc*/ (destructor)BPyGPUFrameBuffer__tp_dealloc,
822 /*tp_vectorcall_offset*/ 0,
823 /*tp_getattr*/ nullptr,
824 /*tp_setattr*/ nullptr,
825 /*tp_as_async*/ nullptr,
826 /*tp_repr*/ nullptr,
827 /*tp_as_number*/ nullptr,
828 /*tp_as_sequence*/ nullptr,
829 /*tp_as_mapping*/ nullptr,
830 /*tp_hash*/ nullptr,
831 /*tp_call*/ nullptr,
832 /*tp_str*/ nullptr,
833 /*tp_getattro*/ nullptr,
834 /*tp_setattro*/ nullptr,
835 /*tp_as_buffer*/ nullptr,
836 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
837 /*tp_doc*/ pygpu_framebuffer__tp_doc,
838 /*tp_traverse*/ nullptr,
839 /*tp_clear*/ nullptr,
840 /*tp_richcompare*/ nullptr,
841 /*tp_weaklistoffset*/ 0,
842 /*tp_iter*/ nullptr,
843 /*tp_iternext*/ nullptr,
844 /*tp_methods*/ pygpu_framebuffer__tp_methods,
845 /*tp_members*/ nullptr,
847 /*tp_base*/ nullptr,
848 /*tp_dict*/ nullptr,
849 /*tp_descr_get*/ nullptr,
850 /*tp_descr_set*/ nullptr,
851 /*tp_dictoffset*/ 0,
852 /*tp_init*/ nullptr,
853 /*tp_alloc*/ nullptr,
854 /*tp_new*/ pygpu_framebuffer__tp_new,
855 /*tp_free*/ nullptr,
856 /*tp_is_gc*/ nullptr,
857 /*tp_bases*/ nullptr,
858 /*tp_mro*/ nullptr,
859 /*tp_cache*/ nullptr,
860 /*tp_subclasses*/ nullptr,
861 /*tp_weaklist*/ nullptr,
862 /*tp_del*/ nullptr,
863 /*tp_version_tag*/ 0,
864 /*tp_finalize*/ nullptr,
865 /*tp_vectorcall*/ nullptr,
866};
867
869
870/* -------------------------------------------------------------------- */
873
874PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
875{
877
878#ifndef GPU_NO_USE_PY_REFERENCES
879 if (shared_reference) {
881 if (ref) {
882 /* Retrieve BPyGPUFrameBuffer reference. */
884 BLI_assert(self->fb == fb);
885 Py_INCREF(self);
886 return (PyObject *)self;
887 }
888 }
889#else
890 UNUSED_VARS(shared_reference);
891#endif
892
894 self->fb = fb;
895
896#ifndef GPU_NO_USE_PY_REFERENCES
897 self->shared_reference = shared_reference;
898
901#endif
902
903 return (PyObject *)self;
904}
905
907
908#undef PYGPU_FRAMEBUFFER_CHECK_OBJ
#define BLI_assert(a)
Definition BLI_assert.h:46
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()
void GPU_framebuffer_read_color(GPUFrameBuffer *fb, int x, int y, int width, int height, int channels, int slot, eGPUDataFormat data_format, void *r_data)
void GPU_framebuffer_viewport_get(GPUFrameBuffer *fb, int r_viewport[4])
GPUFrameBuffer * GPU_framebuffer_create(const char *name)
GPUFrameBuffer * GPU_framebuffer_active_get()
void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int width, int height)
eGPUFrameBufferBits
@ GPU_DEPTH_BIT
@ GPU_STENCIL_BIT
@ GPU_COLOR_BIT
void ** GPU_framebuffer_py_reference_get(GPUFrameBuffer *fb)
void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, 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_py_reference_set(GPUFrameBuffer *fb, void **py_ref)
void GPU_framebuffer_free(GPUFrameBuffer *fb)
void GPU_framebuffer_bind(GPUFrameBuffer *fb)
void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_len)
void GPU_framebuffer_push(GPUFrameBuffer *fb)
GPUFrameBuffer * GPU_framebuffer_pop()
void GPU_framebuffer_clear(GPUFrameBuffer *fb, eGPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, unsigned int clear_stencil)
bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
bool GPU_is_init()
size_t GPU_texture_dataformat_size(eGPUDataFormat data_format)
eGPUDataFormat
@ GPU_DATA_FLOAT
@ GPU_RGBA8
bool GPU_texture_has_depth_format(const GPUTexture *texture)
PyObject * self
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define offsetof(t, d)
uint pos
uint col
#define printf(...)
PyC_StringEnumItems bpygpu_dataformat_items[]
Definition gpu_py.cc:40
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition gpu_py.hh:20
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:96
#define PyUnicode_CompareWithASCIIString
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::@156235326231167136112107117153043033043051263025 buf
PyObject_HEAD GPUFrameBuffer * fb
GPUTexture * tex
PyObject_HEAD BPyGPUFrameBuffer * py_fb
i
Definition text_draw.cc:230
char * buffers[2]