Skip to content
Snippets Groups Projects
wrap_cl.hpp 136 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    
    
    
      inline
      event *enqueue_copy_image(
          command_queue &cq,
          memory_object_holder &src,
          memory_object_holder &dest,
          py::object py_src_origin,
          py::object py_dest_origin,
          py::object py_region,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
        COPY_PY_COORD_TRIPLE(src_origin);
        COPY_PY_COORD_TRIPLE(dest_origin);
        COPY_PY_REGION_TRIPLE(region);
    
        cl_event evt;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          PYOPENCL_CALL_GUARDED(clEnqueueCopyImage, (
                cq.data(), src.data(), dest.data(),
                src_origin, dest_origin, region,
                PYOPENCL_WAITLIST_ARGS, &evt
                ));
          );
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
    
    
    
      inline
      event *enqueue_copy_image_to_buffer(
          command_queue &cq,
          memory_object_holder &src,
          memory_object_holder &dest,
          py::object py_origin,
          py::object py_region,
          size_t offset,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
        COPY_PY_COORD_TRIPLE(origin);
        COPY_PY_REGION_TRIPLE(region);
    
        cl_event evt;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          PYOPENCL_CALL_GUARDED(clEnqueueCopyImageToBuffer, (
                cq.data(), src.data(), dest.data(),
                origin, region, offset,
                PYOPENCL_WAITLIST_ARGS, &evt
                ));
          );
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
    
    
    
      inline
      event *enqueue_copy_buffer_to_image(
          command_queue &cq,
          memory_object_holder &src,
          memory_object_holder &dest,
          size_t offset,
          py::object py_origin,
          py::object py_region,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
        COPY_PY_COORD_TRIPLE(origin);
        COPY_PY_REGION_TRIPLE(region);
    
        cl_event evt;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          PYOPENCL_CALL_GUARDED(clEnqueueCopyBufferToImage, (
                cq.data(), src.data(), dest.data(),
                offset, origin, region,
                PYOPENCL_WAITLIST_ARGS, &evt
                ));
          );
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
      // }}}
    
    #if PYOPENCL_CL_VERSION >= 0x1020
      inline
      event *enqueue_fill_image(
          command_queue &cq,
          memory_object_holder &mem,
          py::object color,
          py::object py_origin, py::object py_region,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        COPY_PY_COORD_TRIPLE(origin);
        COPY_PY_REGION_TRIPLE(region);
    
        const void *color_buf;
    
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
    
        std::unique_ptr<py_buffer_wrapper> ward(new py_buffer_wrapper);
    
    
        ward->get(color.ptr(), PyBUF_ANY_CONTIGUOUS);
    
        color_buf = ward->m_buf.buf;
    #else
        PYOPENCL_BUFFER_SIZE_T color_len;
        if (PyObject_AsReadBuffer(color.ptr(), &color_buf, &color_len))
          throw py::error_already_set();
    #endif
    
        cl_event evt;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          PYOPENCL_CALL_GUARDED(clEnqueueFillImage, (
                cq.data(),
                mem.data(),
                color_buf, origin, region,
                PYOPENCL_WAITLIST_ARGS, &evt
                ));
          );
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    #endif
    
      // }}}
    
    
      // {{{ maps
      class memory_map
      {
        private:
          bool m_valid;
    
          std::shared_ptr<command_queue> m_queue;
    
          memory_object m_mem;
          void *m_ptr;
    
        public:
    
          memory_map(std::shared_ptr<command_queue> cq, memory_object const &mem, void *ptr)
    
            : m_valid(true), m_queue(cq), m_mem(mem), m_ptr(ptr)
          {
          }
    
          ~memory_map()
          {
            if (m_valid)
    
              delete release(0, py::none());
    
          }
    
          event *release(command_queue *cq, py::object py_wait_for)
          {
            PYOPENCL_PARSE_WAIT_FOR;
    
            if (cq == 0)
    
              cq = m_queue.get();
    
    
            cl_event evt;
            PYOPENCL_CALL_GUARDED(clEnqueueUnmapMemObject, (
                  cq->data(), m_mem.data(), m_ptr,
                  PYOPENCL_WAITLIST_ARGS, &evt
                  ));
    
            m_valid = false;
    
            PYOPENCL_RETURN_NEW_EVENT(evt);
          }
      };
    
    
    
    
    
      // FIXME: Reenable in pypy
    #ifndef PYPY_VERSION
    
      inline
      py::object enqueue_map_buffer(
    
          std::shared_ptr<command_queue> cq,
    
          memory_object_holder &buf,
          cl_map_flags flags,
          size_t offset,
          py::object py_shape, py::object dtype,
          py::object py_order, py::object py_strides,
          py::object py_wait_for,
          bool is_blocking
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
        PYOPENCL_PARSE_NUMPY_ARRAY_SPEC;
    
        npy_uintp size_in_bytes = tp_descr->elsize;
    
        for (npy_intp sdim: shape)
    
          size_in_bytes *= sdim;
    
    
        py::object result;
    
    
        cl_event evt;
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clEnqueueMapBuffer");
        void *mapped;
    
        PYOPENCL_RETRY_IF_MEM_ERROR(
            {
    
              {
                py::gil_scoped_release release;
                mapped = clEnqueueMapBuffer(
                      cq->data(), buf.data(),
                      PYOPENCL_CAST_BOOL(is_blocking), flags,
                      offset, size_in_bytes,
                      PYOPENCL_WAITLIST_ARGS, &evt,
                      &status_code);
              }
    
              if (status_code != CL_SUCCESS)
                throw pyopencl::error("clEnqueueMapBuffer", status_code);
            } );
    
        event evt_handle(evt, false);
    
    
        std::unique_ptr<memory_map> map;
    
          result = py::object(py::reinterpret_steal<py::object>(PyArray_NewFromDescr(
    
              &PyArray_Type, tp_descr,
              shape.size(),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
              shape.empty() ? nullptr : &shape.front(),
              strides.empty() ? nullptr : &strides.front(),
              mapped, ary_flags, /*obj*/nullptr)));
    
          if (size_in_bytes != (npy_uintp) PyArray_NBYTES(result.ptr()))
    
            throw pyopencl::error("enqueue_map_buffer", CL_INVALID_VALUE,
                "miscalculated numpy array size (not contiguous?)");
    
    
           map = std::unique_ptr<memory_map>(new memory_map(cq, buf, mapped));
    
        }
        catch (...)
        {
          PYOPENCL_CALL_GUARDED_CLEANUP(clEnqueueUnmapMemObject, (
    
                cq->data(), buf.data(), mapped, 0, 0, 0));
    
        py::object map_py(handle_from_new_ptr(map.release()));
        PyArray_BASE(result.ptr()) = map_py.ptr();
        Py_INCREF(map_py.ptr());
    
    
        return py::make_tuple(
            result,
            handle_from_new_ptr(new event(evt_handle)));
      }
    
      // FIXME: Reenable in pypy
    #ifndef PYPY_VERSION
    
      inline
      py::object enqueue_map_image(
    
          std::shared_ptr<command_queue> cq,
    
          memory_object_holder &img,
          cl_map_flags flags,
          py::object py_origin,
          py::object py_region,
          py::object py_shape, py::object dtype,
          py::object py_order, py::object py_strides,
          py::object py_wait_for,
          bool is_blocking
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
        PYOPENCL_PARSE_NUMPY_ARRAY_SPEC;
        COPY_PY_COORD_TRIPLE(origin);
        COPY_PY_REGION_TRIPLE(region);
    
        cl_event evt;
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clEnqueueMapImage");
        size_t row_pitch, slice_pitch;
        void *mapped;
        PYOPENCL_RETRY_IF_MEM_ERROR(
          {
    
            {
              py::gil_scoped_release release;
              mapped = clEnqueueMapImage(
                    cq->data(), img.data(),
                    PYOPENCL_CAST_BOOL(is_blocking), flags,
                    origin, region, &row_pitch, &slice_pitch,
                    PYOPENCL_WAITLIST_ARGS, &evt,
                    &status_code);
            }
    
            if (status_code != CL_SUCCESS)
              throw pyopencl::error("clEnqueueMapImage", status_code);
          } );
    
        event evt_handle(evt, false);
    
    
        std::unique_ptr<memory_map> map;
    
           map = std::unique_ptr<memory_map>(new memory_map(cq, img, mapped));
    
        }
        catch (...)
        {
          PYOPENCL_CALL_GUARDED_CLEANUP(clEnqueueUnmapMemObject, (
    
                cq->data(), img.data(), mapped, 0, 0, 0));
    
        py::object result = py::reinterpret_steal<py::object>(PyArray_NewFromDescr(
    
            &PyArray_Type, tp_descr,
            shape.size(),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
            shape.empty() ? nullptr : &shape.front(),
            strides.empty() ? nullptr : &strides.front(),
            mapped, ary_flags, /*obj*/nullptr));
    
        py::object map_py(handle_from_new_ptr(map.release()));
        PyArray_BASE(result.ptr()) = map_py.ptr();
        Py_INCREF(map_py.ptr());
    
    
        return py::make_tuple(
            result,
            handle_from_new_ptr(new event(evt_handle)),
            row_pitch, slice_pitch);
      }
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
      // {{{ svm
    
    #if PYOPENCL_CL_VERSION >= 0x2000
    
      class svm_arg_wrapper
      {
        private:
          void *m_ptr;
          PYOPENCL_BUFFER_SIZE_T m_size;
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
            std::unique_ptr<py_buffer_wrapper> ward;
    #endif
    
        public:
          svm_arg_wrapper(py::object holder)
          {
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
            ward = std::unique_ptr<py_buffer_wrapper>(new py_buffer_wrapper);
    
    #ifdef PYPY_VERSION
            // FIXME: get a read-only buffer
            // Not quite honest, but Pypy doesn't consider numpy arrays
            // created from objects with the __aray_interface__ writeable.
            ward->get(holder.ptr(), PyBUF_ANY_CONTIGUOUS);
    #else
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
            ward->get(holder.ptr(), PyBUF_ANY_CONTIGUOUS | PyBUF_WRITABLE);
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
            m_ptr = ward->m_buf.buf;
            m_size = ward->m_buf.len;
    #else
    
            py::object ward = holder;
            if (PyObject_AsWriteBuffer(holder.ptr(), &m_ptr, &m_size))
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
              throw py::error_already_set();
    #endif
          }
    
          void *ptr() const
          {
            return m_ptr;
          }
          size_t size() const
          {
            return m_size;
          }
      };
    
    
      class svm_allocation : noncopyable
      {
        private:
          std::shared_ptr<context> m_context;
          void *m_allocation;
    
        public:
          svm_allocation(std::shared_ptr<context> const &ctx, size_t size, cl_uint alignment, cl_svm_mem_flags flags)
            : m_context(ctx)
          {
            PYOPENCL_PRINT_CALL_TRACE("clSVMalloc");
            m_allocation = clSVMAlloc(
                ctx->data(),
                flags, size, alignment);
    
            if (!m_allocation)
              throw pyopencl::error("clSVMAlloc", CL_OUT_OF_RESOURCES);
          }
    
          ~svm_allocation()
          {
            if (m_allocation)
              release();
          }
    
          void release()
          {
            if (!m_allocation)
              throw error("SVMAllocation.release", CL_INVALID_VALUE,
                  "trying to double-unref svm allocation");
    
            clSVMFree(m_context->data(), m_allocation);
            m_allocation = nullptr;
          }
    
          void enqueue_release(command_queue &queue, py::object py_wait_for)
          {
            PYOPENCL_PARSE_WAIT_FOR;
    
            if (!m_allocation)
              throw error("SVMAllocation.release", CL_INVALID_VALUE,
                  "trying to double-unref svm allocation");
    
            cl_event evt;
    
            PYOPENCL_CALL_GUARDED_CLEANUP(clEnqueueSVMFree, (
                  queue.data(), 1, &m_allocation,
                  nullptr, nullptr,
                  PYOPENCL_WAITLIST_ARGS, &evt));
    
            m_allocation = nullptr;
          }
    
          void *ptr() const
          {
            return m_allocation;
          }
    
          intptr_t ptr_as_int() const
          {
            return (intptr_t) m_allocation;
          }
    
          bool operator==(svm_allocation const &other) const
          {
            return m_allocation == other.m_allocation;
          }
    
          bool operator!=(svm_allocation const &other) const
          {
            return m_allocation != other.m_allocation;
          }
      };
    
    
      inline
      event *enqueue_svm_memcpy(
          command_queue &cq,
          cl_bool is_blocking,
          svm_arg_wrapper &dst, svm_arg_wrapper &src,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        if (src.size() != dst.size())
          throw error("_enqueue_svm_memcpy", CL_INVALID_VALUE,
              "sizes of source and destination buffer do not match");
    
        cl_event evt;
        PYOPENCL_CALL_GUARDED(
            clEnqueueSVMMemcpy,
            (
              cq.data(),
              is_blocking,
              dst.ptr(), src.ptr(),
              dst.size(),
              PYOPENCL_WAITLIST_ARGS,
              &evt
            ));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
    
      inline
      event *enqueue_svm_memfill(
          command_queue &cq,
          svm_arg_wrapper &dst, py::object py_pattern,
          py::object byte_count,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
    
        const void *pattern_ptr;
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
        PYOPENCL_BUFFER_SIZE_T pattern_len;
    
    #ifdef PYOPENCL_USE_NEW_BUFFER_INTERFACE
        std::unique_ptr<py_buffer_wrapper> pattern_ward(new py_buffer_wrapper);
    
        pattern_ward->get(py_pattern.ptr(), PyBUF_ANY_CONTIGUOUS);
    
        pattern_ptr = pattern_ward->m_buf.buf;
        pattern_len = pattern_ward->m_buf.len;
    #else
        py::object pattern_ward = py_pattern;
    
        if (PyObject_AsReadBuffer(py_pattern.ptr(), &pattern_ptr, &pattern_len))
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
          throw py::error_already_set();
    #endif
    
        size_t fill_size = dst.size();
        if (!byte_count.is_none())
          fill_size = py::cast<size_t>(byte_count);
    
        cl_event evt;
        PYOPENCL_CALL_GUARDED(
            clEnqueueSVMMemFill,
            (
              cq.data(),
              dst.ptr(), pattern_ptr,
              pattern_len,
              fill_size,
              PYOPENCL_WAITLIST_ARGS,
              &evt
            ));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
    
      inline
      event *enqueue_svm_map(
          command_queue &cq,
          cl_bool is_blocking,
          cl_map_flags flags,
          svm_arg_wrapper &svm,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        cl_event evt;
        PYOPENCL_CALL_GUARDED(
            clEnqueueSVMMap,
            (
              cq.data(),
              is_blocking,
              flags,
              svm.ptr(), svm.size(),
              PYOPENCL_WAITLIST_ARGS,
              &evt
            ));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    
    
      inline
      event *enqueue_svm_unmap(
          command_queue &cq,
          svm_arg_wrapper &svm,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        cl_event evt;
        PYOPENCL_CALL_GUARDED(
            clEnqueueSVMUnmap,
            (
              cq.data(),
              svm.ptr(),
              PYOPENCL_WAITLIST_ARGS,
              &evt
            ));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    #endif
    
    
    #if PYOPENCL_CL_VERSION >= 0x2010
      inline
      event *enqueue_svm_migratemem(
          command_queue &cq,
          py::sequence svms,
          cl_mem_migration_flags flags,
          py::object py_wait_for
          )
      {
        PYOPENCL_PARSE_WAIT_FOR;
    
        std::vector<const void *> svm_pointers;
        std::vector<size_t> sizes;
    
        for (py::handle py_svm: svms)
        {
          svm_arg_wrapper &svm(py::cast<svm_arg_wrapper &>(py_svm));
    
          svm_pointers.push_back(svm.ptr());
          sizes.push_back(svm.size());
        }
    
        cl_event evt;
        PYOPENCL_CALL_GUARDED(
            clEnqueueSVMMigrateMem,
            (
             cq.data(),
             svm_pointers.size(),
             svm_pointers.empty() ? nullptr : &svm_pointers.front(),
             sizes.empty() ? nullptr : &sizes.front(),
             flags,
             PYOPENCL_WAITLIST_ARGS,
             &evt
            ));
    
        PYOPENCL_RETURN_NEW_EVENT(evt);
      }
    #endif
    
      // }}}
    
    
    
      // {{{ sampler
    
    
      class sampler : noncopyable
    
      {
        private:
          cl_sampler m_sampler;
    
        public:
    
    #if PYOPENCL_CL_VERSION >= 0x2000
          sampler(context const &ctx, py::sequence py_props)
          {
            int hex_plat_version = ctx.get_hex_platform_version();
    
            if (hex_plat_version  < 0x2000)
            {
              std::cerr <<
                "sampler properties given as an iterable, "
                "which uses an OpenCL 2+-only interface, "
                "but the context's platform does not "
                "declare OpenCL 2 support. Proceeding "
                "as requested, but the next thing you see "
                "may be a crash." << std:: endl;
            }
    
    
            PYOPENCL_STACK_CONTAINER(cl_sampler_properties, props, py::len(py_props) + 1);
    
            {
              size_t i = 0;
              for (auto prop: py_props)
                props[i++] = py::cast<cl_sampler_properties>(prop);
              props[i++] = 0;
            }
    
            cl_int status_code;
            PYOPENCL_PRINT_CALL_TRACE("clCreateSamplerWithProperties");
    
            m_sampler = clCreateSamplerWithProperties(
                ctx.data(),
    
                &status_code);
    
            if (status_code != CL_SUCCESS)
              throw pyopencl::error("Sampler", status_code);
          }
    #endif
    
    
          sampler(context const &ctx, bool normalized_coordinates,
              cl_addressing_mode am, cl_filter_mode fm)
          {
            PYOPENCL_PRINT_CALL_TRACE("clCreateSampler");
    
            int hex_plat_version = ctx.get_hex_platform_version();
    #if PYOPENCL_CL_VERSION >= 0x2000
            if (hex_plat_version  >= 0x2000)
            {
                cl_sampler_properties props_list[] = {
                  CL_SAMPLER_NORMALIZED_COORDS, normalized_coordinates,
                  CL_SAMPLER_ADDRESSING_MODE, am,
                  CL_SAMPLER_FILTER_MODE, fm,
                  0,
                };
    
                cl_int status_code;
    
                PYOPENCL_PRINT_CALL_TRACE("clCreateSamplerWithProperties");
                m_sampler = clCreateSamplerWithProperties(
                    ctx.data(), props_list, &status_code);
    
                if (status_code != CL_SUCCESS)
                  throw pyopencl::error("Sampler", status_code);
            }
            else
    #endif
            {
              cl_int status_code;
    
    
    #if defined(__GNUG__) && !defined(__clang__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    #endif
    
              m_sampler = clCreateSampler(
                  ctx.data(),
                  normalized_coordinates,
                  am, fm, &status_code);
    
    #if defined(__GNUG__) && !defined(__clang__)
    #pragma GCC diagnostic pop
    #endif
    
              if (status_code != CL_SUCCESS)
                throw pyopencl::error("Sampler", status_code);
            }
    
          }
    
          sampler(cl_sampler samp, bool retain)
            : m_sampler(samp)
          {
            if (retain)
              PYOPENCL_CALL_GUARDED(clRetainSampler, (samp));
          }
    
          ~sampler()
          {
            PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseSampler, (m_sampler));
          }
    
          cl_sampler data() const
          {
            return m_sampler;
          }
    
          PYOPENCL_EQUALITY_TESTS(sampler);
    
          py::object get_info(cl_sampler_info param_name) const
          {
            switch (param_name)
            {
              case CL_SAMPLER_REFERENCE_COUNT:
                PYOPENCL_GET_INTEGRAL_INFO(Sampler, m_sampler, param_name,
                    cl_uint);
              case CL_SAMPLER_CONTEXT:
                PYOPENCL_GET_OPAQUE_INFO(Sampler, m_sampler, param_name,
                    cl_context, context);
              case CL_SAMPLER_ADDRESSING_MODE:
                PYOPENCL_GET_INTEGRAL_INFO(Sampler, m_sampler, param_name,
                    cl_addressing_mode);
              case CL_SAMPLER_FILTER_MODE:
                PYOPENCL_GET_INTEGRAL_INFO(Sampler, m_sampler, param_name,
                    cl_filter_mode);
              case CL_SAMPLER_NORMALIZED_COORDS:
                PYOPENCL_GET_INTEGRAL_INFO(Sampler, m_sampler, param_name,
                    cl_bool);
    
              default:
                throw error("Sampler.get_info", CL_INVALID_VALUE);
            }
          }
      };
    
      // }}}
    
    
      class program : noncopyable
    
          enum program_kind_type { KND_UNKNOWN, KND_SOURCE, KND_BINARY, KND_IL };
    
    
        private:
          cl_program m_program;
          program_kind_type m_program_kind;
    
        public:
          program(cl_program prog, bool retain, program_kind_type progkind=KND_UNKNOWN)
            : m_program(prog), m_program_kind(progkind)
          {
            if (retain)
              PYOPENCL_CALL_GUARDED(clRetainProgram, (prog));
          }
    
          ~program()
          {
            PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseProgram, (m_program));
          }
    
          cl_program data() const
          {
            return m_program;
          }
    
          program_kind_type kind() const
          {
            return m_program_kind;
          }
    
          PYOPENCL_EQUALITY_TESTS(program);
    
          py::object get_info(cl_program_info param_name) const
          {
            switch (param_name)
            {
              case CL_PROGRAM_REFERENCE_COUNT:
                PYOPENCL_GET_INTEGRAL_INFO(Program, m_program, param_name,
                    cl_uint);
              case CL_PROGRAM_CONTEXT:
                PYOPENCL_GET_OPAQUE_INFO(Program, m_program, param_name,
                    cl_context, context);
              case CL_PROGRAM_NUM_DEVICES:
                PYOPENCL_GET_INTEGRAL_INFO(Program, m_program, param_name,
                    cl_uint);
              case CL_PROGRAM_DEVICES:
                {
                  std::vector<cl_device_id> result;
                  PYOPENCL_GET_VEC_INFO(Program, m_program, param_name, result);
    
                  py::list py_result;
    
                  for (cl_device_id did: result)
    
                    py_result.append(handle_from_new_ptr(
                          new pyopencl::device(did)));
                  return py_result;
                }
              case CL_PROGRAM_SOURCE:
                PYOPENCL_GET_STR_INFO(Program, m_program, param_name);
              case CL_PROGRAM_BINARY_SIZES:
                {
                  std::vector<size_t> result;
                  PYOPENCL_GET_VEC_INFO(Program, m_program, param_name, result);
                  PYOPENCL_RETURN_VECTOR(size_t, result);
                }
              case CL_PROGRAM_BINARIES:
                // {{{
                {
                  std::vector<size_t> sizes;
                  PYOPENCL_GET_VEC_INFO(Program, m_program, CL_PROGRAM_BINARY_SIZES, sizes);
    
                  size_t total_size = std::accumulate(sizes.begin(), sizes.end(), 0);
    
    
                  std::unique_ptr<unsigned char []> result(
    
                      new unsigned char[total_size]);
                  std::vector<unsigned char *> result_ptrs;
    
                  unsigned char *ptr = result.get();
                  for (unsigned i = 0; i < sizes.size(); ++i)
                  {
                    result_ptrs.push_back(ptr);
                    ptr += sizes[i];
                  }
    
                  PYOPENCL_CALL_GUARDED(clGetProgramInfo,
                      (m_program, param_name, sizes.size()*sizeof(unsigned char *),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
                       result_ptrs.empty( ) ? nullptr : &result_ptrs.front(), 0)); \
    
    
                  py::list py_result;
                  ptr = result.get();
                  for (unsigned i = 0; i < sizes.size(); ++i)
                  {
    
                    py::object binary_pyobj(
                        py::reinterpret_steal<py::object>(
    
    #if PY_VERSION_HEX >= 0x03000000
                        PyBytes_FromStringAndSize(
                          reinterpret_cast<char *>(ptr), sizes[i])
    #else
                        PyString_FromStringAndSize(
                          reinterpret_cast<char *>(ptr), sizes[i])
    #endif
    
                    py_result.append(binary_pyobj);
                    ptr += sizes[i];
                  }
                  return py_result;
                }
                // }}}
    #if PYOPENCL_CL_VERSION >= 0x1020
              case CL_PROGRAM_NUM_KERNELS:
                PYOPENCL_GET_INTEGRAL_INFO(Program, m_program, param_name,
                    size_t);
              case CL_PROGRAM_KERNEL_NAMES:
                PYOPENCL_GET_STR_INFO(Program, m_program, param_name);
    #endif
    
              default:
                throw error("Program.get_info", CL_INVALID_VALUE);
            }
          }
    
          py::object get_build_info(
              device const &dev,
              cl_program_build_info param_name) const
          {
            switch (param_name)
            {
    #define PYOPENCL_FIRST_ARG m_program, dev.data() // hackety hack
              case CL_PROGRAM_BUILD_STATUS:
                PYOPENCL_GET_INTEGRAL_INFO(ProgramBuild,
                    PYOPENCL_FIRST_ARG, param_name,
                    cl_build_status);
              case CL_PROGRAM_BUILD_OPTIONS:
              case CL_PROGRAM_BUILD_LOG:
                PYOPENCL_GET_STR_INFO(ProgramBuild,
                    PYOPENCL_FIRST_ARG, param_name);
    #if PYOPENCL_CL_VERSION >= 0x1020
              case CL_PROGRAM_BINARY_TYPE:
                PYOPENCL_GET_INTEGRAL_INFO(ProgramBuild,
                    PYOPENCL_FIRST_ARG, param_name,
                    cl_program_binary_type);
    #endif
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
    #if PYOPENCL_CL_VERSION >= 0x2000
              case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE:
                PYOPENCL_GET_INTEGRAL_INFO(ProgramBuild,
                    PYOPENCL_FIRST_ARG, param_name,
                    size_t);
    #endif
    
    #undef PYOPENCL_FIRST_ARG
    
              default:
                throw error("Program.get_build_info", CL_INVALID_VALUE);
            }
          }
    
          void build(std::string options, py::object py_devices)
          {
            PYOPENCL_PARSE_PY_DEVICES;
    
            PYOPENCL_CALL_GUARDED_THREADED(clBuildProgram,
                (m_program, num_devices, devices,
                 options.c_str(), 0 ,0));
          }
    
    #if PYOPENCL_CL_VERSION >= 0x1020
          void compile(std::string options, py::object py_devices,
              py::object py_headers)
          {
            PYOPENCL_PARSE_PY_DEVICES;
    
            // {{{ pick apart py_headers
            // py_headers is a list of tuples *(name, program)*
    
            std::vector<std::string> header_names;
            std::vector<cl_program> programs;
    
            for (py::handle name_hdr_tup_py: py_headers)
    
              py::tuple name_hdr_tup = py::reinterpret_borrow<py::tuple>(name_hdr_tup_py);
    
              if (py::len(name_hdr_tup) != 2)
                throw error("Program.compile", CL_INVALID_VALUE,
                    "epxected (name, header) tuple in headers list");
    
              std::string name = (name_hdr_tup[0]).cast<std::string>();
              program &prg = (name_hdr_tup[1]).cast<program &>();
    
    
              header_names.push_back(name);
              programs.push_back(prg.data());
            }
    
            std::vector<const char *> header_name_ptrs;
    
            for (std::string const &name: header_names)
    
              header_name_ptrs.push_back(name.c_str());
    
            // }}}
    
            PYOPENCL_CALL_GUARDED_THREADED(clCompileProgram,
                (m_program, num_devices, devices,
                 options.c_str(), header_names.size(),
    
    Andreas Klöckner's avatar
    Andreas Klöckner committed
                 programs.empty() ? nullptr : &programs.front(),
                 header_name_ptrs.empty() ? nullptr : &header_name_ptrs.front(),
    
                 0, 0));
          }
    #endif
      };
    
    
    
    
      inline
      program *create_program_with_source(
          context &ctx,
          std::string const &src)
      {
        const char *string = src.c_str();
        size_t length = src.size();
    
        cl_int status_code;
        PYOPENCL_PRINT_CALL_TRACE("clCreateProgramWithSource");
        cl_program result = clCreateProgramWithSource(
            ctx.data(), 1, &string, &length, &status_code);
        if (status_code != CL_SUCCESS)
          throw pyopencl::error("clCreateProgramWithSource", status_code);
    
        try
        {
          return new program(result, false, program::KND_SOURCE);
        }
        catch (...)
        {
          clReleaseProgram(result);
          throw;
        }
      }