From 530845d345b1bd358355f64c941ae4fb67cf868c Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Wed, 16 Sep 2020 14:09:17 -0500
Subject: [PATCH] Add pipe support

---
 doc/runtime_memory.rst | 20 +++++++++++
 pyopencl/__init__.py   |  7 +++-
 src/wrap_cl.hpp        | 79 ++++++++++++++++++++++++++++++++++++++++--
 src/wrap_cl_part_2.cpp | 30 ++++++++++++++++
 4 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/doc/runtime_memory.rst b/doc/runtime_memory.rst
index ce2ee222..fc121554 100644
--- a/doc/runtime_memory.rst
+++ b/doc/runtime_memory.rst
@@ -381,3 +381,23 @@ Samplers
 
     |comparable|
 
+Pipes
+-----
+
+.. class:: Pipe(context, flags, packet_size, max_packets, properties)
+
+    See :class:`mem_flags` for values of *flags*.
+
+    :arg properties: a sequence
+        of keys and values from :class:`pipe_properties` as accepted
+        by :c:func:`clCreatePipe`. The trailing *0* is added automatically
+        and does not need to be included.
+
+    This function Requires OpenCL 2 or newer.
+
+    .. versionadded:: 2020.3
+
+    .. method:: get_pipe_info(param)
+
+        See :class:`pipe_info` for values of *param*.
+
diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py
index ac7760f8..91d6af10 100644
--- a/pyopencl/__init__.py
+++ b/pyopencl/__init__.py
@@ -156,6 +156,10 @@ from pyopencl._cl import (  # noqa: F401
 
         Image,
         Sampler,
+
+        # This class is available unconditionally, even though CL only
+        # has it on CL2.0 and newer.
+        Pipe,
         )
 
 try:
@@ -1014,7 +1018,7 @@ def _add_functionality():
     class _ImageInfoGetter:
         def __init__(self, event):
             from warnings import warn
-            warn("Image.image.attr is deprecated. "
+            warn("Image.image.attr is deprecated and will go away in 2021. "
                     "Use Image.attr directly, instead.")
 
             self.event = event
@@ -1297,6 +1301,7 @@ def _add_functionality():
             _cl.MemoryObjectHolder:
             (MemoryObjectHolder.get_info, _cl.mem_info, []),
             Image: (_cl.Image.get_image_info, _cl.image_info, []),
+            Pipe: (_cl.Image.get_pipe_info, _cl.pipe_info, []),
             Program: (Program.get_info, _cl.program_info, []),
             Kernel: (Kernel.get_info, _cl.kernel_info, []),
             _cl.Sampler: (Sampler.get_info, _cl.sampler_info, []),
diff --git a/src/wrap_cl.hpp b/src/wrap_cl.hpp
index 75a08a47..7cf2dc07 100644
--- a/src/wrap_cl.hpp
+++ b/src/wrap_cl.hpp
@@ -30,8 +30,7 @@
 // CL 1.2 undecided:
 // clSetPrintfCallback
 
-// CL 2.0 missing:
-// pipes
+// CL 2.0 complete
 
 // CL 2.1 missing:
 // clGetKernelSubGroupInfo
@@ -2580,7 +2579,7 @@ namespace pyopencl
 #endif
 
           default:
-            throw error("MemoryObject.get_image_info", CL_INVALID_VALUE);
+            throw error("Image.get_image_info", CL_INVALID_VALUE);
         }
       }
   };
@@ -3059,6 +3058,80 @@ namespace pyopencl
   // }}}
 
 
+  // {{{ pipe
+
+  class pipe : public memory_object
+  {
+    public:
+      pipe(cl_mem mem, bool retain)
+        : memory_object(mem, retain)
+      { }
+
+      py::object get_pipe_info(cl_pipe_info param_name) const
+      {
+#if PYOPENCL_CL_VERSION >= 0x2000
+        switch (param_name)
+        {
+          case CL_PIPE_PACKET_SIZE:
+          case CL_PIPE_MAX_PACKETS:
+            PYOPENCL_GET_TYPED_INFO(Pipe, data(), param_name, cl_uint);
+
+          default:
+            throw error("Pipe.get_pipe_info", CL_INVALID_VALUE);
+        }
+#else
+        throw error("Pipes not available. PyOpenCL was not compiled against a CL2+ header.",
+            CL_INVALID_VALUE);
+#endif
+      }
+  };
+
+#if PYOPENCL_CL_VERSION >= 0x2000
+  inline
+  pipe *create_pipe(
+      context const &ctx,
+      cl_mem_flags flags,
+      cl_uint pipe_packet_size,
+      cl_uint pipe_max_packets,
+      py::sequence py_props)
+  {
+    PYOPENCL_STACK_CONTAINER(cl_pipe_properties, props, py::len(py_props) + 1);
+    {
+      size_t i = 0;
+      for (auto prop: py_props)
+        props[i++] = py::cast<cl_pipe_properties>(prop);
+      props[i++] = 0;
+    }
+
+    cl_int status_code;
+    PYOPENCL_PRINT_CALL_TRACE("clCreatePipe");
+
+    cl_mem mem = clCreatePipe(
+        ctx.data(),
+        flags,
+        pipe_packet_size,
+        pipe_max_packets,
+        PYOPENCL_STACK_CONTAINER_GET_PTR(props),
+        &status_code);
+
+    if (status_code != CL_SUCCESS)
+      throw pyopencl::error("Pipe", status_code);
+
+    try
+    {
+      return new pipe(mem, false);
+    }
+    catch (...)
+    {
+      PYOPENCL_CALL_GUARDED(clReleaseMemObject, (mem));
+      throw;
+    }
+}
+#endif
+
+  // }}}
+
+
   // {{{ maps
   class memory_map
   {
diff --git a/src/wrap_cl_part_2.cpp b/src/wrap_cl_part_2.cpp
index 6735d41f..df56d0af 100644
--- a/src/wrap_cl_part_2.cpp
+++ b/src/wrap_cl_part_2.cpp
@@ -222,6 +222,36 @@ void pyopencl_expose_part_2(py::module &m)
 
   // }}}
 
+  // {{{ pipe
+
+  {
+    typedef pyopencl::pipe cls;
+    py::class_<cls, memory_object>(m, "Pipe", py::dynamic_attr())
+#if PYOPENCL_CL_VERSION >= 0x2000
+      .def(
+          py::init(
+            [](
+              context const &ctx,
+              cl_mem_flags flags,
+              cl_uint pipe_packet_size,
+              cl_uint pipe_max_packets,
+              py::sequence py_props)
+            {
+              return create_pipe(ctx, flags, pipe_packet_size, pipe_max_packets, py_props);
+            }),
+          py::arg("context"),
+          py::arg("flags"),
+          py::arg("packet_size"),
+          py::arg("max_packets"),
+          py::arg("properties")
+          )
+#endif
+      .DEF_SIMPLE_METHOD(get_pipe_info)
+      ;
+  }
+
+  // }}}
+
   // {{{ memory_map
   {
     typedef memory_map cls;
-- 
GitLab