Files
test2/source/blender/python/gpu/gpu_py_matrix.cc
Brecht Van Lommel 920e709069 Refactor: Make header files more clangd and clang-tidy friendly
When using clangd or running clang-tidy on headers there are
currently many errors. These are noisy in IDEs, make auto fixes
impossible, and break features like code completion, refactoring
and navigation.

This makes source/blender headers work by themselves, which is
generally the goal anyway. But #includes and forward declarations
were often incomplete.

* Add #includes and forward declarations
* Add IWYU pragma: export in a few places
* Remove some unused #includes (but there are many more)
* Tweak ShaderCreateInfo macros to work better with clangd

Some types of headers still have errors, these could be fixed or
worked around with more investigation. Mostly preprocessor
template headers like NOD_static_types.h.

Note that that disabling WITH_UNITY_BUILD is required for clangd to
work properly, otherwise compile_commands.json does not contain
the information for the relevant source files.

For more details see the developer docs:
https://developer.blender.org/docs/handbook/tooling/clangd/

Pull Request: https://projects.blender.org/blender/blender/pulls/132608
2025-01-07 12:39:13 +01:00

690 lines
18 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bpygpu
*
* 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.
*
* - Use `bpygpu_` for local API.
* - Use `BPyGPU` for public API.
*/
#include <Python.h>
#include "BLI_utildefines.h"
#include "../mathutils/mathutils.hh"
#define USE_GPU_PY_MATRIX_API
#include "GPU_matrix.hh"
#undef USE_GPU_PY_MATRIX_API
#include "gpu_py.hh"
#include "gpu_py_matrix.hh" /* own include */
/* -------------------------------------------------------------------- */
/** \name Helper Functions
* \{ */
static bool pygpu_stack_is_push_model_view_ok_or_error()
{
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()
{
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()
{
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()
{
if (GPU_matrix_stack_level_get_projection() == 0) {
PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
return false;
}
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Manage Stack
* \{ */
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_push_doc,
".. function:: push()\n"
"\n"
" Add to the model-view matrix stack.\n");
static PyObject *pygpu_matrix_push(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
return nullptr;
}
GPU_matrix_push();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_pop_doc,
".. function:: pop()\n"
"\n"
" Remove the last model-view matrix from the stack.\n");
static PyObject *pygpu_matrix_pop(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
return nullptr;
}
GPU_matrix_pop();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_push_projection_doc,
".. function:: push_projection()\n"
"\n"
" Add to the projection matrix stack.\n");
static PyObject *pygpu_matrix_push_projection(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
if (!pygpu_stack_is_push_projection_ok_or_error()) {
return nullptr;
}
GPU_matrix_push_projection();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_pop_projection_doc,
".. function:: pop_projection()\n"
"\n"
" Remove the last projection matrix from the stack.\n");
static PyObject *pygpu_matrix_pop_projection(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
if (!pygpu_stack_is_pop_projection_ok_or_error()) {
return nullptr;
}
GPU_matrix_pop_projection();
Py_RETURN_NONE;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stack (Context Manager)
*
* Safer alternative to ensure balanced push/pop calls.
*
* \{ */
struct BPyGPU_MatrixStackContext {
PyObject_HEAD /* Required Python macro. */
int type;
int level;
};
enum {
PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
PYGPU_MATRIX_TYPE_PROJECTION = 2,
};
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self);
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
#if (defined(__GNUC__) && !defined(__clang__))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
static PyMethodDef pygpu_matrix_stack_context__tp_methods[] = {
{"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
{"__exit__", (PyCFunction)pygpu_matrix_stack_context_exit, METH_VARARGS},
{nullptr},
};
#if (defined(__GNUC__) && !defined(__clang__))
# pragma GCC diagnostic pop
#endif
static PyTypeObject PyGPUMatrixStackContext_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "GPUMatrixStackContext",
/*tp_basicsize*/ sizeof(BPyGPU_MatrixStackContext),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ nullptr,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
/*tp_doc*/ nullptr,
/*tp_traverse*/ nullptr,
/*tp_clear*/ nullptr,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ pygpu_matrix_stack_context__tp_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_free*/ nullptr,
/*tp_is_gc*/ nullptr,
/*tp_bases*/ nullptr,
/*tp_mro*/ nullptr,
/*tp_cache*/ nullptr,
/*tp_subclasses*/ nullptr,
/*tp_weaklist*/ nullptr,
/*tp_del*/ nullptr,
/*tp_version_tag*/ 0,
/*tp_finalize*/ nullptr,
/*tp_vectorcall*/ nullptr,
};
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
/* sanity - should never happen */
if (self->level != -1) {
PyErr_SetString(PyExc_RuntimeError, "Already in use");
return nullptr;
}
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
return nullptr;
}
GPU_matrix_push();
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 nullptr;
}
GPU_matrix_push_projection();
self->level = GPU_matrix_stack_level_get_projection();
}
else {
BLI_assert_unreachable();
}
Py_RETURN_NONE;
}
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self,
PyObject * /*args*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
/* 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) {
GPU_matrix_pop();
}
}
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) {
GPU_matrix_pop_projection();
}
}
else {
BLI_assert_unreachable();
}
finally:
Py_RETURN_NONE;
}
static PyObject *pygpu_matrix_push_pop_impl(int type)
{
BPyGPU_MatrixStackContext *ret = PyObject_New(BPyGPU_MatrixStackContext,
&PyGPUMatrixStackContext_Type);
ret->type = type;
ret->level = -1;
return (PyObject *)ret;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_push_pop_doc,
".. function:: 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 * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_push_pop_projection_doc,
".. function:: 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 * /*self*/)
{
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Manipulate State
* \{ */
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_multiply_matrix_doc,
".. function:: multiply_matrix(matrix)\n"
"\n"
" Multiply the current stack matrix.\n"
"\n"
" :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_multiply_matrix(PyObject * /*self*/, PyObject *value)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
return nullptr;
}
GPU_matrix_mul(pymat->matrix);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_scale_doc,
".. function:: scale(scale)\n"
"\n"
" Scale the current stack matrix.\n"
"\n"
" :arg scale: Scale the current stack matrix with 2 or 3 floats.\n"
" :type scale: Sequence[float]\n");
static PyObject *pygpu_matrix_scale(PyObject * /*self*/, PyObject *value)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
float scale[3];
int len;
if ((len = mathutils_array_parse(
scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1)
{
return nullptr;
}
if (len == 2) {
GPU_matrix_scale_2fv(scale);
}
else {
GPU_matrix_scale_3fv(scale);
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_scale_uniform_doc,
".. function:: scale_uniform(scale)\n"
"\n"
" :arg scale: Scale the current stack matrix.\n"
" :type scale: float\n");
static PyObject *pygpu_matrix_scale_uniform(PyObject * /*self*/, PyObject *value)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
float scalar;
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError, "expected a number, not %.200s", Py_TYPE(value)->tp_name);
return nullptr;
}
GPU_matrix_scale_1f(scalar);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_translate_doc,
".. function:: translate(offset)\n"
"\n"
" Scale the current stack matrix.\n"
"\n"
" :arg offset: Translate the current stack matrix with 2 or 3 floats.\n"
" :type offset: Sequence[float]\n");
static PyObject *pygpu_matrix_translate(PyObject * /*self*/, PyObject *value)
{
float offset[3];
int len;
if ((len = mathutils_array_parse(
offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1)
{
return nullptr;
}
if (len == 2) {
GPU_matrix_translate_2fv(offset);
}
else {
GPU_matrix_translate_3fv(offset);
}
Py_RETURN_NONE;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Write State
* \{ */
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_reset_doc,
".. function:: reset()\n"
"\n"
" Empty stack and set to identity.\n");
static PyObject *pygpu_matrix_reset(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
GPU_matrix_reset();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_load_identity_doc,
".. function:: load_identity()\n"
"\n"
" Load an identity matrix into the stack.\n");
static PyObject *pygpu_matrix_load_identity(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
GPU_matrix_identity_set();
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_load_matrix_doc,
".. function:: load_matrix(matrix)\n"
"\n"
" Load a matrix into the stack.\n"
"\n"
" :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_load_matrix(PyObject * /*self*/, PyObject *value)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
return nullptr;
}
GPU_matrix_set(pymat->matrix);
Py_RETURN_NONE;
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_load_projection_matrix_doc,
".. function:: load_projection_matrix(matrix)\n"
"\n"
" Load a projection matrix into the stack.\n"
"\n"
" :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_load_projection_matrix(PyObject * /*self*/, PyObject *value)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
return nullptr;
}
GPU_matrix_projection_set(pymat->matrix);
Py_RETURN_NONE;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read State
* \{ */
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_get_projection_matrix_doc,
".. function:: get_projection_matrix()\n"
"\n"
" Return a copy of the projection matrix.\n"
"\n"
" :return: A 4x4 projection matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_get_projection_matrix(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
float matrix[4][4];
GPU_matrix_projection_get(matrix);
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_get_model_view_matrix_doc,
".. function:: get_model_view_matrix()\n"
"\n"
" Return a copy of the model-view matrix.\n"
"\n"
" :return: A 4x4 view matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_get_model_view_matrix(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
float matrix[4][4];
GPU_matrix_model_view_get(matrix);
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
}
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix_get_normal_matrix_doc,
".. function:: get_normal_matrix()\n"
"\n"
" Return a copy of the normal matrix.\n"
"\n"
" :return: A 3x3 normal matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_get_normal_matrix(PyObject * /*self*/)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
float matrix[3][3];
GPU_matrix_normal_get(matrix);
return Matrix_CreatePyObject(&matrix[0][0], 3, 3, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Module
* \{ */
#if (defined(__GNUC__) && !defined(__clang__))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
static PyMethodDef pygpu_matrix__tp_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_projection",
(PyCFunction)pygpu_matrix_push_projection,
METH_NOARGS,
pygpu_matrix_push_projection_doc},
{"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},
{"scale", (PyCFunction)pygpu_matrix_scale, METH_O, pygpu_matrix_scale_doc},
{"scale_uniform",
(PyCFunction)pygpu_matrix_scale_uniform,
METH_O,
pygpu_matrix_scale_uniform_doc},
{"translate", (PyCFunction)pygpu_matrix_translate, METH_O, pygpu_matrix_translate_doc},
/* TODO */
#if 0
{"rotate", (PyCFunction)pygpu_matrix_rotate, METH_O, pygpu_matrix_rotate_doc},
{"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis, METH_O, pygpu_matrix_rotate_axis_doc},
{"look_at", (PyCFunction)pygpu_matrix_look_at, METH_O, pygpu_matrix_look_at_doc},
#endif
/* Write State */
{"reset", (PyCFunction)pygpu_matrix_reset, METH_NOARGS, pygpu_matrix_reset_doc},
{"load_identity",
(PyCFunction)pygpu_matrix_load_identity,
METH_NOARGS,
pygpu_matrix_load_identity_doc},
{"load_matrix", (PyCFunction)pygpu_matrix_load_matrix, METH_O, pygpu_matrix_load_matrix_doc},
{"load_projection_matrix",
(PyCFunction)pygpu_matrix_load_projection_matrix,
METH_O,
pygpu_matrix_load_projection_matrix_doc},
/* Read State */
{"get_projection_matrix",
(PyCFunction)pygpu_matrix_get_projection_matrix,
METH_NOARGS,
pygpu_matrix_get_projection_matrix_doc},
{"get_model_view_matrix",
(PyCFunction)pygpu_matrix_get_model_view_matrix,
METH_NOARGS,
pygpu_matrix_get_model_view_matrix_doc},
{"get_normal_matrix",
(PyCFunction)pygpu_matrix_get_normal_matrix,
METH_NOARGS,
pygpu_matrix_get_normal_matrix_doc},
{nullptr, nullptr, 0, nullptr},
};
#if (defined(__GNUC__) && !defined(__clang__))
# pragma GCC diagnostic pop
#endif
PyDoc_STRVAR(
/* Wrap. */
pygpu_matrix__tp_doc,
"This module provides access to the matrix stack.");
static PyModuleDef pygpu_matrix_module_def = {
/*m_base*/ PyModuleDef_HEAD_INIT,
/*m_name*/ "gpu.matrix",
/*m_doc*/ pygpu_matrix__tp_doc,
/*m_size*/ 0,
/*m_methods*/ pygpu_matrix__tp_methods,
/*m_slots*/ nullptr,
/*m_traverse*/ nullptr,
/*m_clear*/ nullptr,
/*m_free*/ nullptr,
};
PyObject *bpygpu_matrix_init()
{
PyObject *submodule;
submodule = PyModule_Create(&pygpu_matrix_module_def);
if (PyType_Ready(&PyGPUMatrixStackContext_Type) < 0) {
return nullptr;
}
return submodule;
}
/** \} */