Bpy.data.images perf issues

I took a quick stab at using the buffer protocol, it’s working and looking rather hopeful, but it’s gonna need some spit and polish and testing before i can submit it.

size time in seconds
512px 0.004001s
1024px 0.015502s
2048px 0.056007s
4096px 0.208527s
8192px 0.86661s

just in case anyone wants to play

 source/blender/python/intern/bpy_rna_array.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c
index 4cc3c4c0fae..4a6943fd41f 100644
--- a/source/blender/python/intern/bpy_rna_array.c
+++ b/source/blender/python/intern/bpy_rna_array.c
@@ -303,6 +303,15 @@ static int validate_array(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop,
 
 	/* validate type first because length validation may modify property array length */
 
+	/* TODO:  This needs some actual checking of things. */
+	if (PyObject_CheckBuffer(rvalue)) {
+		Py_buffer buf;
+		if (PyObject_GetBuffer(rvalue, &buf, PyBUF_C_CONTIGUOUS) == 0) {
+			*totitem = buf.len / buf.itemsize;
+			PyBuffer_Release(&buf);
+			return 0;
+		}
+	}
 
 #ifdef USE_MATHUTILS
 	if (lvalue_dim == 0) { /* only valid for first level array */
@@ -394,6 +403,15 @@ static char *copy_values(
 		return NULL;
 	}
 
+	if (PyObject_CheckBuffer(seq))
+	{
+		Py_buffer buf;
+		if (PyObject_GetBuffer(seq, &buf, PyBUF_C_CONTIGUOUS) == 0) {
+			memcpy(data, buf.buf, min(buf.len, seq_size * sizeof(float))); 
+			PyBuffer_Release(&buf);
+			return data;
+		}
+	}
 
 #ifdef USE_MATHUTILS
 	if (dim == 0) {

also updated benchmark code, cause the one in the opening post was accidentally producing doubles which needed conversion, so that took time/memory as well

import bpy
import numpy as np
import mathutils as ms 
from datetime import datetime

def benchmark(size):

    img = bpy.data.images.get("output")
    if (img == None):
        img = bpy.data.images.new("output", width=size, height=size, alpha=False, float_buffer=True)
    if (size != img.generated_width):
        img.generated_width = size
    if (size != img.generated_height):
        img.generated_height = size 

    pixel_data = np.ones(size*size*4, dtype=np.float32)

    t0 = datetime.now()
    img.pixels = pixel_data
    t1 = datetime.now()
    meth1 = (t1-t0).total_seconds()

    print("|%spx|%ss" % ( size , meth1 ))

benchmark(512)
benchmark(1024)
benchmark(2048)
benchmark(4096)
benchmark(8192)
6 Likes