diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index 219c7b1c6c1..4e136ed34b2 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -113,6 +113,16 @@ const float (*gpuGetNormalMatrixInverse(float m[3][3]))[3]; void gpuBindMatrices(const Gwn_ShaderInterface*); bool gpuMatricesDirty(void); /* since last bind */ + +/* Python API needs to be able to inspect the stack so errors raise exceptions instead of crashing. */ +#ifdef USE_GPU_PY_MATRIX_API +int GPU_matrix_stack_level_get_model_view(void); +int GPU_matrix_stack_level_get_projection(void); +/* static assert ensures this doesn't change! */ +#define GPU_PY_MATRIX_STACK_LEN 31 +#endif /* USE_GPU_PY_MATRIX_API */ + + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c index f03a076a9fd..7f024a18ba7 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.c @@ -30,7 +30,9 @@ */ #define SUPPRESS_GENERIC_MATRIX_API +#define USE_GPU_PY_MATRIX_API /* only so values are declared */ #include "GPU_matrix.h" +#undef USE_GPU_PY_MATRIX_API #include "BLI_math_matrix.h" #include "BLI_math_rotation.h" @@ -622,3 +624,24 @@ bool gpuMatricesDirty(void) { return state.dirty; } + + +/* -------------------------------------------------------------------- */ + +/** \name Python API Helpers + * \{ */ +BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch"); + +/* Return int since caller is may subtract. */ + +int GPU_matrix_stack_level_get_model_view(void) +{ + return (int)state.model_view_stack.top; +} + +int GPU_matrix_stack_level_get_projection(void) +{ + return (int)state.projection_stack.top; +} + +/** \} */ diff --git a/source/blender/python/intern/gpu_py_matrix.c b/source/blender/python/intern/gpu_py_matrix.c index 74d49a771c5..ab40e4b9110 100644 --- a/source/blender/python/intern/gpu_py_matrix.c +++ b/source/blender/python/intern/gpu_py_matrix.c @@ -22,6 +22,9 @@ * \ingroup pythonintern * * This file defines the gpu.matrix stack API. + * + * \warning While these functions attempt to ensure correct stack usage. + * Mixing Python and C functions may still crash on invalid use. */ #include @@ -35,7 +38,9 @@ #include "gpu.h" +#define USE_GPU_PY_MATRIX_API #include "GPU_matrix.h" +#undef USE_GPU_PY_MATRIX_API /* -------------------------------------------------------------------- */ @@ -45,10 +50,15 @@ PyDoc_STRVAR(pygpu_matrix_push_doc, "push()\n" "\n" -" Add to the matrix stack.\n" +" Add to the model-view matrix stack.\n" ); 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"); + return NULL; + } gpuPushMatrix(); Py_RETURN_NONE; } @@ -56,10 +66,15 @@ static PyObject *pygpu_matrix_push(PyObject *UNUSED(self)) PyDoc_STRVAR(pygpu_matrix_pop_doc, "pop()\n" "\n" -" Remove the last matrix from the stack.\n" +" Remove the last model-view matrix from the stack.\n" ); 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"); + return NULL; + } gpuPopMatrix(); Py_RETURN_NONE; } @@ -71,7 +86,13 @@ PyDoc_STRVAR(pygpu_matrix_push_projection_doc, ); static PyObject *pygpu_matrix_push_projection(PyObject *UNUSED(self)) { - gpuPushMatrix(); + 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 NULL; + } + + gpuPushProjectionMatrix(); Py_RETURN_NONE; } @@ -82,7 +103,12 @@ PyDoc_STRVAR(pygpu_matrix_pop_projection_doc, ); static PyObject *pygpu_matrix_pop_projection(PyObject *UNUSED(self)) { - gpuPopMatrix(); + if (GPU_matrix_stack_level_get_projection() == 0) { + PyErr_SetString(PyExc_RuntimeError, + "Minimum projection stack depth reached"); + return NULL; + } + gpuPopProjectionMatrix(); Py_RETURN_NONE; } @@ -289,8 +315,10 @@ static PyObject *pygpu_matrix_get_normal_matrix(PyObject *UNUSED(self)) static struct PyMethodDef BPy_GPU_matrix_methods[] = { /* Manage Stack */ - {"push", (PyCFunction)pygpu_matrix_push, METH_NOARGS, pygpu_matrix_push_doc}, - {"pop", (PyCFunction)pygpu_matrix_pop, METH_NOARGS, pygpu_matrix_pop_doc}, + {"push", (PyCFunction)pygpu_matrix_push, + METH_NOARGS, pygpu_matrix_push_doc}, + {"pop", (PyCFunction)pygpu_matrix_pop, + METH_NOARGS, pygpu_matrix_pop_doc}, {"push_projection", (PyCFunction)pygpu_matrix_push_projection, METH_NOARGS, pygpu_matrix_push_projection_doc},