mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
PyAPI: use the vectorcall protocol for mathutils types
The Vectorcall protocol avoids creating a tuple, and also provides the number of arguments in advance, providing a ~1.6x speedup for creation of mathutils types. Ref !146237
This commit is contained in:
parent
d26a79b144
commit
b2176bfdd7
5 changed files with 98 additions and 66 deletions
|
|
@ -61,34 +61,37 @@ static PyObject *Color_to_tuple_ex(ColorObject *self, int ndigits)
|
|||
/** \name Color Type: `__new__` / `mathutils.Color()`
|
||||
* \{ */
|
||||
|
||||
static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static PyObject *Color_vectorcall(PyObject *type,
|
||||
PyObject *const *args,
|
||||
const size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
float col[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
if (UNLIKELY(kwnames && PyDict_Size(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"mathutils.Color(): "
|
||||
"takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (PyTuple_GET_SIZE(args)) {
|
||||
const size_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
switch (nargs) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if (mathutils_array_parse(
|
||||
col, COLOR_SIZE, COLOR_SIZE, PyTuple_GET_ITEM(args, 0), "mathutils.Color()") == -1)
|
||||
{
|
||||
if (mathutils_array_parse(col, COLOR_SIZE, COLOR_SIZE, args[0], "mathutils.Color()") == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"mathutils.Color(): "
|
||||
"more than a single arg given");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"mathutils.Color(): "
|
||||
"takes at most 1 argument (%zd given)",
|
||||
nargs);
|
||||
return nullptr;
|
||||
}
|
||||
return Color_CreatePyObject(col, type);
|
||||
return Color_CreatePyObject(col, (PyTypeObject *)type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
@ -1382,7 +1385,7 @@ PyTypeObject color_Type = {
|
|||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ Color_new,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
|
||||
/*tp_bases*/ nullptr,
|
||||
|
|
@ -1393,7 +1396,7 @@ PyTypeObject color_Type = {
|
|||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
/*tp_vectorcall*/ Color_vectorcall,
|
||||
};
|
||||
|
||||
#ifdef MATH_STANDALONE
|
||||
|
|
|
|||
|
|
@ -94,40 +94,49 @@ static PyObject *Euler_to_tuple_ex(EulerObject *self, int ndigits)
|
|||
/** \name Euler Type: `__new__` / `mathutils.Euler()`
|
||||
* \{ */
|
||||
|
||||
static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static PyObject *Euler_vectorcall(PyObject *type,
|
||||
PyObject *const *args,
|
||||
const size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
PyObject *seq = nullptr;
|
||||
const char *order_str = nullptr;
|
||||
|
||||
float eul[EULER_SIZE] = {0.0f, 0.0f, 0.0f};
|
||||
short order = EULER_ORDER_XYZ;
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
if (UNLIKELY(kwnames && PyDict_Size(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"mathutils.Euler(): "
|
||||
"takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|Os:mathutils.Euler", &seq, &order_str)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (PyTuple_GET_SIZE(args)) {
|
||||
const size_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
switch (nargs) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1) {
|
||||
case 2: {
|
||||
const char *order_str;
|
||||
|
||||
if (((order_str = PyUnicode_AsUTF8(args[1])) == nullptr) ||
|
||||
((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ATTR_FALLTHROUGH;
|
||||
}
|
||||
case 1:
|
||||
if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1) {
|
||||
if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, args[0], "mathutils.Euler()") == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"mathutils.Euler(): "
|
||||
"takes at most 2 arguments (%zd given)",
|
||||
nargs);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return Euler_CreatePyObject(eul, order, type);
|
||||
return Euler_CreatePyObject(eul, order, (PyTypeObject *)type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
@ -926,7 +935,7 @@ PyTypeObject euler_Type = {
|
|||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ Euler_new,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
|
||||
/*tp_bases*/ nullptr,
|
||||
|
|
@ -937,7 +946,7 @@ PyTypeObject euler_Type = {
|
|||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
/*tp_vectorcall*/ Euler_vectorcall,
|
||||
};
|
||||
|
||||
#ifdef MATH_STANDALONE
|
||||
|
|
|
|||
|
|
@ -594,20 +594,24 @@ Mathutils_Callback mathutils_matrix_translation_cb = {
|
|||
/** \name Matrix Type: `__new__` / `mathutils.Matrix()`
|
||||
* \{ */
|
||||
|
||||
static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static PyObject *Matrix_vectorcall(PyObject *type,
|
||||
PyObject *const *args,
|
||||
const size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
if (UNLIKELY(kwnames && PyDict_Size(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Matrix(): "
|
||||
"takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (PyTuple_GET_SIZE(args)) {
|
||||
switch (PyVectorcall_NARGS(nargsf)) {
|
||||
case 0:
|
||||
return Matrix_CreatePyObject(nullptr, 4, 4, type);
|
||||
return Matrix_CreatePyObject(nullptr, 4, 4, (PyTypeObject *)type);
|
||||
|
||||
case 1: {
|
||||
PyObject *arg = PyTuple_GET_ITEM(args, 0);
|
||||
PyObject *arg = args[0];
|
||||
|
||||
/* Input is now as a sequence of rows so length of sequence
|
||||
* is the number of rows */
|
||||
|
|
@ -623,7 +627,8 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
|
||||
if (col_num >= 2 && col_num <= 4) {
|
||||
/* Sane row & col size, new matrix and assign as slice. */
|
||||
PyObject *matrix = Matrix_CreatePyObject(nullptr, col_num, row_num, type);
|
||||
PyObject *matrix = Matrix_CreatePyObject(
|
||||
nullptr, col_num, row_num, (PyTypeObject *)type);
|
||||
if (Matrix_ass_slice((MatrixObject *)matrix, 0, INT_MAX, arg) == 0) {
|
||||
return matrix;
|
||||
}
|
||||
|
|
@ -637,7 +642,7 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
|
||||
/* will overwrite error */
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Matrix(): "
|
||||
"mathutils.Matrix(): "
|
||||
"expects no args or a single arg containing 2-4 numeric sequences");
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -3564,7 +3569,7 @@ PyTypeObject matrix_Type = {
|
|||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ Matrix_new,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
|
||||
/*tp_bases*/ nullptr,
|
||||
|
|
@ -3575,7 +3580,7 @@ PyTypeObject matrix_Type = {
|
|||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
/*tp_vectorcall*/ Matrix_vectorcall,
|
||||
};
|
||||
|
||||
#ifdef MATH_STANDALONE
|
||||
|
|
|
|||
|
|
@ -104,32 +104,31 @@ static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits)
|
|||
/** \name Quaternion Type: `__new__` / `mathutils.Quaternion()`
|
||||
* \{ */
|
||||
|
||||
static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static PyObject *Quaternion_vectorcall(PyObject *type,
|
||||
PyObject *const *args,
|
||||
const size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
PyObject *seq = nullptr;
|
||||
double angle = 0.0f;
|
||||
float quat[QUAT_SIZE];
|
||||
unit_qt(quat);
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
if (UNLIKELY(kwnames && PyDict_Size(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"mathutils.Quaternion(): "
|
||||
"takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (PyTuple_GET_SIZE(args)) {
|
||||
const size_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
switch (nargs) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
int size;
|
||||
const int size = mathutils_array_parse(
|
||||
quat, 3, QUAT_SIZE, args[0], "mathutils.Quaternion()");
|
||||
|
||||
if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == -1)
|
||||
{
|
||||
if (UNLIKELY(size == -1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -146,16 +145,30 @@ static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kw
|
|||
}
|
||||
case 2: {
|
||||
float axis[3];
|
||||
if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) {
|
||||
if (mathutils_array_parse(axis, 3, 3, args[0], "mathutils.Quaternion()") == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
angle = PyFloat_AsDouble(args[1]);
|
||||
if (UNLIKELY(angle == -1.0 && PyErr_Occurred())) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"mathutils.Quaternion(): "
|
||||
"angle must be a real number, not '%.200s'",
|
||||
Py_TYPE(args[1])->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
angle = angle_wrap_rad(angle); /* clamp because of precision issues */
|
||||
axis_angle_to_quat(quat, axis, angle);
|
||||
break;
|
||||
/* PyArg_ParseTuple assures no more than 2 */
|
||||
}
|
||||
default: {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"mathutils.Quaternion() "
|
||||
"takes at most 2 arguments (%zd given)",
|
||||
nargs);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return Quaternion_CreatePyObject(quat, type);
|
||||
return Quaternion_CreatePyObject(quat, (PyTypeObject *)type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
@ -1873,7 +1886,7 @@ PyTypeObject quaternion_Type = {
|
|||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ Quaternion_new,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
|
||||
/*tp_bases*/ nullptr,
|
||||
|
|
@ -1884,7 +1897,7 @@ PyTypeObject quaternion_Type = {
|
|||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
/*tp_vectorcall*/ Quaternion_vectorcall,
|
||||
};
|
||||
|
||||
#ifdef MATH_STANDALONE
|
||||
|
|
|
|||
|
|
@ -138,19 +138,22 @@ static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits)
|
|||
* Supports 2D, 3D, and 4D vector objects both int and float values
|
||||
* accepted. Mixed float and int values accepted. Ints are parsed to float
|
||||
*/
|
||||
static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
static PyObject *Vector_vectorcall(PyObject *type,
|
||||
PyObject *const *args,
|
||||
const size_t nargsf,
|
||||
PyObject *kwnames)
|
||||
{
|
||||
float *vec = nullptr;
|
||||
int vec_num = 3; /* default to a 3D vector */
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
if (UNLIKELY(kwnames && PyDict_Size(kwnames))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Vector(): "
|
||||
"takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (PyTuple_GET_SIZE(args)) {
|
||||
const size_t nargs = PyVectorcall_NARGS(nargsf);
|
||||
switch (nargs) {
|
||||
case 0:
|
||||
vec = static_cast<float *>(PyMem_Malloc(vec_num * sizeof(float)));
|
||||
|
||||
|
|
@ -164,19 +167,18 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
copy_vn_fl(vec, vec_num, 0.0f);
|
||||
break;
|
||||
case 1:
|
||||
if ((vec_num = mathutils_array_parse_alloc(
|
||||
&vec, 2, PyTuple_GET_ITEM(args, 0), "mathutils.Vector()")) == -1)
|
||||
{
|
||||
if ((vec_num = mathutils_array_parse_alloc(&vec, 2, args[0], "mathutils.Vector()")) == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"mathutils.Vector(): "
|
||||
"more than a single arg given");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"mathutils.Vector(): "
|
||||
"takes at most 1 argument (%zd given)",
|
||||
nargs);
|
||||
return nullptr;
|
||||
}
|
||||
return Vector_CreatePyObject_alloc(vec, vec_num, type);
|
||||
return Vector_CreatePyObject_alloc(vec, vec_num, (PyTypeObject *)type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
@ -3483,7 +3485,7 @@ PyTypeObject vector_Type = {
|
|||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ Vector_new,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
|
||||
/*tp_bases*/ nullptr,
|
||||
|
|
@ -3494,7 +3496,7 @@ PyTypeObject vector_Type = {
|
|||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
/*tp_vectorcall*/ Vector_vectorcall,
|
||||
};
|
||||
|
||||
#ifdef MATH_STANDALONE
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue