diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2017-04-10 17:53:12 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2017-04-10 17:53:12 +0300 |
commit | 346964eb3f3c3074d30676f27ed477ce542299ea (patch) | |
tree | 24112aba81885de0ccb4e21030df67635bae819c | |
parent | 39451ac7128ef753da24ae74486c51a8a40e2719 (diff) | |
parent | fd203a09330ce88725e8c5c8d9b1f9ce7b2f3c10 (diff) |
Merge branch 'master' into blender2.8
Conflicts:
source/blender/editors/gpencil/drawgpencil.c
-rwxr-xr-x | build_files/build_environment/install_deps.sh | 6 | ||||
-rw-r--r-- | intern/cycles/device/opencl/opencl_util.cpp | 7 | ||||
-rw-r--r-- | intern/cycles/kernel/geom/geom_curve.h | 4 | ||||
-rw-r--r-- | intern/cycles/kernel/geom/geom_motion_curve.h | 2 | ||||
-rw-r--r-- | intern/cycles/kernel/kernel_compat_cpu.h | 42 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/idprop.c | 2 | ||||
-rw-r--r-- | source/blender/editors/gpencil/drawgpencil.c | 17 | ||||
-rw-r--r-- | source/blender/editors/space_clip/clip_utils.c | 5 | ||||
-rw-r--r-- | source/blender/python/generic/idprop_py_api.c | 419 | ||||
-rw-r--r-- | tests/python/bl_pyapi_idprop.py | 62 |
10 files changed, 401 insertions, 165 deletions
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 479585656d1..b10ae4e7a4a 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -2262,7 +2262,9 @@ compile_ALEMBIC() { fi if [ -d $INST/boost ]; then - cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost" + if [ -d $INST/boost ]; then + cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost" + fi cmake_d="$cmake_d -D USE_STATIC_BOOST=ON" else cmake_d="$cmake_d -D USE_STATIC_BOOST=OFF" @@ -2770,7 +2772,7 @@ install_DEB() { boost_version=$(echo `get_package_version_DEB libboost-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') - install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave}$boost_version-dev + install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave,program-options}$boost_version-dev clean_Boost else compile_Boost diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp index 6dca642f3f3..fe1c65a2224 100644 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ b/intern/cycles/device/opencl/opencl_util.cpp @@ -1058,13 +1058,16 @@ cl_device_type OpenCLInfo::get_device_type(cl_device_id device_id) string OpenCLInfo::get_readable_device_name(cl_device_id device_id) { char board_name[1024]; + size_t length = 0; if(clGetDeviceInfo(device_id, CL_DEVICE_BOARD_NAME_AMD, sizeof(board_name), &board_name, - NULL) == CL_SUCCESS) + &length) == CL_SUCCESS) { - return board_name; + if(length != 0 && board_name[0] != '\0') { + return board_name; + } } /* Fallback to standard device name API. */ return get_device_name(device_id); diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index bb33b91847e..8888000f0e6 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -270,7 +270,7 @@ ccl_device_curveintersect bool bvh_cardinal_curve_intersect(KernelGlobals *kg, I int ka = max(k0 - 1, v00.x); int kb = min(k1 + 1, v00.x + v00.y - 1); -#if defined(__KERNEL_AVX2__) && (!defined(_MSC_VER) || _MSC_VER > 1800) +#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && (!defined(_MSC_VER) || _MSC_VER > 1800) avxf P_curve_0_1, P_curve_2_3; if(is_curve_primitive) { P_curve_0_1 = _mm256_loadu2_m128(&kg->__curve_keys.data[k0].x, &kg->__curve_keys.data[ka].x); @@ -305,7 +305,7 @@ ccl_device_curveintersect bool bvh_cardinal_curve_intersect(KernelGlobals *kg, I ssef htfm1 = shuffle<1, 0, 1, 3>(load1f_first(extract<0>(d_ss)), vdir0); ssef htfm2 = shuffle<1, 3, 2, 3>(mul_shuf, vdir0); -#if defined(__KERNEL_AVX2__) && (!defined(_MSC_VER) || _MSC_VER > 1800) +#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) && (!defined(_MSC_VER) || _MSC_VER > 1800) const avxf vPP = _mm256_broadcast_ps(&P.m128); const avxf htfm00 = avxf(htfm0.m128, htfm0.m128); const avxf htfm11 = avxf(htfm1.m128, htfm1.m128); diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index dc1388b6643..119bdb2f15c 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -152,7 +152,7 @@ ccl_device_inline void motion_cardinal_curve_keys(KernelGlobals *kg, keys[3] = (1.0f - t)*keys[3] + t*next_keys[3]; } -#ifdef __KERNEL_AVX2__ +#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) /* Similar to above, but returns keys as pair of two AVX registers with each * holding two float4. */ diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index cad5f4d2959..21da180bb8e 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -353,7 +353,7 @@ template<typename T> struct texture_image { { int ix, iy, iz; int nix, niy, niz; - + float tx = frac(x*(float)width - 0.5f, &ix); float ty = frac(y*(float)height - 0.5f, &iy); float tz = frac(z*(float)depth - 0.5f, &iz); @@ -404,7 +404,18 @@ template<typename T> struct texture_image { return r; } - ccl_never_inline float4 interp_3d_ex_tricubic(float x, float y, float z) + /* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are + * causing stack overflow issue in this function unless it is inlined. + * + * Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization + * enabled. + */ +#ifdef __GNUC__ + ccl_always_inline +#else + ccl_never_inline +#endif + float4 interp_3d_ex_tricubic(float x, float y, float z) { int ix, iy, iz; int nix, niy, niz; @@ -463,13 +474,13 @@ template<typename T> struct texture_image { const int xc[4] = {pix, ix, nix, nnix}; const int yc[4] = {width * piy, - width * iy, - width * niy, - width * nniy}; + width * iy, + width * niy, + width * nniy}; const int zc[4] = {width * height * piz, - width * height * iz, - width * height * niz, - width * height * nniz}; + width * height * iz, + width * height * niz, + width * height * nniz}; float u[4], v[4], w[4]; /* Some helper macro to keep code reasonable size, @@ -478,14 +489,14 @@ template<typename T> struct texture_image { #define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]])) #define COL_TERM(col, row) \ (v[col] * (u[0] * DATA(0, col, row) + \ - u[1] * DATA(1, col, row) + \ - u[2] * DATA(2, col, row) + \ - u[3] * DATA(3, col, row))) + u[1] * DATA(1, col, row) + \ + u[2] * DATA(2, col, row) + \ + u[3] * DATA(3, col, row))) #define ROW_TERM(row) \ (w[row] * (COL_TERM(0, row) + \ - COL_TERM(1, row) + \ - COL_TERM(2, row) + \ - COL_TERM(3, row))) + COL_TERM(1, row) + \ + COL_TERM(2, row) + \ + COL_TERM(3, row))) SET_CUBIC_SPLINE_WEIGHTS(u, tx); SET_CUBIC_SPLINE_WEIGHTS(v, ty); @@ -502,11 +513,10 @@ template<typename T> struct texture_image { ccl_always_inline float4 interp_3d_ex(float x, float y, float z, int interpolation = INTERPOLATION_LINEAR) { - if(UNLIKELY(!data)) return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - switch(interpolation) { + switch(interpolation) { case INTERPOLATION_CLOSEST: return interp_3d_ex_closest(x, y, z); case INTERPOLATION_LINEAR: diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index e51c47d7b19..7c79e13cb01 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -891,7 +891,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * *(float *)&prop->data.val = val->f; break; case IDP_DOUBLE: - prop = MEM_callocN(sizeof(IDProperty), "IDProperty float"); + prop = MEM_callocN(sizeof(IDProperty), "IDProperty double"); *(double *)&prop->data.val = val->d; break; case IDP_ARRAY: diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 19c717b4ecb..11881a4a019 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -731,7 +731,8 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi */ { const bGPDspoint *pt1, *pt2; - float pm[2]; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ int i; float fpt[3]; @@ -743,17 +744,17 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi #ifdef WITH_GL_PROFILE_COMPAT immBegin(PRIM_QUADS_XXX, (totpoints - 2) * 4 + 12); + /* get x and y coordinates from first point */ + mul_v3_m4v3(fpt, diff_mat, &points->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { - float s0[2], s1[2]; /* segment 'center' points */ float t0[2], t1[2]; /* tessellated coordinates */ float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ - /* get x and y coordinates from points */ - mul_v3_m4v3(fpt, diff_mat, &pt1->x); - gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); - + /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ mul_v3_m4v3(fpt, diff_mat, &pt2->x); gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); @@ -879,7 +880,9 @@ static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thi immVertex2fv(pos, t1); immVertex2fv(pos, t0); } - + + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 462be829a23..272d99c2b0e 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -71,7 +71,7 @@ void clip_graph_tracking_values_iterate_track( BKE_movieclip_get_size(clip, &sc->user, &width, &height); for (coord = 0; coord < 2; coord++) { - int i, prevfra = 0; + int i, prevfra = track->markers[0].framenr; bool open = false; float prevval = 0.0f; @@ -184,6 +184,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); bool has_bundle = false; char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2]; + const bool used_for_stabilization = (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)); if (track == act_track) tracking->act_track = NULL; @@ -205,7 +206,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) { + if (used_for_stabilization) { WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 2e15b7b1413..0a9931f2683 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -335,19 +335,9 @@ static char idp_sequence_type(PyObject *seq_fast) return type; } -/** - * \note group can be a pointer array or a group. - * assume we already checked key is a string. - * - * \return success. - */ -bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) +static const char *idp_try_read_name(PyObject *name_obj) { - IDProperty *prop = NULL; - IDPropertyTemplate val = {0}; - - const char *name; - + const char *name = NULL; if (name_obj) { Py_ssize_t name_size; name = _PyUnicode_AsStringAndSize(name_obj, &name_size); @@ -356,168 +346,297 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyErr_Format(PyExc_KeyError, "invalid id-property key, expected a string, not a %.200s", Py_TYPE(name_obj)->tp_name); - return false; + return NULL; } if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters"); - return false; + return NULL; } } else { name = ""; } + return name; +} - if (PyFloat_Check(ob)) { - val.d = PyFloat_AsDouble(ob); - prop = IDP_New(IDP_DOUBLE, &val, name); - } - else if (PyLong_Check(ob)) { - val.i = _PyLong_AsInt(ob); - if (val.i == -1 && PyErr_Occurred()) { - return false; - } - prop = IDP_New(IDP_INT, &val, name); +/* -------------------------------------------------------------------------- */ + +/** + * The 'idp_from_Py*' functions expect that the input type has been checked before + * and return NULL if the IDProperty can't be created. + */ + +static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.d = PyFloat_AsDouble(ob); + return IDP_New(IDP_DOUBLE, &val, name); +} + +static IDProperty *idp_from_PyLong(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.i = _PyLong_AsInt(ob); + if (val.i == -1 && PyErr_Occurred()) { + return NULL; } - else if (PyUnicode_Check(ob)) { + return IDP_New(IDP_INT, &val, name); +} + +static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; #ifdef USE_STRING_COERCE - Py_ssize_t value_size; - PyObject *value_coerce = NULL; - val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce); - val.string.len = (int)value_size + 1; - val.string.subtype = IDP_STRING_SUB_UTF8; - prop = IDP_New(IDP_STRING, &val, name); - Py_XDECREF(value_coerce); + Py_ssize_t value_size; + PyObject *value_coerce = NULL; + val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce); + val.string.len = (int)value_size + 1; + val.string.subtype = IDP_STRING_SUB_UTF8; + prop = IDP_New(IDP_STRING, &val, name); + Py_XDECREF(value_coerce); #else - val.str = _PyUnicode_AsString(ob); - prop = IDP_New(IDP_STRING, val, name); + val.str = _PyUnicode_AsString(ob); + prop = IDP_New(IDP_STRING, val, name); #endif - } - else if (PyBytes_Check(ob)) { - val.string.str = PyBytes_AS_STRING(ob); - val.string.len = PyBytes_GET_SIZE(ob); - val.string.subtype = IDP_STRING_SUB_BYTE; + return prop; +} - prop = IDP_New(IDP_STRING, &val, name); - //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob)); - //prop->subtype = IDP_STRING_SUB_BYTE; +static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.string.str = PyBytes_AS_STRING(ob); + val.string.len = PyBytes_GET_SIZE(ob); + val.string.subtype = IDP_STRING_SUB_BYTE; + return IDP_New(IDP_STRING, &val, name); +} + +static int idp_array_type_from_format_char(char format) +{ + if (format == 'i') return IDP_INT; + if (format == 'f') return IDP_FLOAT; + if (format == 'd') return IDP_DOUBLE; + return -1; +} + +static const char *idp_format_from_array_type(int type) +{ + if (type == IDP_INT) return "i"; + if (type == IDP_FLOAT) return "f"; + if (type == IDP_DOUBLE) return "d"; + return NULL; +} + +static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; + + int format = idp_array_type_from_format_char(*buffer->format); + if (format == -1) { + /* should never happen as the type has been checked before */ + return NULL; } - else if (PySequence_Check(ob)) { - PyObject *ob_seq_fast; - PyObject **ob_seq_fast_items; - PyObject *item; - int i; + else { + val.array.type = format; + val.array.len = buffer->len / buffer->itemsize; + } + prop = IDP_New(IDP_ARRAY, &val, name); + memcpy(IDP_Array(prop), buffer->buf, buffer->len); + return prop; +} - if (!(ob_seq_fast = PySequence_Fast(ob, "py -> idprop"))) { - return false; - } +static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; - ob_seq_fast_items = PySequence_Fast_ITEMS(ob_seq_fast); + PyObject **ob_seq_fast_items; + PyObject *item; + int i; - if ((val.array.type = idp_sequence_type(ob_seq_fast)) == (char)-1) { - Py_DECREF(ob_seq_fast); - PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); - return false; - } + ob_seq_fast_items = PySequence_Fast_ITEMS(ob); - /* validate sequence and derive type. - * we assume IDP_INT unless we hit a float - * number; then we assume it's */ + if ((val.array.type = idp_sequence_type(ob)) == (char)-1) { + PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); + return NULL; + } - val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast); + /* validate sequence and derive type. + * we assume IDP_INT unless we hit a float + * number; then we assume it's */ - switch (val.array.type) { - case IDP_DOUBLE: - { - double *prop_data; - - prop = IDP_New(IDP_ARRAY, &val, name); - prop_data = IDP_Array(prop); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { - Py_DECREF(ob_seq_fast); - return false; - } + val.array.len = PySequence_Fast_GET_SIZE(ob); + + switch (val.array.type) { + case IDP_DOUBLE: + { + double *prop_data; + prop = IDP_New(IDP_ARRAY, &val, name); + prop_data = IDP_Array(prop); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { + return NULL; } - break; } - case IDP_INT: - { - int *prop_data; - prop = IDP_New(IDP_ARRAY, &val, name); - prop_data = IDP_Array(prop); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { - Py_DECREF(ob_seq_fast); - return false; - } + break; + } + case IDP_INT: + { + int *prop_data; + prop = IDP_New(IDP_ARRAY, &val, name); + prop_data = IDP_Array(prop); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { + return NULL; } - break; } - case IDP_IDPARRAY: - { - prop = IDP_NewIDPArray(name); - for (i = 0; i < val.array.len; i++) { - item = ob_seq_fast_items[i]; - - if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { - Py_DECREF(ob_seq_fast); - return false; - } + break; + } + case IDP_IDPARRAY: + { + prop = IDP_NewIDPArray(name); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { + return NULL; } - break; } - default: - /* should never happen */ - Py_DECREF(ob_seq_fast); - PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); - return false; + break; + } + default: + /* should never happen */ + PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); + return NULL; + } + return prop; +} + + +static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) +{ + Py_buffer buffer; + bool use_buffer = false; + + if (PyObject_CheckBuffer(ob)) { + PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT); + char format = *buffer.format; + if (ELEM(format, 'i', 'f', 'd')) { + use_buffer = true; } + else { + PyBuffer_Release(&buffer); + } + } - Py_DECREF(ob_seq_fast); + if (use_buffer) { + IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer); + PyBuffer_Release(&buffer); + return prop; } - else if (PyMapping_Check(ob)) { - PyObject *keys, *vals, *key, *pval; - int i, len; - /*yay! we get into recursive stuff now!*/ - keys = PyMapping_Keys(ob); - vals = PyMapping_Values(ob); - - /* we allocate the group first; if we hit any invalid data, - * we can delete it easily enough.*/ - prop = IDP_New(IDP_GROUP, &val, name); - len = PyMapping_Length(ob); - for (i = 0; i < len; i++) { - key = PySequence_GetItem(keys, i); - pval = PySequence_GetItem(vals, i); - if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { - IDP_FreeProperty(prop); - MEM_freeN(prop); - Py_XDECREF(keys); - Py_XDECREF(vals); - Py_XDECREF(key); - Py_XDECREF(pval); - /* error is already set */ - return false; - } + else { + PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop"); + if (ob_seq_fast != NULL) { + IDProperty *prop = idp_from_PySequence_Fast(name, ob_seq_fast); + Py_DECREF(ob_seq_fast); + return prop; + } + else { + return NULL; + } + } +} + +static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob) +{ + IDProperty *prop; + IDPropertyTemplate val = {0}; + + PyObject *keys, *vals, *key, *pval; + int i, len; + /* yay! we get into recursive stuff now! */ + keys = PyMapping_Keys(ob); + vals = PyMapping_Values(ob); + + /* we allocate the group first; if we hit any invalid data, + * we can delete it easily enough.*/ + prop = IDP_New(IDP_GROUP, &val, name); + len = PyMapping_Length(ob); + for (i = 0; i < len; i++) { + key = PySequence_GetItem(keys, i); + pval = PySequence_GetItem(vals, i); + if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { + IDP_FreeProperty(prop); + MEM_freeN(prop); + Py_XDECREF(keys); + Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); + /* error is already set */ + return NULL; } - Py_XDECREF(keys); - Py_XDECREF(vals); + Py_XDECREF(key); + Py_XDECREF(pval); + } + Py_XDECREF(keys); + Py_XDECREF(vals); + return prop; +} + +static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob) +{ + const char *name = idp_try_read_name(name_obj); + if (name == NULL) { + return NULL; + } + + if (PyFloat_Check(ob)) { + return idp_from_PyFloat(name, ob); + } + else if (PyLong_Check(ob)) { + return idp_from_PyLong(name, ob); + } + else if (PyUnicode_Check(ob)) { + return idp_from_PyUnicode(name, ob); + } + else if (PyBytes_Check(ob)) { + return idp_from_PyBytes(name, ob); + } + else if (PySequence_Check(ob)) { + return idp_from_PySequence(name, ob); + } + else if (PyMapping_Check(ob)) { + return idp_from_PyMapping(name, ob); } else { PyErr_Format(PyExc_TypeError, "invalid id-property type %.200s not supported", Py_TYPE(ob)->tp_name); + return NULL; + } +} + +/* -------------------------------------------------------------------------- */ + +/** + * \note group can be a pointer array or a group. + * assume we already checked key is a string. + * + * \return success. + */ +bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) +{ + IDProperty *prop = idp_from_PyObject(name_obj, ob); + if (prop == NULL) { return false; } if (group->type == IDP_IDPARRAY) { IDP_AppendArray(group, prop); - // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ + /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ MEM_freeN(prop); } else { @@ -1371,6 +1490,44 @@ static PyMappingMethods BPy_IDArray_AsMapping = { (objobjargproc)BPy_IDArray_ass_subscript }; +static int itemsize_by_idarray_type(int array_type) +{ + if (array_type == IDP_INT) return sizeof(int); + if (array_type == IDP_FLOAT) return sizeof(float); + if (array_type == IDP_DOUBLE) return sizeof(double); + return -1; /* should never happen */ +} + +static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags) +{ + IDProperty *prop = self->prop; + int itemsize = itemsize_by_idarray_type(prop->subtype); + int length = itemsize * prop->len; + + if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) { + return -1; + } + + view->itemsize = itemsize; + view->format = (char *)idp_format_from_array_type(prop->subtype); + + Py_ssize_t *shape = MEM_mallocN(sizeof(Py_ssize_t), __func__); + shape[0] = prop->len; + view->shape = shape; + + return 0; +} + +static void BPy_IDArray_releasebuffer(BPy_IDArray *UNUSED(self), Py_buffer *view) +{ + MEM_freeN(view->shape); +} + +static PyBufferProcs BPy_IDArray_Buffer = { + (getbufferproc)BPy_IDArray_getbuffer, + (releasebufferproc)BPy_IDArray_releasebuffer, +}; + PyTypeObject BPy_IDArray_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -1403,7 +1560,7 @@ PyTypeObject BPy_IDArray_Type = { NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ + &BPy_IDArray_Buffer, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index 0a9cb044571..7bf68c16cc7 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -3,6 +3,7 @@ # ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose import bpy import unittest +import numpy as np from array import array @@ -75,7 +76,7 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase): mylist = [1.2, 3.4, 5.6] self.id["a"] = array("f", mylist) self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist) - self.assertEqual(self.id["a"].typecode, "d") + self.assertEqual(self.id["a"].typecode, "f") def test_sequence_double_array(self): mylist = [1.2, 3.4, 5.6] @@ -138,6 +139,65 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase): self.id["a"] = self +class TestBufferProtocol(TestHelper, unittest.TestCase): + + def test_int(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 5) + a[2] = 10 + self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5]) + + def test_float(self): + self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 4) + a[-1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0]) + + def test_double(self): + self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0]) + + def test_full_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[:] = [10, 20, 30, 40, 50, 60] + self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60]) + + def test_partial_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1:5] = [10, 20, 30, 40] + self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8]) + + def test_copy(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5]) + self.id["b"] = self.id["a"] + self.assertEqual(self.id["a"].to_list(), self.id["b"].to_list()) + + def test_memview_attributes(self): + mylist = [1, 2, 3] + self.id["a"] = mylist + + view1 = memoryview(self.id["a"]) + view2 = memoryview(array("i", mylist)) + + self.assertEqualMemviews(view1, view2) + + def assertEqualMemviews(self, view1, view2): + props_to_compare = ( + "contiguous", "format", "itemsize", "nbytes", "ndim", + "readonly", "shape", "strides", "suboffsets" + ) + for attr in props_to_compare: + self.assertEqual(getattr(view1, attr), getattr(view2, attr)) + + self.assertEqual(list(view1), list(view2)) + self.assertEqual(view1.tobytes(), view2.tobytes()) + if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) |