Blender V5.0
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
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
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) == 0) {
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) == 0) {
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) == 0) {
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 blender::gpu::FrameBuffer *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 frame-buffer 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) {
465 col, 3, 4, py_col, "gpu::FrameBuffer.clear(), invalid 'color' arg") == -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`` & "
553 "``10_11_11_REV``.\n"
554 " ``UINT_24_8`` is deprecated, use ``FLOAT`` instead.\n"
555 " :type format: str\n"
556 " :arg data: Optional Buffer object to fill with the pixels values.\n"
557 " :type data: :class:`gpu.types.Buffer`\n"
558 " :return: The Buffer with the read pixels.\n"
559 " :rtype: :class:`gpu.types.Buffer`\n");
561 PyObject *args,
562 PyObject *kwds)
563{
565 int x, y, w, h, channels;
566 uint slot;
567 PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items,
568 int(blender::gpu::TextureFormat::UNORM_8_8_8_8)};
569 BPyGPUBuffer *py_buffer = nullptr;
570
571 static const char *_keywords[] = {
572 "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", nullptr};
573 static _PyArg_Parser _parser = {
575 "i" /* `x` */
576 "i" /* `y` */
577 "i" /* `xsize` */
578 "i" /* `ysize` */
579 "i" /* `channels` */
580 "I" /* `slot` */
581 "O&" /* `format` */
582 "|$" /* Optional keyword only arguments. */
583 "O!" /* `data` */
584 ":read_color",
585 _keywords,
586 nullptr,
587 };
588 if (!_PyArg_ParseTupleAndKeywordsFast(args,
589 kwds,
590 &_parser,
591 &x,
592 &y,
593 &w,
594 &h,
595 &channels,
596 &slot,
598 &pygpu_dataformat,
600 &py_buffer))
601 {
602 return nullptr;
603 }
604
605 if (pygpu_dataformat.value_found == GPU_DATA_UINT_24_8_DEPRECATED) {
606 PyErr_WarnEx(PyExc_DeprecationWarning, "`UINT_24_8` is deprecated, use `FLOAT` instead", 1);
607 }
608
609 if (!IN_RANGE_INCL(channels, 1, 4)) {
610 PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
611 return nullptr;
612 }
613
614 if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
615 PyErr_SetString(PyExc_ValueError, "slot overflow");
616 return nullptr;
617 }
618
619 if (py_buffer) {
620 if (pygpu_dataformat.value_found != py_buffer->format) {
621 PyErr_SetString(PyExc_AttributeError,
622 "the format of the buffer is different from that specified");
623 return nullptr;
624 }
625
626 size_t size_curr = bpygpu_Buffer_size(py_buffer);
627 size_t size_expected = w * h * channels *
629 eGPUDataFormat(pygpu_dataformat.value_found));
630 if (size_curr < size_expected) {
631 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
632 return nullptr;
633 }
634 Py_INCREF(py_buffer);
635 }
636 else {
637 const Py_ssize_t shape[3] = {h, w, channels};
638 py_buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, 3, nullptr);
639 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
640 size_t(w) * size_t(h) * size_t(channels) *
642 }
643
645 x,
646 y,
647 w,
648 h,
649 channels,
650 int(slot),
651 eGPUDataFormat(pygpu_dataformat.value_found),
652 py_buffer->buf.as_void);
653
654 return (PyObject *)py_buffer;
655}
656
658 /* Wrap. */
659 pygpu_framebuffer_read_depth_doc,
660 ".. function:: read_depth(x, y, xsize, ysize, *, data=data)\n"
661 "\n"
662 " Read a pixel depth block from the frame buffer.\n"
663 "\n"
664 " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
665 " :type x, y: int\n"
666 " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
667 " :type xsize, ysize: int\n"
668 " :arg data: Optional Buffer object to fill with the pixels values.\n"
669 " :type data: :class:`gpu.types.Buffer`\n"
670 " :return: The Buffer with the read pixels.\n"
671 " :rtype: :class:`gpu.types.Buffer`\n");
673 PyObject *args,
674 PyObject *kwds)
675{
677 int x, y, w, h;
678 BPyGPUBuffer *py_buffer = nullptr;
679
680 static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", nullptr};
681 static _PyArg_Parser _parser = {
683 "i" /* `x` */
684 "i" /* `y` */
685 "i" /* `xsize` */
686 "i" /* `ysize` */
687 "|$" /* Optional keyword only arguments. */
688 "O!" /* `data` */
689 ":read_depth",
690 _keywords,
691 nullptr,
692 };
693 if (!_PyArg_ParseTupleAndKeywordsFast(
694 args, kwds, &_parser, &x, &y, &w, &h, &BPyGPU_BufferType, &py_buffer))
695 {
696 return nullptr;
697 }
698
699 if (py_buffer) {
700 if (py_buffer->format != GPU_DATA_FLOAT) {
701 PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
702 return nullptr;
703 }
704
705 size_t size_curr = bpygpu_Buffer_size(py_buffer);
706 size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
707 if (size_curr < size_expected) {
708 PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
709 return nullptr;
710 }
711 Py_INCREF(py_buffer);
712 }
713 else {
714 const Py_ssize_t shape[2] = {h, w};
715 py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, shape, 2, nullptr);
716 BLI_assert(bpygpu_Buffer_size(py_buffer) ==
718 }
719
721
722 return (PyObject *)py_buffer;
723}
724
725#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
727 /* Wrap. */
728 pygpu_framebuffer_free_doc,
729 ".. method:: free()\n"
730 "\n"
731 " Free the framebuffer object.\n"
732 " The framebuffer will no longer be accessible.\n");
733static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
734{
737 Py_RETURN_NONE;
738}
739#endif
740
742{
744 Py_TYPE(self)->tp_free((PyObject *)self);
745}
746
747static PyGetSetDef pygpu_framebuffer__tp_getseters[] = {
748 {"is_bound",
750 (setter) nullptr,
751 pygpu_framebuffer_is_bound_doc,
752 nullptr},
753 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
754};
755
756#ifdef __GNUC__
757# ifdef __clang__
758# pragma clang diagnostic push
759# pragma clang diagnostic ignored "-Wcast-function-type"
760# else
761# pragma GCC diagnostic push
762# pragma GCC diagnostic ignored "-Wcast-function-type"
763# endif
764#endif
765
766static PyMethodDef pygpu_framebuffer__tp_methods[] = {
767 {"bind", (PyCFunction)pygpu_framebuffer_bind, METH_NOARGS, pygpu_framebuffer_bind_doc},
768 {"clear",
769 (PyCFunction)pygpu_framebuffer_clear,
770 METH_VARARGS | METH_KEYWORDS,
771 pygpu_framebuffer_clear_doc},
772 {"viewport_set",
774 METH_NOARGS,
775 pygpu_framebuffer_viewport_set_doc},
776 {"viewport_get",
778 METH_NOARGS,
779 pygpu_framebuffer_viewport_get_doc},
780 {"read_color",
781 (PyCFunction)pygpu_framebuffer_read_color,
782 METH_VARARGS | METH_KEYWORDS,
783 pygpu_framebuffer_read_color_doc},
784 {"read_depth",
785 (PyCFunction)pygpu_framebuffer_read_depth,
786 METH_VARARGS | METH_KEYWORDS,
787 pygpu_framebuffer_read_depth_doc},
788#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
789 {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
790#endif
791 {nullptr, nullptr, 0, nullptr},
792};
793
794#ifdef __GNUC__
795# ifdef __clang__
796# pragma clang diagnostic pop
797# else
798# pragma GCC diagnostic pop
799# endif
800#endif
801
802/* Ideally type aliases would de-duplicate:
803 * `GPUTexture | dict[str, int | GPUTexture]` in this doc-string. */
805 /* Wrap. */
806 pygpu_framebuffer__tp_doc,
807 ".. class:: GPUFrameBuffer(*, depth_slot=None, color_slots=None)\n"
808 "\n"
809 " This object gives access to framebuffer functionalities.\n"
810 " When a 'layer' is specified in a argument, a single layer of a 3D or array "
811 "texture is attached to the frame-buffer.\n"
812 " For cube map textures, layer is translated into a cube map face.\n"
813 "\n"
814 " :arg depth_slot: GPUTexture to attach or a ``dict`` containing keywords: "
815 "'texture', 'layer' and 'mip'.\n"
816 " :type depth_slot: :class:`gpu.types.GPUTexture` | dict[] | None\n"
817 " :arg color_slots: Tuple where each item can be a GPUTexture or a ``dict`` "
818 "containing keywords: 'texture', 'layer' and 'mip'.\n"
819 " :type color_slots: :class:`gpu.types.GPUTexture` | "
820 "dict[str, int | :class:`gpu.types.GPUTexture`] | "
821 "Sequence[:class:`gpu.types.GPUTexture` | dict[str, int | "
822 ":class:`gpu.types.GPUTexture`]] | "
823 "None\n");
825 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
826 /*tp_name*/ "GPUFrameBuffer",
827 /*tp_basicsize*/ sizeof(BPyGPUFrameBuffer),
828 /*tp_itemsize*/ 0,
829 /*tp_dealloc*/ (destructor)BPyGPUFrameBuffer__tp_dealloc,
830 /*tp_vectorcall_offset*/ 0,
831 /*tp_getattr*/ nullptr,
832 /*tp_setattr*/ nullptr,
833 /*tp_as_async*/ nullptr,
834 /*tp_repr*/ nullptr,
835 /*tp_as_number*/ nullptr,
836 /*tp_as_sequence*/ nullptr,
837 /*tp_as_mapping*/ nullptr,
838 /*tp_hash*/ nullptr,
839 /*tp_call*/ nullptr,
840 /*tp_str*/ nullptr,
841 /*tp_getattro*/ nullptr,
842 /*tp_setattro*/ nullptr,
843 /*tp_as_buffer*/ nullptr,
844 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
845 /*tp_doc*/ pygpu_framebuffer__tp_doc,
846 /*tp_traverse*/ nullptr,
847 /*tp_clear*/ nullptr,
848 /*tp_richcompare*/ nullptr,
849 /*tp_weaklistoffset*/ 0,
850 /*tp_iter*/ nullptr,
851 /*tp_iternext*/ nullptr,
852 /*tp_methods*/ pygpu_framebuffer__tp_methods,
853 /*tp_members*/ nullptr,
855 /*tp_base*/ nullptr,
856 /*tp_dict*/ nullptr,
857 /*tp_descr_get*/ nullptr,
858 /*tp_descr_set*/ nullptr,
859 /*tp_dictoffset*/ 0,
860 /*tp_init*/ nullptr,
861 /*tp_alloc*/ nullptr,
862 /*tp_new*/ pygpu_framebuffer__tp_new,
863 /*tp_free*/ nullptr,
864 /*tp_is_gc*/ nullptr,
865 /*tp_bases*/ nullptr,
866 /*tp_mro*/ nullptr,
867 /*tp_cache*/ nullptr,
868 /*tp_subclasses*/ nullptr,
869 /*tp_weaklist*/ nullptr,
870 /*tp_del*/ nullptr,
871 /*tp_version_tag*/ 0,
872 /*tp_finalize*/ nullptr,
873 /*tp_vectorcall*/ nullptr,
874};
875
877
878/* -------------------------------------------------------------------- */
881
883{
885
886#ifndef GPU_NO_USE_PY_REFERENCES
887 if (shared_reference) {
889 if (ref) {
890 /* Retrieve BPyGPUFrameBuffer reference. */
892 BLI_assert(self->fb == fb);
893 Py_INCREF(self);
894 return (PyObject *)self;
895 }
896 }
897#else
898 UNUSED_VARS(shared_reference);
899#endif
900
902 self->fb = fb;
903
904#ifndef GPU_NO_USE_PY_REFERENCES
905 self->shared_reference = shared_reference;
906
909#endif
910
911 return (PyObject *)self;
912}
913
915
916#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()
blender::gpu::FrameBuffer * GPU_framebuffer_create(const char *name)
GPUFrameBufferBits
@ GPU_DEPTH_BIT
@ GPU_STENCIL_BIT
@ GPU_COLOR_BIT
void GPU_framebuffer_read_color(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height, int channels, int slot, eGPUDataFormat data_format, void *r_data)
void ** GPU_framebuffer_py_reference_get(blender::gpu::FrameBuffer *fb)
void GPU_framebuffer_py_reference_set(blender::gpu::FrameBuffer *fb, void **py_ref)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_free(blender::gpu::FrameBuffer *fb)
uint GPU_framebuffer_stack_level_get()
void GPU_framebuffer_viewport_set(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height)
blender::gpu::FrameBuffer * GPU_framebuffer_pop()
bool GPU_framebuffer_bound(blender::gpu::FrameBuffer *fb)
void GPU_framebuffer_read_depth(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height, eGPUDataFormat data_format, void *r_data)
void GPU_framebuffer_push(blender::gpu::FrameBuffer *fb)
void GPU_framebuffer_config_array(blender::gpu::FrameBuffer *fb, const GPUAttachment *config, int config_len)
void GPU_framebuffer_clear(blender::gpu::FrameBuffer *fb, GPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, unsigned int clear_stencil)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
void GPU_framebuffer_viewport_get(blender::gpu::FrameBuffer *fb, int r_viewport[4])
blender::gpu::FrameBuffer * GPU_framebuffer_active_get()
bool GPU_is_init()
size_t GPU_texture_dataformat_size(eGPUDataFormat data_format)
bool GPU_texture_has_depth_format(const blender::gpu::Texture *texture)
eGPUDataFormat
@ GPU_DATA_UINT_24_8_DEPRECATED
@ GPU_DATA_FLOAT
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
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 PyObject * pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *)
static bool pygpu_framebuffer_stack_push_and_bind_or_error(blender::gpu::FrameBuffer *fb)
static PyTypeObject FramebufferStackContext_Type
#define PYGPU_FRAMEBUFFER_CHECK_OBJ(bpygpu)
PyObject * BPyGPUFrameBuffer_CreatePyObject(blender::gpu::FrameBuffer *fb, bool shared_reference)
static void pygpu_framebuffer_stack_context__tp_dealloc(PyFrameBufferStackContext *self)
#define BPYGPU_FB_MAX_COLOR_ATTACHMENT
static PyObject * pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void *)
static void pygpu_framebuffer_free_if_possible(blender::gpu::FrameBuffer *fb)
static PyObject * pygpu_framebuffer_stack_context_enter(PyFrameBufferStackContext *self)
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 bool pygpu_framebuffer_stack_pop_and_restore_or_error(blender::gpu::FrameBuffer *fb)
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)
#define printf(...)
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
int PyC_ParseStringEnum(PyObject *o, void *p)
uint32_t PyC_Long_AsU32(PyObject *value)
header-only compatibility defines.
Py_DECREF(oname)
#define PY_ARG_PARSER_HEAD_COMPAT()
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
union BPyGPUBuffer::@121060215011127262115351146023070133300154361035 buf
PyObject_HEAD blender::gpu::FrameBuffer * fb
blender::gpu::Texture * tex
PyObject_HEAD BPyGPUFrameBuffer * py_fb
i
Definition text_draw.cc:230
char * buffers[2]