Skip to content
Snippets Groups Projects
wrap_cl.hpp 124 KiB
Newer Older
  • Learn to ignore specific revisions
  •                   case CL_CGL_SHAREGROUP_KHR:
    #endif
    
                        value = py::cast(result[i+1]);
    
                        break;
    
    #endif
                      case 0:
                        break;
    
                      default:
                        throw error("Context.get_info", CL_INVALID_VALUE,
                            "unknown context_property key encountered");
                    }
    
                    py_result.append(py::make_tuple(result[i], value));
                  }
                  return py_result;
                }
    
    #if PYOPENCL_CL_VERSION >= 0x1010
              case CL_CONTEXT_NUM_DEVICES:
                PYOPENCL_GET_INTEGRAL_INFO(
                    Context, m_context, param_name, cl_uint);
    #endif
    
              default:
                throw error("Context.get_info", CL_INVALID_VALUE);
            }
          }
      };
    
    
      inline
      std::vector<cl_context_properties> parse_context_properties(
          py::object py_properties)
      {
        std::vector<cl_context_properties> props;
    
        if (py_properties.ptr() != Py_None)
        {
    
          for (py::handle prop_tuple_py: py_properties)
    
            py::tuple prop_tuple(prop_tuple_py.cast<py::tuple>());
    
    
            if (len(prop_tuple) != 2)
              throw error("Context", CL_INVALID_VALUE, "property tuple must have length 2");
    
            cl_context_properties prop = prop_tuple[0].cast<cl_context_properties>();
    
            props.push_back(prop);
    
            if (prop == CL_CONTEXT_PLATFORM)
            {
              props.push_back(
    
                  reinterpret_cast<cl_context_properties>(
                    prop_tuple[1].cast<const platform &>().data()));
    
            }
    #if defined(PYOPENCL_GL_SHARING_VERSION) && (PYOPENCL_GL_SHARING_VERSION >= 1)
    #if defined(_WIN32)
           else if (prop == CL_WGL_HDC_KHR)
           {
             // size_t is a stand-in for HANDLE, hopefully has the same size.
    
             size_t hnd = (prop_tuple[1]).cast<size_t>();
    
             props.push_back(hnd);
           }
    #endif
           else if (
    #if defined(__APPLE__) && defined(HAVE_GL)
                prop == CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE
    #else
                prop == CL_GL_CONTEXT_KHR
                || prop == CL_EGL_DISPLAY_KHR
                || prop == CL_GLX_DISPLAY_KHR
                || prop == CL_CGL_SHAREGROUP_KHR
    #endif
               )
           {
    
              py::object ctypes = py::module::import("ctypes");
    
              py::object prop = prop_tuple[1], c_void_p = ctypes.attr("c_void_p");
              py::object ptr = ctypes.attr("cast")(prop, c_void_p);
    
              props.push_back(ptr.attr("value").cast<cl_context_properties>());
    
           }
    #endif
            else
              throw error("Context", CL_INVALID_VALUE, "invalid context property");
          }
          props.push_back(0);
        }
    
        return props;
      }
    
    
      inline
      context *create_context_inner(py::object py_devices, py::object py_properties,
          py::object py_dev_type)
      {
        std::vector<cl_context_properties> props
          = parse_context_properties(py_properties);
    
        cl_context_properties *props_ptr
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
          = props.empty( ) ? nullptr : &props.front();
    
    
        cl_int status_code;
    
        cl_context ctx;
    
        // from device list
        if (py_devices.ptr() != Py_None)
        {
          if (py_dev_type.ptr() != Py_None)
            throw error("Context", CL_INVALID_VALUE,
                "one of 'devices' or 'dev_type' must be None");
    
          std::vector<cl_device_id> devices;
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
          for (py::handle py_dev: py_devices)
    
            devices.push_back(py_dev.cast<const device &>().data());
    
    
          PYOPENCL_PRINT_CALL_TRACE("clCreateContext");
          ctx = clCreateContext(
              props_ptr,
              devices.size(),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
              devices.empty( ) ? nullptr : &devices.front(),
    
              0, 0, &status_code);
        }
        // from dev_type
        else
        {
          cl_device_type dev_type = CL_DEVICE_TYPE_DEFAULT;
          if (py_dev_type.ptr() != Py_None)
    
            dev_type = py_dev_type.cast<cl_device_type>();
    
    
          PYOPENCL_PRINT_CALL_TRACE("clCreateContextFromType");
          ctx = clCreateContextFromType(props_ptr, dev_type, 0, 0, &status_code);
        }
    
        if (status_code != CL_SUCCESS)
          throw pyopencl::error("Context", status_code);
    
        try
        {
          return new context(ctx, false);
        }
        catch (...)
        {
          PYOPENCL_CALL_GUARDED(clReleaseContext, (ctx));
          throw;
        }
      }
    
    
      inline
      context *create_context(py::object py_devices, py::object py_properties,
          py::object py_dev_type)
      {
        PYOPENCL_RETRY_RETURN_IF_MEM_ERROR(
          return create_context_inner(py_devices, py_properties, py_dev_type);
        )
      }
    
      // }}}
    
    
      // {{{ command_queue
    
      class command_queue
      {
        private:
          cl_command_queue m_queue;
    
        public:
          command_queue(cl_command_queue q, bool retain)
            : m_queue(q)
          {
            if (retain)
              PYOPENCL_CALL_GUARDED(clRetainCommandQueue, (q));
          }
    
          command_queue(command_queue const &src)
            : m_queue(src.m_queue)
          {
            PYOPENCL_CALL_GUARDED(clRetainCommandQueue, (m_queue));
          }
    
          command_queue(
              const context &ctx,
              const device *py_dev=0,
              cl_command_queue_properties props=0)
          {
            cl_device_id dev;
            if (py_dev)
              dev = py_dev->data();
            else
            {
              std::vector<cl_device_id> devs;
              PYOPENCL_GET_VEC_INFO(Context, ctx.data(), CL_CONTEXT_DEVICES, devs);
              if (devs.size() == 0)
                throw pyopencl::error("CommandQueue", CL_INVALID_VALUE,
                    "context doesn't have any devices? -- don't know which one to default to");
              dev = devs[0];
            }
    
            cl_int status_code;
            PYOPENCL_PRINT_CALL_TRACE("clCreateCommandQueue");
            m_queue = clCreateCommandQueue(
                ctx.data(), dev, props, &status_code);
    
            if (status_code != CL_SUCCESS)
              throw pyopencl::error("CommandQueue", status_code);
          }
    
          ~command_queue()
          {
            PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseCommandQueue,
                (m_queue));
          }
    
          const cl_command_queue data() const
          { return m_queue; }
    
          PYOPENCL_EQUALITY_TESTS(command_queue);
    
          py::object get_info(cl_command_queue_info param_name) const
          {
            switch (param_name)
            {
              case CL_QUEUE_CONTEXT:
                PYOPENCL_GET_OPAQUE_INFO(CommandQueue, m_queue, param_name,
                    cl_context, context);
              case CL_QUEUE_DEVICE:
                PYOPENCL_GET_OPAQUE_INFO(CommandQueue, m_queue, param_name,
                    cl_device_id, device);
              case CL_QUEUE_REFERENCE_COUNT:
                PYOPENCL_GET_INTEGRAL_INFO(CommandQueue, m_queue, param_name,
                    cl_uint);
              case CL_QUEUE_PROPERTIES:
                PYOPENCL_GET_INTEGRAL_INFO(CommandQueue, m_queue, param_name,
                    cl_command_queue_properties);
    
              default:
                throw error("CommandQueue.get_info", CL_INVALID_VALUE);
            }
          }
    
    
          std::unique_ptr<context> get_context() const
    
          {
            cl_context param_value;
            PYOPENCL_CALL_GUARDED(clGetCommandQueueInfo,
                (m_queue, CL_QUEUE_CONTEXT, sizeof(param_value), &param_value, 0));
    
            return std::unique_ptr<context>(
    
                new context(param_value, /*retain*/ true));
          }
    
    #if PYOPENCL_CL_VERSION < 0x1010
          cl_command_queue_properties set_property(
              cl_command_queue_properties prop,
              bool enable)
          {
            cl_command_queue_properties old_prop;
            PYOPENCL_CALL_GUARDED(clSetCommandQueueProperty,
                (m_queue, prop, PYOPENCL_CAST_BOOL(enable), &old_prop));
            return old_prop;
          }
    #endif
    
          void flush()
          { PYOPENCL_CALL_GUARDED(clFlush, (m_queue)); }
          void finish()
          { PYOPENCL_CALL_GUARDED_THREADED(clFinish, (m_queue)); }
      };
    
      // }}}
    
    
      // {{{ event/synchronization
    
    
      class event : noncopyable
    
      {
        private:
          cl_event m_event;
    
        public:
          event(cl_event event, bool retain)
            : m_event(event)
          {
            if (retain)
              PYOPENCL_CALL_GUARDED(clRetainEvent, (event));
          }
    
          event(event const &src)
            : m_event(src.m_event)
          { PYOPENCL_CALL_GUARDED(clRetainEvent, (m_event)); }
    
          virtual ~event()
          {
            PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseEvent,
                (m_event));
          }
    
          const cl_event data() const
          { return m_event; }
    
          PYOPENCL_EQUALITY_TESTS(event);
    
          py::object get_info(cl_event_info param_name) const
          {
            switch (param_name)
            {
              case CL_EVENT_COMMAND_QUEUE:
                PYOPENCL_GET_OPAQUE_INFO(Event, m_event, param_name,
                    cl_command_queue, command_queue);
              case CL_EVENT_COMMAND_TYPE:
                PYOPENCL_GET_INTEGRAL_INFO(Event, m_event, param_name,
                    cl_command_type);
              case CL_EVENT_COMMAND_EXECUTION_STATUS:
                PYOPENCL_GET_INTEGRAL_INFO(Event, m_event, param_name,
                    cl_int);
              case CL_EVENT_REFERENCE_COUNT:
                PYOPENCL_GET_INTEGRAL_INFO(Event, m_event, param_name,
                    cl_uint);
    #if PYOPENCL_CL_VERSION >= 0x1010
              case CL_EVENT_CONTEXT:
                PYOPENCL_GET_OPAQUE_INFO(Event, m_event, param_name,
                    cl_context, context);
    #endif
    
              default:
                throw error("Event.get_info", CL_INVALID_VALUE);
            }
          }
    
          py::object get_profiling_info(cl_profiling_info param_name) const
          {
            switch (param_name)
            {
              case CL_PROFILING_COMMAND_QUEUED:
              case CL_PROFILING_COMMAND_SUBMIT:
              case CL_PROFILING_COMMAND_START:
              case CL_PROFILING_COMMAND_END:
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
    #if PYOPENCL_CL_VERSION >= 0x2000
              case CL_PROFILING_COMMAND_COMPLETE:
    #endif
    
                PYOPENCL_GET_INTEGRAL_INFO(EventProfiling, m_event, param_name,
                    cl_ulong);
              default:
                throw error("Event.get_profiling_info", CL_INVALID_VALUE);
            }
          }
    
          virtual void wait()
          {
            PYOPENCL_CALL_GUARDED_THREADED(clWaitForEvents, (1, &m_event));
          }
    
    
    #if PYOPENCL_CL_VERSION >= 0x1010
        // {{{ set_callback, by way of a a thread-based construction
    
        private:
          struct event_callback_info_t
          {
            std::mutex m_mutex;
            std::condition_variable m_condvar;
    
            py::object m_py_event;
            py::object m_py_callback;
    
            bool m_set_callback_suceeded;
    
            cl_event m_event;
            cl_int m_command_exec_status;
    
            event_callback_info_t(py::object py_event, py::object py_callback)
    
            : m_py_event(py_event), m_py_callback(py_callback), m_set_callback_suceeded(true)
    
            {}
          };
    
          static void evt_callback(cl_event evt, cl_int command_exec_status, void *user_data)
          {
            event_callback_info_t *cb_info = reinterpret_cast<event_callback_info_t *>(user_data);
            {
              std::lock_guard<std::mutex> lg(cb_info->m_mutex);
              cb_info->m_event = evt;
              cb_info->m_command_exec_status = command_exec_status;
            }
            cb_info->m_condvar.notify_one();
          }
    
        public:
          void set_callback(cl_int command_exec_callback_type, py::object pfn_event_notify)
          {
            // The reason for doing this via a thread is that we're able to wait on
            // acquiring the GIL. (which we can't in the callback)
    
            std::unique_ptr<event_callback_info_t> cb_info_holder(
                new event_callback_info_t(
                  handle_from_new_ptr(new event(*this)),
                  pfn_event_notify));
            event_callback_info_t *cb_info = cb_info_holder.get();
    
            std::thread notif_thread([cb_info]()
                {
                  std::unique_lock<std::mutex> ulk(cb_info->m_mutex);
                  cb_info->m_condvar.wait(ulk);
    
                  {
                    py::gil_scoped_acquire acquire;
    
                    if (cb_info->m_set_callback_suceeded)
                    {
                      try {
                        cb_info->m_py_callback(
                            // cb_info->m_py_event,
                            cb_info->m_command_exec_status);
                      }
                      catch (std::exception &exc)
                      {
                        std::cerr
                        << "[pyopencl] event callback handler threw an exception, ignoring: "
                        << exc.what()
                        << std::endl;
                      }
                    }
    
                    // Need to hold GIL to delete py::object instances in
                    // event_callback_info_t
                    delete cb_info;
                  }
                });
            // Thread is away--it is now its responsibility to free cb_info.
            cb_info_holder.release();
    
            // notif_thread should no longer be coupled to the lifetime of the thread.
            notif_thread.detach();
    
            try
            {
              PYOPENCL_CALL_GUARDED(clSetEventCallback, (
                    data(), command_exec_callback_type, &event::evt_callback, cb_info));
            }
            catch (...) {
              // Setting the callback did not succeed. The thread would never
              // be woken up. Wake it up to let it know that it can stop.
              {
                std::lock_guard<std::mutex> lg(cb_info->m_mutex);
                cb_info->m_set_callback_suceeded = false;
              }
              cb_info->m_condvar.notify_one();
              throw;
            }
          }
          // }}}
    #endif
    
      };
    
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
      class nanny_event : public event
      {
        // In addition to everything an event does, the nanny event holds a reference
        // to a Python object and waits for its own completion upon destruction.
    
        protected:
    
          std::unique_ptr<py_buffer_wrapper> m_ward;
    
          nanny_event(cl_event evt, bool retain, std::unique_ptr<py_buffer_wrapper> &ward)
    
            : event(evt, retain), m_ward(std::move(ward))
    
          { }
    
          ~nanny_event()
          { wait(); }
    
          py::object get_ward() const
          {
            if (m_ward.get())
            {
    
              return py::reinterpret_borrow<py::object>(m_ward->m_buf.obj);
    
              return py::none();
    
          }
    
          virtual void wait()
          {
            event::wait();
            m_ward.reset();
          }
      };
    #else
      class nanny_event : public event
      {
        // In addition to everything an event does, the nanny event holds a reference
        // to a Python object and waits for its own completion upon destruction.
    
        protected:
          py::object        m_ward;
    
        public:
    
          nanny_event(cl_event evt, bool retain, py::object ward)
            : event(evt, retain), m_ward(ward)
          { }
    
          nanny_event(nanny_event const &src)
            : event(src), m_ward(src.m_ward)
          { }
    
          ~nanny_event()
          { wait(); }
    
          py::object get_ward() const
          { return m_ward; }
    
          virtual void wait()
          {
            event::wait();
    
            m_ward = py::none();
    
          }
      };
    #endif
    
    
    
    
      inline
      void wait_for_events(py::object events)
      {
        cl_uint num_events_in_wait_list = 0;
        std::vector<cl_event> event_wait_list(len(events));
    
    
        for (py::handle evt: events)
    
          event_wait_list[num_events_in_wait_list++] =
    
            evt.cast<event &>().data();
    
    
        PYOPENCL_CALL_GUARDED_THREADED(clWaitForEvents, (
              PYOPENCL_WAITLIST_ARGS));
      }
    
    
    
    
    #if PYOPENCL_CL_VERSION >= 0x1020
      inline
      event *enqueue_marker_with_wait_list(command_queue &cq,
          py::object py_wait_for)
      {
        PYOPENCL_PARSE_WAIT_FOR;
        cl_event evt;
    
        PYOPENCL_CALL_GUARDED(clEnqueueMarkerWithWaitList, (
              cq.data(), PYOPENCL_WAITLIST_ARGS, &evt));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
      inline
      event *enqueue_barrier_with_wait_list(command_queue &cq,
          py::object py_wait_for)
      {
        PYOPENCL_PARSE_WAIT_FOR;
        cl_event evt;
    
        PYOPENCL_CALL_GUARDED(clEnqueueBarrierWithWaitList,
            (cq.data(), PYOPENCL_WAITLIST_ARGS, &evt));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    #endif
    
    
      // {{{ used internally for pre-OpenCL-1.2 contexts
    
      inline
      event *enqueue_marker(command_queue &cq)
      {
        cl_event evt;
    
        PYOPENCL_CALL_GUARDED(clEnqueueMarker, (
              cq.data(), &evt));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
      inline
      void enqueue_wait_for_events(command_queue &cq, py::object py_events)
      {
        cl_uint num_events = 0;
        std::vector<cl_event> event_list(len(py_events));
    
    
        for (py::handle py_evt: py_events)
    
          event_list[num_events++] = py_evt.cast<event &>().data();
    
    
        PYOPENCL_CALL_GUARDED(clEnqueueWaitForEvents, (
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
              cq.data(), num_events, event_list.empty( ) ? nullptr : &event_list.front()));
    
      }
    
      inline
      void enqueue_barrier(command_queue &cq)
      {
        PYOPENCL_CALL_GUARDED(clEnqueueBarrier, (cq.data()));
      }
    
      // }}}
    
    
    #if PYOPENCL_CL_VERSION >= 0x1010
      class user_event : public event
      {
        public:
          user_event(cl_event evt, bool retain)
            : event(evt, retain)
          { }
    
          void set_status(cl_int execution_status)
          {
            PYOPENCL_CALL_GUARDED(clSetUserEventStatus, (data(), execution_status));
          }
      };
    
    
    
    
      inline
    
      user_event *create_user_event(context &ctx)
    
      {
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clCreateUserEvent");
        cl_event evt = clCreateUserEvent(ctx.data(), &status_code);
    
        if (status_code != CL_SUCCESS)
          throw pyopencl::error("UserEvent", status_code);
    
        try
        {
          return new user_event(evt, false);
        }
        catch (...)
        {
          clReleaseEvent(evt);
          throw;
        }
      }
    
    #endif
    
      // }}}
    
    
      // {{{ memory_object
    
      py::object create_mem_object_wrapper(cl_mem mem);
    
      class memory_object_holder
      {
        public:
          virtual const cl_mem data() const = 0;
    
          PYOPENCL_EQUALITY_TESTS(memory_object_holder);
    
          size_t size() const
          {
            size_t param_value;
            PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
                (data(), CL_MEM_SIZE, sizeof(param_value), &param_value, 0));
            return param_value;
          }
    
          py::object get_info(cl_mem_info param_name) const;
      };
    
    
    
    
    
      class memory_object : noncopyable, public memory_object_holder
    
      {
        public:
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
    
          typedef std::unique_ptr<py_buffer_wrapper> hostbuf_t;
    
    #else
          typedef py::object hostbuf_t;
    #endif
    
        private:
          bool m_valid;
          cl_mem m_mem;
          hostbuf_t m_hostbuf;
    
        public:
          memory_object(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
            : m_valid(true), m_mem(mem)
          {
            if (retain)
              PYOPENCL_CALL_GUARDED(clRetainMemObject, (mem));
    
    
            m_hostbuf = PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf);
    
          }
    
          memory_object(memory_object &src)
    
            : m_valid(true), m_mem(src.m_mem),
    
            m_hostbuf(PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(src.m_hostbuf))
    
          {
            PYOPENCL_CALL_GUARDED(clRetainMemObject, (m_mem));
          }
    
          memory_object(memory_object_holder const &src)
            : m_valid(true), m_mem(src.data())
          {
            PYOPENCL_CALL_GUARDED(clRetainMemObject, (m_mem));
          }
    
          void release()
          {
            if (!m_valid)
                throw error("MemoryObject.free", CL_INVALID_VALUE,
                    "trying to double-unref mem object");
            PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseMemObject, (m_mem));
            m_valid = false;
          }
    
          virtual ~memory_object()
          {
            if (m_valid)
              release();
          }
    
          py::object hostbuf()
          {
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
            if (m_hostbuf.get())
    
              return py::reinterpret_borrow<py::object>(m_hostbuf->m_buf.obj);
    
              return py::none();
    
    #else
            return m_hostbuf;
    #endif
          }
    
          const cl_mem data() const
          { return m_mem; }
    
      };
    
    #if PYOPENCL_CL_VERSION >= 0x1020
      inline
      event *enqueue_migrate_mem_objects(
          command_queue &cq,
          py::object py_mem_objects,
          cl_mem_migration_flags flags,
          py::object py_wait_for)
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        std::vector<cl_mem> mem_objects;
    
        for (py::handle mo: py_mem_objects)
    
          mem_objects.push_back(mo.cast<const memory_object &>().data());
    
    
        cl_event evt;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          PYOPENCL_CALL_GUARDED(clEnqueueMigrateMemObjects, (
                cq.data(),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
                mem_objects.size(), mem_objects.empty( ) ? nullptr : &mem_objects.front(),
    
                flags,
                PYOPENCL_WAITLIST_ARGS, &evt
                ));
          );
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    #endif
    
      // }}}
    
    
      // {{{ buffer
    
      inline cl_mem create_buffer(
          cl_context ctx,
          cl_mem_flags flags,
          size_t size,
          void *host_ptr)
      {
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clCreateBuffer");
        cl_mem mem = clCreateBuffer(ctx, flags, size, host_ptr, &status_code);
    
        if (status_code != CL_SUCCESS)
          throw pyopencl::error("create_buffer", status_code);
    
        return mem;
      }
    
    
    
    
      inline cl_mem create_buffer_gc(
          cl_context ctx,
          cl_mem_flags flags,
          size_t size,
          void *host_ptr)
      {
        PYOPENCL_RETRY_RETURN_IF_MEM_ERROR(
          return create_buffer(ctx, flags, size, host_ptr);
        );
      }
    
    
    
    #if PYOPENCL_CL_VERSION >= 0x1010
      inline cl_mem create_sub_buffer(
          cl_mem buffer, cl_mem_flags flags, cl_buffer_create_type bct,
          const void *buffer_create_info)
      {
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clCreateSubBuffer");
        cl_mem mem = clCreateSubBuffer(buffer, flags,
            bct, buffer_create_info, &status_code);
    
        if (status_code != CL_SUCCESS)
          throw pyopencl::error("clCreateSubBuffer", status_code);
    
        return mem;
      }
    
    
    
    
      inline cl_mem create_sub_buffer_gc(
          cl_mem buffer, cl_mem_flags flags, cl_buffer_create_type bct,
          const void *buffer_create_info)
      {
        PYOPENCL_RETRY_RETURN_IF_MEM_ERROR(
          return create_sub_buffer(buffer, flags, bct, buffer_create_info);
        );
      }
    #endif
    
    
    
      class buffer : public memory_object
      {
        public:
          buffer(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
    
            : memory_object(mem, retain, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf))
    
          { }
    
    #if PYOPENCL_CL_VERSION >= 0x1010
          buffer *get_sub_region(
              size_t origin, size_t size, cl_mem_flags flags) const
          {
            cl_buffer_region region = { origin, size};
    
            cl_mem mem = create_sub_buffer_gc(
                data(), flags, CL_BUFFER_CREATE_TYPE_REGION, &region);
    
            try
            {
              return new buffer(mem, false);
            }
            catch (...)
            {
              PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
              throw;
            }
          }
    
          buffer *getitem(py::slice slc) const
          {
            PYOPENCL_BUFFER_SIZE_T start, end, stride, length;
    
            size_t my_length;
            PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
                (data(), CL_MEM_SIZE, sizeof(my_length), &my_length, 0));
    
    #if PY_VERSION_HEX >= 0x03020000
            if (PySlice_GetIndicesEx(slc.ptr(),
    #else
            if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject *>(slc.ptr()),
    #endif
                  my_length, &start, &end, &stride, &length) != 0)
              throw py::error_already_set();
    
            if (stride != 1)
              throw pyopencl::error("Buffer.__getitem__", CL_INVALID_VALUE,
                  "Buffer slice must have stride 1");
    
            cl_mem_flags my_flags;
            PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
                (data(), CL_MEM_FLAGS, sizeof(my_flags), &my_flags, 0));
    
            my_flags &= ~CL_MEM_COPY_HOST_PTR;
    
            if (end <= start)
              throw pyopencl::error("Buffer.__getitem__", CL_INVALID_VALUE,
                  "Buffer slice have end > start");
    
            return get_sub_region(start, end-start, my_flags);
          }
    #endif
      };
    
      // {{{ buffer creation
    
      inline
      buffer *create_buffer_py(
          context &ctx,
          cl_mem_flags flags,
          size_t size,
          py::object py_hostbuf
          )
      {
        if (py_hostbuf.ptr() != Py_None &&
            !(flags & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)))
          PyErr_Warn(PyExc_UserWarning, "'hostbuf' was passed, "
              "but no memory flags to make use of it.");
    
        void *buf = 0;
    
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
    
        std::unique_ptr<py_buffer_wrapper> retained_buf_obj;
    
        if (py_hostbuf.ptr() != Py_None)
        {
    
          retained_buf_obj = std::unique_ptr<py_buffer_wrapper>(new py_buffer_wrapper);
    
    
          int py_buf_flags = PyBUF_ANY_CONTIGUOUS;
          if ((flags & CL_MEM_USE_HOST_PTR)
              && ((flags & CL_MEM_READ_WRITE)
                || (flags & CL_MEM_WRITE_ONLY)))
            py_buf_flags |= PyBUF_WRITABLE;
    
          retained_buf_obj->get(py_hostbuf.ptr(), py_buf_flags);
    
          buf = retained_buf_obj->m_buf.buf;
    
          if (size > size_t(retained_buf_obj->m_buf.len))
            throw pyopencl::error("Buffer", CL_INVALID_VALUE,
                "specified size is greater than host buffer size");
          if (size == 0)
            size = retained_buf_obj->m_buf.len;
        }
    #else
        py::object retained_buf_obj;
        if (py_hostbuf.ptr() != Py_None)
        {
          PYOPENCL_BUFFER_SIZE_T len;
          if ((flags & CL_MEM_USE_HOST_PTR)
              && ((flags & CL_MEM_READ_WRITE)
                || (flags & CL_MEM_WRITE_ONLY)))
          {
            if (PyObject_AsWriteBuffer(py_hostbuf.ptr(), &buf, &len))
              throw py::error_already_set();
          }
          else
          {
            if (PyObject_AsReadBuffer(
                  py_hostbuf.ptr(), const_cast<const void **>(&buf), &len))
              throw py::error_already_set();
          }
    
          if (flags & CL_MEM_USE_HOST_PTR)
            retained_buf_obj = py_hostbuf;
    
          if (size > size_t(len))
            throw pyopencl::error("Buffer", CL_INVALID_VALUE,
                "specified size is greater than host buffer size");
          if (size == 0)
            size = len;
        }
    #endif
    
        cl_mem mem = create_buffer_gc(ctx.data(), flags, size, buf);
    
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
        if (!(flags & CL_MEM_USE_HOST_PTR))
          retained_buf_obj.reset();
    #endif
    
        try
        {
    
          return new buffer(mem, false, PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(retained_buf_obj));
    
        }
        catch (...)
        {
          PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
          throw;
        }
      }
    
      // }}}
    
      // {{{ buffer transfers
    
      // {{{ byte-for-byte transfers
    
      inline
      event *enqueue_read_buffer(
          command_queue &cq,
          memory_object_holder &mem,
          py::object buffer,
          size_t device_offset,
          py::object py_wait_for,
          bool is_blocking)
      {
        PYOPENCL_PARSE_WAIT_FOR;