Python GPU Buffer: Add a 'setter' to Buffer.dimensions

The attribute `Buffer.dimensions` does not need to be readonly.
This commit is contained in:
Germano Cavalcante 2021-04-21 17:05:44 -03:00
parent ef9551afd1
commit 19360c2c1c
Notes: blender-bot 2023-02-13 22:38:46 +01:00
Referenced by commit 48c5129d1f, Fix errors in Buffer.dimensions `setter`
1 changed files with 114 additions and 64 deletions

View File

@ -37,17 +37,92 @@
#include "gpu_py_buffer.h"
// #define PYGPU_BUFFER_PROTOCOL
//#define PYGPU_BUFFER_PROTOCOL
#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
static bool pygpu_buffer_dimensions_compare(int ndim,
const Py_ssize_t *shape_a,
const Py_ssize_t *shape_b)
static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
{
return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t));
Py_ssize_t tot = shape[0];
for (int i = 1; i < shape_len; i++) {
tot *= shape[i];
}
return tot;
}
static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
const Py_ssize_t shape_a_len,
const Py_ssize_t *shape_b,
const Py_ssize_t shape_b_len)
{
if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) {
PyErr_Format(PyExc_BufferError, "array size does not match");
return false;
}
return true;
}
static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
Py_ssize_t r_shape[MAX_DIMENSIONS],
Py_ssize_t *r_shape_len)
{
Py_ssize_t shape_len = 0;
if (PyLong_Check(shape_obj)) {
shape_len = 1;
if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) {
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
return false;
}
}
else if (PySequence_Check(shape_obj)) {
Py_ssize_t shape_len = PySequence_Size(shape_obj);
if (shape_len > MAX_DIMENSIONS) {
PyErr_SetString(PyExc_AttributeError,
"too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
return false;
}
if (shape_len < 1) {
PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
return false;
}
for (int i = 0; i < shape_len; i++) {
PyObject *ob = PySequence_GetItem(shape_obj, i);
if (!PyLong_Check(ob)) {
PyErr_Format(PyExc_TypeError,
"invalid dimension %i, expected an int, not a %.200s",
i,
Py_TYPE(ob)->tp_name);
Py_DECREF(ob);
return false;
}
r_shape[i] = PyLong_AsLong(ob);
Py_DECREF(ob);
if (r_shape[i] < 1) {
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
return false;
}
}
*r_shape_len = shape_len;
}
else {
PyErr_Format(PyExc_TypeError,
"invalid second argument argument expected a sequence "
"or an int, not a %.200s",
Py_TYPE(shape_obj)->tp_name);
}
*r_shape_len = shape_len;
return true;
}
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
@ -174,7 +249,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
return list;
}
static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg))
{
PyObject *list = PyList_New(self->shape_len);
int i;
@ -186,6 +261,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
return list;
}
static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type))
{
Py_ssize_t shape[MAX_DIMENSIONS];
Py_ssize_t shape_len = 0;
if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
return -1;
}
if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
return -1;
}
size_t size = shape_len * sizeof(*self->shape);
if (shape_len != self->shape_len) {
MEM_freeN(self->shape);
self->shape = MEM_mallocN(size, __func__);
}
self->shape_len = shape_len;
memcpy(self->shape, shape, size);
return 0;
}
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
@ -280,14 +379,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
return err;
}
#define MAX_DIMENSIONS 64
static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
PyObject *length_ob, *init = NULL;
BPyGPUBuffer *buffer = NULL;
Py_ssize_t shape[MAX_DIMENSIONS];
Py_ssize_t i, shape_len = 0;
Py_ssize_t shape_len = 0;
if (kwds && PyDict_Size(kwds)) {
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
@ -300,49 +398,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
if (PyLong_Check(length_ob)) {
shape_len = 1;
if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) {
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
return NULL;
}
}
else if (PySequence_Check(length_ob)) {
shape_len = PySequence_Size(length_ob);
if (shape_len > MAX_DIMENSIONS) {
PyErr_SetString(PyExc_AttributeError,
"too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
return NULL;
}
if (shape_len < 1) {
PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
return NULL;
}
for (i = 0; i < shape_len; i++) {
PyObject *ob = PySequence_GetItem(length_ob, i);
if (!PyLong_Check(ob)) {
PyErr_Format(PyExc_TypeError,
"invalid dimension %i, expected an int, not a %.200s",
i,
Py_TYPE(ob)->tp_name);
Py_DECREF(ob);
return NULL;
}
shape[i] = PyLong_AsLong(ob);
Py_DECREF(ob);
if (shape[i] < 1) {
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
return NULL;
}
}
}
else {
PyErr_Format(PyExc_TypeError,
"invalid second argument argument expected a sequence "
"or an int, not a %.200s",
Py_TYPE(length_ob)->tp_name);
if (pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len) == -1) {
return NULL;
}
@ -354,11 +410,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
if (shape_len != pybuffer.ndim ||
!pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) {
PyErr_Format(PyExc_TypeError, "array size does not match");
}
else {
if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
buffer = pygpu_buffer_make_from_data(
init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
}
@ -518,7 +570,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = {
};
static PyGetSetDef pygpu_buffer_getseters[] = {
{"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL},
{"dimensions",
(getter)pygpu_buffer_dimensions_get,
(setter)pygpu_buffer_dimensions_set,
NULL,
NULL},
{NULL, NULL, NULL, NULL, NULL},
};
@ -625,13 +681,7 @@ static size_t pygpu_buffer_calc_size(const int format,
const int shape_len,
const Py_ssize_t *shape)
{
size_t r_size = GPU_texture_dataformat_size(format);
for (int i = 0; i < shape_len; i++) {
r_size *= shape[i];
}
return r_size;
return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format);
}
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)