From e761fdb8874e00ee14b7b64336b744429349ef79 Mon Sep 17 00:00:00 2001 From: Marko Bencun <mbencun@gmail.com> Date: Wed, 11 Sep 2013 13:55:51 +0200 Subject: [PATCH] improved buffer interface --- pyopencl/__init__.py | 1 - pyopencl/cffi_cl.py | 46 +++++++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py index 52862916..2ad94445 100644 --- a/pyopencl/__init__.py +++ b/pyopencl/__init__.py @@ -475,7 +475,6 @@ def _add_functionality(): self.set_arg(i, arg) else: from pyopencl._pvt_struct import pack - for i, (arg, arg_type_char) in enumerate( zip(args, arg_type_chars)): if arg_type_char and arg_type_char != "V": diff --git a/pyopencl/cffi_cl.py b/pyopencl/cffi_cl.py index 16edab64..fe255b57 100644 --- a/pyopencl/cffi_cl.py +++ b/pyopencl/cffi_cl.py @@ -204,18 +204,35 @@ class MemoryObjectHolder(_Common): class MemoryObject(MemoryObjectHolder): pass - def _c_buffer_from_obj(obj, writable=False): + """ + Convert a Python object to a tuple (cdata('void *'), num_bytes, dummy) to be able to pass + a data stream to a C function. The dummy variable exists only to ensure that the Python object referencing the + C buffer is not garbage collected at the end of this function, making the C buffer itself invalid. + """ + if obj is None: return _ffi.NULL, 0 - # numpy array - if hasattr(obj, '__array_interface__'): # if writeable == False, some tests fail... - return _ffi.cast('void *', obj.__array_interface__['data'][0]), obj.nbytes + # CPYthon: use the old buffer protocol + + # {{{ special case: numpy (also works with numpypy) + if isinstance(obj, np.ndarray): + # numpy array + return _ffi.cast('void *', obj.__array_interface__['data'][0]), obj.nbytes, None + if isinstance(obj, np.generic): + # numpy scalar + # * obj.__array_interface__ exists in CPython, but the address does not seem to point + # to the actual scalar (not supported/bug?). + # * does not exist (yet?) in numpypy. + s_array = np.array([obj]) # obj[()] not supported yet by numpypy + return _ffi.cast('void *', s_array.__array_interface__['data'][0]), s_array.nbytes, s_array - # ... ? + # }}} + + # TODO: is there a cross-interpreter solution? - # fall back to old CPython buffer protocol API + # {{{ fall back to the old CPython buffer protocol API addr = ctypes.c_void_p() length = ctypes.c_ssize_t() try: @@ -228,7 +245,10 @@ def _c_buffer_from_obj(obj, writable=False): else: if status: raise Exception('TODO error_already_set') - return _ffi.cast('void *', addr.value), length.value + print addr.value, length + return _ffi.cast('void *', addr.value), length.value, None + + # }}} class Buffer(MemoryObject): _id = 'buffer' @@ -237,7 +257,7 @@ class Buffer(MemoryObject): warnings.warn("'hostbuf' was passed, but no memory flags to make use of it.") c_hostbuf = _ffi.NULL if hostbuf is not None: - c_hostbuf, hostbuf_size = _c_buffer_from_obj(hostbuf, writable=flags & mem_flags.USE_HOST_PTR) + c_hostbuf, hostbuf_size, _ = _c_buffer_from_obj(hostbuf, writable=flags & mem_flags.USE_HOST_PTR) if size > hostbuf_size: raise RuntimeError("Buffer", status_code.INVALID_VALUE, "specified size is greater than host buffer size") if size == 0: @@ -383,7 +403,7 @@ class Kernel(_Common): _handle_error(_lib.kernel__set_arg_sampler(self.ptr, arg_index, arg.ptr)) else: # todo: how to handle args other than numpy arrays? - c_buf, size = _c_buffer_from_obj(arg) + c_buf, size, _ = _c_buffer_from_obj(arg) _handle_error(_lib.kernel__set_arg_buf(self.ptr, arg_index, c_buf, size)) def get_work_group_info(self, param, device): @@ -467,7 +487,7 @@ def _c_obj_list(objs=None): return _ffi.new('void *[]', [ev.ptr for ev in objs]), len(objs) def _enqueue_read_buffer(queue, mem, hostbuf, device_offset=0, wait_for=None, is_blocking=True): - c_buf, size = _c_buffer_from_obj(hostbuf, writable=True) + c_buf, size, _ = _c_buffer_from_obj(hostbuf, writable=True) ptr_event = _ffi.new('void **') c_wait_for, num_wait_for = _c_obj_list(wait_for) _handle_error(_lib._enqueue_read_buffer( @@ -498,7 +518,7 @@ def _enqueue_copy_buffer(queue, src, dst, byte_count=-1, src_offset=0, dst_offse return _create_instance(Event, ptr_event[0]) def _enqueue_write_buffer(queue, mem, hostbuf, device_offset=0, wait_for=None, is_blocking=True): - c_buf, size = _c_buffer_from_obj(hostbuf) + c_buf, size, _ = _c_buffer_from_obj(hostbuf) ptr_event = _ffi.new('void **') c_wait_for, num_wait_for = _c_obj_list(wait_for) _handle_error(_lib._enqueue_write_buffer( @@ -514,7 +534,7 @@ def _enqueue_write_buffer(queue, mem, hostbuf, device_offset=0, wait_for=None, i return _create_instance(Event, ptr_event[0]) def _enqueue_read_image(queue, mem, origin, region, hostbuf, row_pitch=0, slice_pitch=0, wait_for=None, is_blocking=True): - c_buf, size = _c_buffer_from_obj(hostbuf, writable=True) + c_buf, size, _ = _c_buffer_from_obj(hostbuf, writable=True) ptr_event = _ffi.new('void **') c_wait_for, num_wait_for = _c_obj_list(wait_for) _handle_error(_lib._enqueue_read_image( @@ -675,7 +695,7 @@ class Image(MemoryObject): if shape is None: raise LogicError("Image", status_code.INVALID_VALUE, "'shape' must be given") - c_buf, size = _c_buffer_from_obj(buffer, writable=flags & mem_flags.USE_HOST_PTR) + c_buf, size, _ = _c_buffer_from_obj(buffer, writable=flags & mem_flags.USE_HOST_PTR) dims = len(shape) if dims == 2: -- GitLab