[Bf-blender-cvs] [cb03cc66006] blender2.8: PyAPI: gpu.matrix.push_pop context manager
Campbell Barton
noreply at git.blender.org
Sun Aug 20 11:41:46 CEST 2017
Commit: cb03cc6600601f209c441f1e1ce820c70c294fc1
Author: Campbell Barton
Date: Sun Aug 20 19:31:59 2017 +1000
Branches: blender2.8
https://developer.blender.org/rBcb03cc6600601f209c441f1e1ce820c70c294fc1
PyAPI: gpu.matrix.push_pop context manager
Avoid un-balanced push/pop usage (which can interfere with Blender's
internal state) using a context manager.
===================================================================
M source/blender/python/intern/gpu_py_matrix.c
===================================================================
diff --git a/source/blender/python/intern/gpu_py_matrix.c b/source/blender/python/intern/gpu_py_matrix.c
index ab40e4b9110..68b08dfb324 100644
--- a/source/blender/python/intern/gpu_py_matrix.c
+++ b/source/blender/python/intern/gpu_py_matrix.c
@@ -43,7 +43,52 @@
#undef USE_GPU_PY_MATRIX_API
/* -------------------------------------------------------------------- */
+/** \name Helper Functions
+ * \{ */
+
+static bool pygpu_stack_is_push_model_view_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ return false;
+ }
+ return true;
+}
+
+static bool pygpu_stack_is_push_projection_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ return false;
+ }
+ return true;
+}
+static bool pygpu_stack_is_pop_model_view_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_model_view() == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Minimum model-view stack depth reached");
+ return false;
+ }
+ return true;
+}
+
+static bool pygpu_stack_is_pop_projection_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_projection() == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Minimum projection stack depth reached");
+ return false;
+ }
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Manage Stack
* \{ */
@@ -54,9 +99,7 @@ PyDoc_STRVAR(pygpu_matrix_push_doc,
);
static PyObject *pygpu_matrix_push(PyObject *UNUSED(self))
{
- if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
- PyErr_SetString(PyExc_RuntimeError,
- "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ if (!pygpu_stack_is_push_model_view_ok_or_error()) {
return NULL;
}
gpuPushMatrix();
@@ -70,9 +113,7 @@ PyDoc_STRVAR(pygpu_matrix_pop_doc,
);
static PyObject *pygpu_matrix_pop(PyObject *UNUSED(self))
{
- if (GPU_matrix_stack_level_get_model_view() == 0) {
- PyErr_SetString(PyExc_RuntimeError,
- "Minimum model-view stack depth reached");
+ if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
return NULL;
}
gpuPopMatrix();
@@ -86,12 +127,9 @@ PyDoc_STRVAR(pygpu_matrix_push_projection_doc,
);
static PyObject *pygpu_matrix_push_projection(PyObject *UNUSED(self))
{
- if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
- PyErr_SetString(PyExc_RuntimeError,
- "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ if (!pygpu_stack_is_push_projection_ok_or_error()) {
return NULL;
}
-
gpuPushProjectionMatrix();
Py_RETURN_NONE;
}
@@ -103,9 +141,7 @@ PyDoc_STRVAR(pygpu_matrix_pop_projection_doc,
);
static PyObject *pygpu_matrix_pop_projection(PyObject *UNUSED(self))
{
- if (GPU_matrix_stack_level_get_projection() == 0) {
- PyErr_SetString(PyExc_RuntimeError,
- "Minimum projection stack depth reached");
+ if (!pygpu_stack_is_pop_projection_ok_or_error()) {
return NULL;
}
gpuPopProjectionMatrix();
@@ -115,7 +151,132 @@ static PyObject *pygpu_matrix_pop_projection(PyObject *UNUSED(self))
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Stack (Context Manager)
+ *
+ * Safer alternative to ensure balanced push/pop calls.
+ *
+ * \{ */
+
+typedef struct {
+ PyObject_HEAD /* required python macro */
+ int type;
+ int level;
+} BPy_GPU_MatrixStackContext;
+
+enum {
+ PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
+ PYGPU_MATRIX_TYPE_PROJECTION = 2,
+};
+static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self);
+static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *args);
+
+static PyMethodDef pygpu_matrix_stack_context_methods[] = {
+ {"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
+ {"__exit__", (PyCFunction)pygpu_matrix_stack_context_exit, METH_VARARGS},
+ {NULL}
+};
+
+static PyTypeObject pygpu_matrix_stack_context_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "GPUMatrixStackContext",
+ .tp_basicsize = sizeof(BPy_GPU_MatrixStackContext),
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = pygpu_matrix_stack_context_methods,
+};
+
+static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self)
+{
+ /* sanity - should never happen */
+ if (self->level != -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Already in use");
+ return NULL;
+ }
+
+ if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
+ if (!pygpu_stack_is_push_model_view_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushMatrix();
+ self->level = GPU_matrix_stack_level_get_model_view();
+ }
+ else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
+ if (!pygpu_stack_is_push_projection_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushProjectionMatrix();
+ self->level = GPU_matrix_stack_level_get_projection();
+ }
+ else {
+ BLI_assert(0);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *UNUSED(args))
+{
+ /* sanity - should never happen */
+ if (self->level == -1) {
+ fprintf(stderr, "Not yet in use\n");
+ goto finally;
+ }
+
+ if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
+ const int level = GPU_matrix_stack_level_get_model_view();
+ if (level != self->level) {
+ fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
+ }
+ if (level != 0) {
+ gpuPopMatrix();
+ }
+ }
+ else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
+ const int level = GPU_matrix_stack_level_get_projection();
+ if (level != self->level) {
+ fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
+ }
+ if (level != 0) {
+ gpuPopProjectionMatrix();
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
+finally:
+ Py_RETURN_NONE;
+}
+
+static PyObject *pygpu_matrix_push_pop_impl(int type)
+{
+ BPy_GPU_MatrixStackContext *ret = PyObject_New(BPy_GPU_MatrixStackContext, &pygpu_matrix_stack_context_Type);
+ ret->type = type;
+ ret->level = -1;
+ return (PyObject *)ret;
+}
+
+PyDoc_STRVAR(pygpu_matrix_push_pop_doc,
+"push_pop()\n"
+"\n"
+" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
+);
+static PyObject *pygpu_matrix_push_pop(PyObject *UNUSED(self))
+{
+ return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
+}
+
+PyDoc_STRVAR(pygpu_matrix_push_pop_projection_doc,
+"push_pop_projection()\n"
+"\n"
+" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
+);
+static PyObject *pygpu_matrix_push_pop_projection(PyObject *UNUSED(self))
+{
+ return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Manipulate State
* \{ */
@@ -207,7 +368,6 @@ static PyObject *pygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value)
/** \} */
/* -------------------------------------------------------------------- */
-
/** \name Write State
* \{ */
@@ -254,7 +414,6 @@ static PyObject *pygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *valu
/** \} */
/* -------------------------------------------------------------------- */
-
/** \name Read State
* \{ */
@@ -306,13 +465,10 @@ static PyObject *pygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
/** \} */
-
/* -------------------------------------------------------------------- */
-
/** \name Module
* \{ */
-
static struct PyMethodDef BPy_GPU_matrix_methods[] = {
/* Manage Stack */
{"push", (PyCFunction)pygpu_matrix_push,
@@ -325,6 +481,12 @@ static struct PyMethodDef BPy_GPU_matrix_methods[] = {
{"pop_projection", (PyCFunction)pygpu_matrix_pop_projection,
METH_NOARGS, pygpu_matrix_pop_projection_doc},
+ /* Stack (Context Manager) */
+ {"push_pop", (PyCFunction)pygpu_matrix_push_pop,
+ METH_NOARGS, pygpu_matrix_push_pop_doc},
+ {"push_pop_projection", (PyCFunction)pygpu_matrix_push_pop_projection,
+ METH_NOARGS, pygpu_matrix_push_pop_projection_doc},
+
/* Manipulate State */
{"multiply_matrix", (PyCFunction)pygpu_matrix_multiply_matrix,
METH_O, pygpu_matrix_multiply_matrix_doc},
@@ -380,6 +542,10 @@ PyObject *BPyInit_gpu_matrix(void)
submodule = PyModule_Create(&BPy_GPU_matrix_module_def);
+ if (PyType_Ready(&pygpu_matrix_stack_context_Type) < 0) {
+ return NULL;
+ }
+
return submodule;
}
More information about the Bf-blender-cvs
mailing list