clover: implements clEnqueueFillImage

Reviewed-by: Francisco Jerez <currojerez@riseup.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4974>
diff --git a/src/gallium/frontends/clover/api/memory.cpp b/src/gallium/frontends/clover/api/memory.cpp
index d069a09..ccf5edf 100644
--- a/src/gallium/frontends/clover/api/memory.cpp
+++ b/src/gallium/frontends/clover/api/memory.cpp
@@ -424,17 +424,6 @@
    return e.get();
 }
 
-CLOVER_API cl_int
-clEnqueueFillImage(cl_command_queue command_queue, cl_mem image,
-                   const void *fill_color,
-                   const size_t *origin, const size_t *region,
-                   cl_uint num_events_in_wait_list,
-                   const cl_event *event_wait_list,
-                   cl_event *event) {
-   CLOVER_NOT_SUPPORTED_UNTIL("1.2");
-   return CL_INVALID_VALUE;
-}
-
 CLOVER_API void *
 clSVMAlloc(cl_context d_ctx,
            cl_svm_mem_flags flags,
diff --git a/src/gallium/frontends/clover/api/transfer.cpp b/src/gallium/frontends/clover/api/transfer.cpp
index b2964b2..fdf2716 100644
--- a/src/gallium/frontends/clover/api/transfer.cpp
+++ b/src/gallium/frontends/clover/api/transfer.cpp
@@ -103,8 +103,9 @@
    validate_object(command_queue &q, image &img,
                    const vector_t &orig, const vector_t &region) {
       vector_t size = { img.width(), img.height(), img.depth() };
+      const auto &dev = q.device();
 
-      if (!q.device().image_support())
+      if (!dev.image_support())
          throw error(CL_INVALID_OPERATION);
 
       if (img.context() != q.context())
@@ -115,6 +116,24 @@
 
       if (any_of(is_zero(), region))
          throw error(CL_INVALID_VALUE);
+
+      switch (img.type()) {
+      case CL_MEM_OBJECT_IMAGE2D: {
+         const size_t max = 1 << dev.max_image_levels_2d();
+         if (img.width() > max || img.height() > max)
+            throw error(CL_INVALID_IMAGE_SIZE);
+         break;
+      }
+      case CL_MEM_OBJECT_IMAGE3D: {
+         const size_t max = 1 << dev.max_image_levels_3d();
+         if (img.width() > max || img.height() > max || img.depth() > max)
+            throw error(CL_INVALID_IMAGE_SIZE);
+         break;
+      }
+      // XXX: Implement missing checks once Clover supports more image types.
+      default:
+         throw error(CL_INVALID_IMAGE_SIZE);
+      }
    }
 
    ///
@@ -456,11 +475,11 @@
    auto &mem = obj<buffer>(d_mem);
    auto deps = objs<wait_list_tag>(d_deps, num_deps);
    vector_t region = { size, 1, 1 };
-   vector_t dst_origin = { offset };
+   vector_t origin = { offset };
    auto dst_pitch = pitch(region, {{ 1 }});
 
    validate_common(q, deps);
-   validate_object(q, mem, dst_origin, dst_pitch, region);
+   validate_object(q, mem, origin, dst_pitch, region);
 
    if (!pattern)
       return CL_INVALID_VALUE;
@@ -480,7 +499,7 @@
    auto hev = create<hard_event>(
       q, CL_COMMAND_FILL_BUFFER, deps,
       [=, &q, &mem](event &) {
-         mem.resource_in(q).clear(q, offset, size, &data[0], data.size());
+         mem.resource_in(q).clear(q, origin, region, data);
       });
 
    ret_object(rd_ev, hev);
@@ -636,6 +655,38 @@
 }
 
 CLOVER_API cl_int
+clEnqueueFillImage(cl_command_queue d_queue, cl_mem d_mem,
+                   const void *fill_color,
+                   const size_t *p_origin, const size_t *p_region,
+                   cl_uint num_deps, const cl_event *d_deps,
+                   cl_event *rd_ev) try {
+   auto &q = obj(d_queue);
+   auto &img = obj<image>(d_mem);
+   auto deps = objs<wait_list_tag>(d_deps, num_deps);
+   auto origin = vector(p_origin);
+   auto region = vector(p_region);
+
+   validate_common(q, deps);
+   validate_object(q, img, origin, region);
+
+   if (!fill_color)
+      return CL_INVALID_VALUE;
+
+   std::string data = std::string((char *)fill_color, sizeof(cl_uint4));
+   auto hev = create<hard_event>(
+      q, CL_COMMAND_FILL_IMAGE, deps,
+      [=, &q, &img](event &) {
+         img.resource_in(q).clear(q, origin, region, data);
+      });
+
+   ret_object(rd_ev, hev);
+   return CL_SUCCESS;
+
+} catch (error &e) {
+   return e.get();
+}
+
+CLOVER_API cl_int
 clEnqueueCopyImage(cl_command_queue d_q, cl_mem d_src_mem, cl_mem d_dst_mem,
                    const size_t *p_src_origin, const size_t *p_dst_origin,
                    const size_t *p_region,
diff --git a/src/gallium/frontends/clover/core/resource.cpp b/src/gallium/frontends/clover/core/resource.cpp
index f43163d..c21073e 100644
--- a/src/gallium/frontends/clover/core/resource.cpp
+++ b/src/gallium/frontends/clover/core/resource.cpp
@@ -65,11 +65,18 @@
 }
 
 void
-resource::clear(command_queue &q, const size_t origin, const size_t size,
-                const void *pattern, const size_t pattern_size) {
-   auto p = offset[0] + origin;
+resource::clear(command_queue &q, const vector &origin, const vector &region,
+                const std::string &data) {
+   auto from = offset + origin;
 
-   q.pipe->clear_buffer(q.pipe, pipe, p, size, pattern, pattern_size);
+   if (pipe->target == PIPE_BUFFER) {
+      q.pipe->clear_buffer(q.pipe, pipe, from[0], region[0], data.data(), data.size());
+   } else {
+      std::string texture_data;
+      texture_data.reserve(util_format_get_blocksize(pipe->format));
+      util_format_pack_rgba(pipe->format, &texture_data[0], data.data(), 1);
+      q.pipe->clear_texture(q.pipe, pipe, 0, box(from, region), texture_data.data());
+   }
 }
 
 void *
diff --git a/src/gallium/frontends/clover/core/resource.hpp b/src/gallium/frontends/clover/core/resource.hpp
index 51ecc53..d4d7615 100644
--- a/src/gallium/frontends/clover/core/resource.hpp
+++ b/src/gallium/frontends/clover/core/resource.hpp
@@ -50,8 +50,8 @@
       void copy(command_queue &q, const vector &origin, const vector &region,
                 resource &src_resource, const vector &src_origin);
 
-      void clear(command_queue &q, const size_t origin, const size_t size,
-                 const void *pattern, const size_t pattern_size);
+      void clear(command_queue &q, const vector &origin, const vector &region,
+                 const std::string &data);
 
       void *add_map(command_queue &q, cl_map_flags flags, bool blocking,
                     const vector &origin, const vector &region);