Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGermano Cavalcante <germano.costa@ig.com.br>2021-02-17 16:48:08 +0300
committerGermano Cavalcante <germano.costa@ig.com.br>2021-02-17 18:27:19 +0300
commit4430e8a00810ca8df2fa20029c4cb8078e8cdbe6 (patch)
treeacea8dc3d311f94c7cd5eabd36371bc76ec06b3f /source/blender/python/gpu/gpu_py_offscreen.c
parentec8c09f2aafdc7c13bb27c0f74e9def83c0400c7 (diff)
Python: gpu module: add new submodules and types
This commit extends the gpu python API with: ``` gpu.types.Buffer #"__init__", "to_list" gpu.types.GPUTexture #"__init__", "clear", "read", "format" gpu.types.GPUFrameBuffer #"__init__", "bind", "clear", "is_bound", "viewport", ("__enter__", "__exit__" with "GPUFrameBufferStackContext") gpu.types.GPUUniformBuf #"__init__", "update" gpu.state #"blend_set", "blend_get", "depth_test_set", "depth_test_get", "depth_mask_set", "depth_mask_get", "viewport_set", "viewport_get", "line_width_set", "line_width_get", "point_size_set", "color_mask_set", "face_culling_set", "front_facing_set", "program_point_size_set" ``` Add these methods to existing objects: ``` gpu.types.GPUShader #"uniform_sample", "uniform_buffer" ``` Maniphest Tasks: T80481 Differential Revision: https://developer.blender.org/D8826
Diffstat (limited to 'source/blender/python/gpu/gpu_py_offscreen.c')
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c260
1 files changed, 161 insertions, 99 deletions
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 2431a1fca5b..a98d9649f6f 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -30,6 +30,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_global.h"
@@ -54,14 +55,23 @@
#include "gpu_py_api.h"
#include "gpu_py_offscreen.h" /* own include */
+/* Define the free method to avoid breakage. */
+#define BPYGPU_USE_GPUOBJ_FREE_METHOD
+
/* -------------------------------------------------------------------- */
/** \name GPUOffScreen Common Utilities
* \{ */
-static int pygpu_offscreen_valid_check(BPyGPUOffScreen *pygpu_ofs)
+static int pygpu_offscreen_valid_check(BPyGPUOffScreen *py_ofs)
{
- if (UNLIKELY(pygpu_ofs->ofs == NULL)) {
- PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
+ if (UNLIKELY(py_ofs->ofs == NULL)) {
+ PyErr_SetString(PyExc_ReferenceError,
+#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
+ "GPU offscreen was freed, no further access is valid"
+#else
+ "GPU offscreen: internal error"
+#endif
+ );
return -1;
}
return 0;
@@ -78,10 +88,129 @@ static int pygpu_offscreen_valid_check(BPyGPUOffScreen *pygpu_ofs)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Stack (Context Manager)
+ *
+ * Safer alternative to ensure balanced push/pop calls.
+ *
+ * \{ */
+
+typedef struct {
+ PyObject_HEAD /* required python macro */
+ BPyGPUOffScreen *py_offs;
+ int level;
+ bool is_explicitly_bound; /* Bound by "bind" method. */
+} OffScreenStackContext;
+
+static void pygpu_offscreen_stack_context__tp_dealloc(OffScreenStackContext *self)
+{
+ Py_DECREF(self->py_offs);
+ PyObject_DEL(self);
+}
+
+static PyObject *pygpu_offscreen_stack_context_enter(OffScreenStackContext *self)
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offs);
+
+ if (!self->is_explicitly_bound) {
+ if (self->level != -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Already in use");
+ return NULL;
+ }
+
+ GPU_offscreen_bind(self->py_offs->ofs, true);
+ self->level = GPU_framebuffer_stack_level_get();
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *pygpu_offscreen_stack_context_exit(OffScreenStackContext *self,
+ PyObject *UNUSED(args))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offs);
+
+ if (self->level == -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet in use\n");
+ return NULL;
+ }
+
+ const int level = GPU_framebuffer_stack_level_get();
+ if (level != self->level) {
+ PyErr_Format(
+ PyExc_RuntimeError, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
+ }
+
+ GPU_offscreen_unbind(self->py_offs->ofs, true);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef pygpu_offscreen_stack_context__tp_methods[] = {
+ {"__enter__", (PyCFunction)pygpu_offscreen_stack_context_enter, METH_NOARGS},
+ {"__exit__", (PyCFunction)pygpu_offscreen_stack_context_exit, METH_VARARGS},
+ {NULL},
+};
+
+static PyTypeObject PyGPUOffscreenStackContext_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUFrameBufferStackContext",
+ .tp_basicsize = sizeof(OffScreenStackContext),
+ .tp_dealloc = (destructor)pygpu_offscreen_stack_context__tp_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = pygpu_offscreen_stack_context__tp_methods,
+};
+
+PyDoc_STRVAR(pygpu_offscreen_bind_doc,
+ ".. function:: bind()\n"
+ "\n"
+ " Context manager to ensure balanced bind calls, even in the case of an error.\n");
+static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self)
+{
+ OffScreenStackContext *ret = PyObject_New(OffScreenStackContext,
+ &PyGPUOffscreenStackContext_Type);
+ ret->py_offs = self;
+ ret->level = -1;
+ ret->is_explicitly_bound = false;
+ Py_INCREF(self);
+
+ pygpu_offscreen_stack_context_enter(ret);
+ ret->is_explicitly_bound = true;
+
+ return (PyObject *)ret;
+}
+
+PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
+ ".. method:: unbind(restore=True)\n"
+ "\n"
+ " Unbind the offscreen object.\n"
+ "\n"
+ " :arg restore: Restore the OpenGL state, can only be used when the state has been "
+ "saved before.\n"
+ " :type restore: `bool`\n");
+static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
+{
+ bool restore = true;
+
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+
+ static const char *_keywords[] = {"restore", NULL};
+ static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
+ return NULL;
+ }
+
+ GPU_offscreen_unbind(self->ofs, restore);
+ GPU_apply_state();
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name GPUOffscreen Type
* \{ */
-static PyObject *pygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds)
+static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self),
+ PyObject *args,
+ PyObject *kwds)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
@@ -90,7 +219,7 @@ static PyObject *pygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args,
char err_out[256];
static const char *_keywords[] = {"width", "height", NULL};
- static _PyArg_Parser _parser = {"ii|i:GPUOffScreen.__new__", _keywords, 0};
+ static _PyArg_Parser _parser = {"ii:GPUOffScreen.__new__", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &width, &height)) {
return NULL;
}
@@ -99,7 +228,7 @@ static PyObject *pygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args,
ofs = GPU_offscreen_create(width, height, true, false, err_out);
}
else {
- strncpy(err_out, "No active GPU context found", 256);
+ STRNCPY(err_out, "No active GPU context found");
}
if (ofs == NULL) {
@@ -136,61 +265,6 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
}
PyDoc_STRVAR(
- pygpu_offscreen_bind_doc,
- ".. method:: bind(save=True)\n"
- "\n"
- " Bind the offscreen object.\n"
- " To make sure that the offscreen gets unbind whether an exception occurs or not,\n"
- " pack it into a `with` statement.\n"
- "\n"
- " :arg save: Save the current OpenGL state, so that it can be restored when unbinding.\n"
- " :type save: `bool`\n");
-static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
-{
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
- bool save = true;
-
- static const char *_keywords[] = {"save", NULL};
- static _PyArg_Parser _parser = {"|O&:bind", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &save)) {
- return NULL;
- }
-
- GPU_offscreen_bind(self->ofs, save);
- GPU_apply_state();
-
- self->is_saved = save;
- Py_INCREF(self);
-
- return (PyObject *)self;
-}
-
-PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
- ".. method:: unbind(restore=True)\n"
- "\n"
- " Unbind the offscreen object.\n"
- "\n"
- " :arg restore: Restore the OpenGL state, can only be used when the state has been "
- "saved before.\n"
- " :type restore: `bool`\n");
-static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
-{
- bool restore = true;
-
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
-
- static const char *_keywords[] = {"restore", NULL};
- static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
- return NULL;
- }
-
- GPU_offscreen_unbind(self->ofs, restore);
- GPU_apply_state();
- Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
"\n"
@@ -210,8 +284,8 @@ PyDoc_STRVAR(
" :type projection_matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
{
- MatrixObject *pygpu_mat_view, *pygpu_mat_projection;
- PyObject *pygpu_scene, *pygpu_view_layer, *pygpu_region, *pygpu_view3d;
+ MatrixObject *py_mat_view, *py_mat_projection;
+ PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
struct Depsgraph *depsgraph;
struct Scene *scene;
@@ -228,18 +302,18 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
- &pygpu_scene,
- &pygpu_view_layer,
- &pygpu_view3d,
- &pygpu_region,
+ &py_scene,
+ &py_view_layer,
+ &py_view3d,
+ &py_region,
Matrix_Parse4x4,
- &pygpu_mat_view,
+ &py_mat_view,
Matrix_Parse4x4,
- &pygpu_mat_projection) ||
- (!(scene = PyC_RNA_AsPointer(pygpu_scene, "Scene")) ||
- !(view_layer = PyC_RNA_AsPointer(pygpu_view_layer, "ViewLayer")) ||
- !(v3d = PyC_RNA_AsPointer(pygpu_view3d, "SpaceView3D")) ||
- !(region = PyC_RNA_AsPointer(pygpu_region, "Region")))) {
+ &py_mat_projection) ||
+ (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
+ !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
+ !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
+ !(region = PyC_RNA_AsPointer(py_region, "Region")))) {
return NULL;
}
@@ -262,8 +336,8 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
region,
GPU_offscreen_width(self->ofs),
GPU_offscreen_height(self->ofs),
- (float(*)[4])pygpu_mat_view->matrix,
- (float(*)[4])pygpu_mat_projection->matrix,
+ (float(*)[4])py_mat_view->matrix,
+ (float(*)[4])py_mat_projection->matrix,
true,
true,
"",
@@ -281,6 +355,7 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
Py_RETURN_NONE;
}
+#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_offscreen_free_doc,
".. method:: free()\n"
"\n"
@@ -294,17 +369,7 @@ static PyObject *pygpu_offscreen_free(BPyGPUOffScreen *self)
self->ofs = NULL;
Py_RETURN_NONE;
}
-
-static PyObject *pygpu_offscreen_bind_context_enter(BPyGPUOffScreen *UNUSED(self))
-{
- Py_RETURN_NONE;
-}
-
-static PyObject *pygpu_offscreen_bind_context_exit(BPyGPUOffScreen *self, PyObject *UNUSED(args))
-{
- GPU_offscreen_unbind(self->ofs, self->is_saved);
- Py_RETURN_NONE;
-}
+#endif
static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
{
@@ -314,7 +379,7 @@ static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
Py_TYPE(self)->tp_free((PyObject *)self);
}
-static PyGetSetDef pygpu_offscreen_getseters[] = {
+static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
{"color_texture",
(getter)pygpu_offscreen_color_texture_get,
(setter)NULL,
@@ -325,11 +390,8 @@ static PyGetSetDef pygpu_offscreen_getseters[] = {
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
-static struct PyMethodDef pygpu_offscreen_methods[] = {
- {"bind",
- (PyCFunction)pygpu_offscreen_bind,
- METH_VARARGS | METH_KEYWORDS,
- pygpu_offscreen_bind_doc},
+static struct PyMethodDef pygpu_offscreen__tp_methods[] = {
+ {"bind", (PyCFunction)pygpu_offscreen_bind, METH_NOARGS, pygpu_offscreen_bind_doc},
{"unbind",
(PyCFunction)pygpu_offscreen_unbind,
METH_VARARGS | METH_KEYWORDS,
@@ -338,13 +400,13 @@ static struct PyMethodDef pygpu_offscreen_methods[] = {
(PyCFunction)pygpu_offscreen_draw_view3d,
METH_VARARGS | METH_KEYWORDS,
pygpu_offscreen_draw_view3d_doc},
+#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
- {"__enter__", (PyCFunction)pygpu_offscreen_bind_context_enter, METH_NOARGS},
- {"__exit__", (PyCFunction)pygpu_offscreen_bind_context_exit, METH_VARARGS},
+#endif
{NULL, NULL, 0, NULL},
};
-PyDoc_STRVAR(pygpu_offscreen_doc,
+PyDoc_STRVAR(pygpu_offscreen__tp_doc,
".. class:: GPUOffScreen(width, height)\n"
"\n"
" This object gives access to off screen buffers.\n"
@@ -358,10 +420,10 @@ PyTypeObject BPyGPUOffScreen_Type = {
.tp_basicsize = sizeof(BPyGPUOffScreen),
.tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = pygpu_offscreen_doc,
- .tp_methods = pygpu_offscreen_methods,
- .tp_getset = pygpu_offscreen_getseters,
- .tp_new = pygpu_offscreen_new,
+ .tp_doc = pygpu_offscreen__tp_doc,
+ .tp_methods = pygpu_offscreen__tp_methods,
+ .tp_getset = pygpu_offscreen__tp_getseters,
+ .tp_new = pygpu_offscreen__tp_new,
};
/** \} */