Python GPU: Add new methods to port the code templates in the manual

This commit adds a new API tha allow to replace the bgl API in the exemples on:
https://docs.blender.org/api/current/gpu.html

**Overview (New API):**
```
gpu.state:      active_framebuffer_get
GPUFramebuffer: read_color
GPUOffscreen:   texture_color
```

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D11031
This commit is contained in:
Germano Cavalcante 2021-04-30 10:48:55 -03:00
parent 04b6296e81
commit 2510bd3a5f
Notes: blender-bot 2023-02-14 04:20:36 +01:00
Referenced by commit 048f1a1b8b, GPU: remove unused member from FrameBuffer
5 changed files with 145 additions and 1 deletions

View File

@ -487,7 +487,7 @@ void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb)
void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
{
BLI_assert(ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
unwrap(gpu_fb)->py_ref = py_ref;
}
#endif

View File

@ -109,6 +109,11 @@ class FrameBuffer {
void **py_ref = nullptr;
#endif
public:
/* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
* Points to BPyGPUFrameBuffer::fb */
void **ref = nullptr;
public:
FrameBuffer(const char *name);
virtual ~FrameBuffer();

View File

@ -37,6 +37,8 @@
#include "gpu_py.h"
#include "gpu_py_texture.h"
#include "gpu_py.h"
#include "gpu_py_buffer.h"
#include "gpu_py_framebuffer.h" /* own include */
/* -------------------------------------------------------------------- */
@ -455,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U
return ret;
}
PyDoc_STRVAR(
pygpu_framebuffer_read_color_doc,
".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
"\n"
" Read a block of pixels from the frame buffer.\n"
"\n"
" :param x, y: Lower left corner of a rectangular block of pixels.\n"
" :param xsize, ysize: Dimensions of the pixel rectangle.\n"
" :type x, y, xsize, ysize: int\n"
" :param channels: Number of components to read.\n"
" :type channels: int\n"
" :param slot: The framebuffer slot to read data from.\n"
" :type slot: int\n"
" :param format: The format that describes the content of a single channel.\n"
" Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
" :type type: str\n"
" :arg data: Optional Buffer object to fill with the pixels values.\n"
" :type data: :class:`gpu.types.Buffer`\n"
" :return: The Buffer with the read pixels.\n"
" :rtype: :class:`gpu.types.Buffer`\n");
static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
PyObject *args,
PyObject *kwds)
{
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
int x, y, w, h, channels;
uint slot;
struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8};
BPyGPUBuffer *py_buffer = NULL;
static const char *_keywords[] = {
"x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
&x,
&y,
&w,
&h,
&channels,
&slot,
PyC_ParseStringEnum,
&pygpu_dataformat,
&BPyGPU_BufferType,
&py_buffer)) {
return NULL;
}
if (!IN_RANGE_INCL(channels, 1, 4)) {
PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
return NULL;
}
if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
PyErr_SetString(PyExc_ValueError, "slot overflow");
return NULL;
}
if (py_buffer) {
if (pygpu_dataformat.value_found != py_buffer->format) {
PyErr_SetString(PyExc_AttributeError,
"the format of the buffer is different from that specified");
return NULL;
}
size_t size_curr = bpygpu_Buffer_size(py_buffer);
size_t size_expected = w * h * channels *
GPU_texture_dataformat_size(pygpu_dataformat.value_found);
if (size_curr < size_expected) {
PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
return NULL;
}
}
else {
py_buffer = BPyGPU_Buffer_CreatePyObject(
pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL);
BLI_assert(bpygpu_Buffer_size(py_buffer) ==
w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found));
}
GPU_framebuffer_read_color(self->fb,
x,
y,
w,
h,
channels,
(int)slot,
pygpu_dataformat.value_found,
py_buffer->buf.as_void);
return (PyObject *)py_buffer;
}
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@ -498,6 +594,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
(PyCFunction)pygpu_framebuffer_viewport_get,
METH_NOARGS,
pygpu_framebuffer_viewport_get_doc},
{"read_color",
(PyCFunction)pygpu_framebuffer_read_color,
METH_VARARGS | METH_KEYWORDS,
pygpu_framebuffer_read_color_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
@ -556,6 +656,10 @@ PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_refer
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb;
self->weak_reference = weak_reference;
BLI_assert(GPU_framebuffer_reference_get(fb) == NULL);
GPU_framebuffer_reference_set(fb, &self->fb);
#if GPU_USE_PY_REFERENCES
self->shared_reference = shared_reference;

View File

@ -53,6 +53,8 @@
#include "../generic/py_capi_utils.h"
#include "gpu_py.h"
#include "gpu_py_texture.h"
#include "gpu_py_offscreen.h" /* own include */
/* Define the free method to avoid breakage. */
@ -264,6 +266,18 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
"The color texture attached.\n"
"\n"
":type: :class:`gpu.types.GPUTexture`");
static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
GPU_texture_ref(texture);
return BPyGPUTexture_CreatePyObject(texture);
}
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
@ -385,6 +399,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
(setter)NULL,
pygpu_offscreen_color_texture_doc,
NULL},
{"texture_color",
(getter)pygpu_offscreen_texture_color_get,
(setter)NULL,
pygpu_offscreen_texture_color_doc,
NULL},
{"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
{"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */

View File

@ -25,11 +25,13 @@
#include <Python.h>
#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
#include "gpu_py_framebuffer.h"
#include "gpu_py_state.h" /* own include */
/* -------------------------------------------------------------------- */
@ -334,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb
Py_RETURN_NONE;
}
PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc,
".. function:: framebuffer_active_get(enable)\n"
"\n"
" Return the active framefuffer in context.\n");
static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self))
{
GPUFrameBuffer *fb = GPU_framebuffer_active_get();
return BPyGPUFrameBuffer_CreatePyObject(fb, true);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -396,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
(PyCFunction)pygpu_state_program_point_size_set,
METH_O,
pygpu_state_program_point_size_set_doc},
{"active_framebuffer_get",
(PyCFunction)pygpu_state_framebuffer_active_get,
METH_NOARGS,
pygpu_state_framebuffer_active_get_doc},
{NULL, NULL, 0, NULL},
};