[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