1//
2// Copyright 2012 Francisco Jerez
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20// OTHER DEALINGS IN THE SOFTWARE.
21//
22
23#include "util/u_math.h"
24#include "api/util.hpp"
25#include "core/memory.hpp"
26#include "core/format.hpp"
27
28using namespace clover;
29
30namespace {
31   cl_mem_flags
32   validate_flags(cl_mem d_parent, cl_mem_flags d_flags) {
33      const cl_mem_flags dev_access_flags =
34         CL_MEM_READ_WRITE | CL_MEM_WRITE_ONLY | CL_MEM_READ_ONLY;
35      const cl_mem_flags host_ptr_flags =
36         CL_MEM_USE_HOST_PTR | CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR;
37      const cl_mem_flags host_access_flags =
38         CL_MEM_HOST_WRITE_ONLY | CL_MEM_HOST_READ_ONLY | CL_MEM_HOST_NO_ACCESS;
39      const cl_mem_flags valid_flags =
40         dev_access_flags | host_access_flags | (d_parent ? 0 : host_ptr_flags);
41
42      if ((d_flags & ~valid_flags) ||
43          util_bitcount(d_flags & dev_access_flags) > 1 ||
44          util_bitcount(d_flags & host_access_flags) > 1)
45         throw error(CL_INVALID_VALUE);
46
47      if ((d_flags & CL_MEM_USE_HOST_PTR) &&
48          (d_flags & (CL_MEM_COPY_HOST_PTR | CL_MEM_ALLOC_HOST_PTR)))
49         throw error(CL_INVALID_VALUE);
50
51      if (d_parent) {
52         const auto &parent = obj(d_parent);
53         const cl_mem_flags flags = (d_flags |
54                                     (d_flags & dev_access_flags ? 0 :
55                                      parent.flags() & dev_access_flags) |
56                                     (d_flags & host_access_flags ? 0 :
57                                      parent.flags() & host_access_flags) |
58                                     (parent.flags() & host_ptr_flags));
59
60         if (~flags & parent.flags() & (dev_access_flags & ~CL_MEM_READ_WRITE))
61            throw error(CL_INVALID_VALUE);
62
63         // Check if new host access flags cause a mismatch between
64         // host-read/write-only.
65         if (!(flags & CL_MEM_HOST_NO_ACCESS) &&
66             (~flags & parent.flags() & host_access_flags))
67            throw error(CL_INVALID_VALUE);
68
69         return flags;
70
71      } else {
72         return d_flags | (d_flags & dev_access_flags ? 0 : CL_MEM_READ_WRITE);
73      }
74   }
75}
76
77CLOVER_API cl_mem
78clCreateBuffer(cl_context d_ctx, cl_mem_flags d_flags, size_t size,
79               void *host_ptr, cl_int *r_errcode) try {
80   const cl_mem_flags flags = validate_flags(NULL, d_flags);
81   auto &ctx = obj(d_ctx);
82
83   if (bool(host_ptr) != bool(flags & (CL_MEM_USE_HOST_PTR |
84                                       CL_MEM_COPY_HOST_PTR)))
85      throw error(CL_INVALID_HOST_PTR);
86
87   if (!size ||
88       size > fold(maximum(), cl_ulong(0),
89                   map(std::mem_fn(&device::max_mem_alloc_size), ctx.devices())
90          ))
91      throw error(CL_INVALID_BUFFER_SIZE);
92
93   ret_error(r_errcode, CL_SUCCESS);
94   return new root_buffer(ctx, flags, size, host_ptr);
95
96} catch (error &e) {
97   ret_error(r_errcode, e);
98   return NULL;
99}
100
101CLOVER_API cl_mem
102clCreateSubBuffer(cl_mem d_mem, cl_mem_flags d_flags,
103                  cl_buffer_create_type op,
104                  const void *op_info, cl_int *r_errcode) try {
105   auto &parent = obj<root_buffer>(d_mem);
106   const cl_mem_flags flags = validate_flags(d_mem, d_flags);
107
108   if (op == CL_BUFFER_CREATE_TYPE_REGION) {
109      auto reg = reinterpret_cast<const cl_buffer_region *>(op_info);
110
111      if (!reg ||
112          reg->origin > parent.size() ||
113          reg->origin + reg->size > parent.size())
114         throw error(CL_INVALID_VALUE);
115
116      if (!reg->size)
117         throw error(CL_INVALID_BUFFER_SIZE);
118
119      ret_error(r_errcode, CL_SUCCESS);
120      return new sub_buffer(parent, flags, reg->origin, reg->size);
121
122   } else {
123      throw error(CL_INVALID_VALUE);
124   }
125
126} catch (error &e) {
127   ret_error(r_errcode, e);
128   return NULL;
129}
130
131CLOVER_API cl_mem
132clCreateImage(cl_context d_ctx, cl_mem_flags d_flags,
133              const cl_image_format *format,
134              const cl_image_desc *desc,
135              void *host_ptr, cl_int *r_errcode) try {
136   auto &ctx = obj(d_ctx);
137
138   if (!any_of(std::mem_fn(&device::image_support), ctx.devices()))
139      throw error(CL_INVALID_OPERATION);
140
141   if (!format)
142      throw error(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR);
143
144   if (!desc)
145      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
146
147   if (desc->image_array_size == 0 &&
148       (desc->image_type == CL_MEM_OBJECT_IMAGE1D_ARRAY ||
149        desc->image_type == CL_MEM_OBJECT_IMAGE2D_ARRAY))
150      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
151
152   if (!host_ptr &&
153       (desc->image_row_pitch || desc->image_slice_pitch))
154      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
155
156   if (desc->num_mip_levels || desc->num_samples)
157      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
158
159   if (bool(desc->buffer) != (desc->image_type == CL_MEM_OBJECT_IMAGE1D_BUFFER))
160      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
161
162   if (bool(host_ptr) != bool(d_flags & (CL_MEM_USE_HOST_PTR |
163                                         CL_MEM_COPY_HOST_PTR)))
164      throw error(CL_INVALID_HOST_PTR);
165
166   const cl_mem_flags flags = validate_flags(desc->buffer, d_flags);
167
168   if (!supported_formats(ctx, desc->image_type).count(*format))
169      throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
170
171   ret_error(r_errcode, CL_SUCCESS);
172
173   switch (desc->image_type) {
174   case CL_MEM_OBJECT_IMAGE2D:
175      if (!desc->image_width || !desc->image_height)
176         throw error(CL_INVALID_IMAGE_SIZE);
177
178      if (all_of([=](const device &dev) {
179               const size_t max = 1 << dev.max_image_levels_2d();
180               return (desc->image_width > max ||
181                       desc->image_height > max);
182            }, ctx.devices()))
183         throw error(CL_INVALID_IMAGE_SIZE);
184
185      return new image2d(ctx, flags, format,
186                         desc->image_width, desc->image_height,
187                         desc->image_row_pitch, host_ptr);
188
189   case CL_MEM_OBJECT_IMAGE3D:
190      if (!desc->image_width || !desc->image_height || !desc->image_depth)
191         throw error(CL_INVALID_IMAGE_SIZE);
192
193      if (all_of([=](const device &dev) {
194               const size_t max = 1 << dev.max_image_levels_3d();
195               return (desc->image_width > max ||
196                       desc->image_height > max ||
197                       desc->image_depth > max);
198            }, ctx.devices()))
199         throw error(CL_INVALID_IMAGE_SIZE);
200
201      return new image3d(ctx, flags, format,
202                         desc->image_width, desc->image_height,
203                         desc->image_depth, desc->image_row_pitch,
204                         desc->image_slice_pitch, host_ptr);
205
206   case CL_MEM_OBJECT_IMAGE1D:
207   case CL_MEM_OBJECT_IMAGE1D_ARRAY:
208   case CL_MEM_OBJECT_IMAGE1D_BUFFER:
209   case CL_MEM_OBJECT_IMAGE2D_ARRAY:
210      // XXX - Not implemented.
211      throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
212
213   default:
214      throw error(CL_INVALID_IMAGE_DESCRIPTOR);
215   }
216
217} catch (error &e) {
218   ret_error(r_errcode, e);
219   return NULL;
220}
221
222CLOVER_API cl_mem
223clCreateImage2D(cl_context d_ctx, cl_mem_flags d_flags,
224                const cl_image_format *format,
225                size_t width, size_t height, size_t row_pitch,
226                void *host_ptr, cl_int *r_errcode) {
227   const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE2D, width, height, 0, 0,
228                                row_pitch, 0, 0, 0, NULL };
229
230   return clCreateImage(d_ctx, d_flags, format, &desc, host_ptr, r_errcode);
231}
232
233CLOVER_API cl_mem
234clCreateImage3D(cl_context d_ctx, cl_mem_flags d_flags,
235                const cl_image_format *format,
236                size_t width, size_t height, size_t depth,
237                size_t row_pitch, size_t slice_pitch,
238                void *host_ptr, cl_int *r_errcode) {
239   const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE3D, width, height, depth, 0,
240                                row_pitch, slice_pitch, 0, 0, NULL };
241
242   return clCreateImage(d_ctx, d_flags, format, &desc, host_ptr, r_errcode);
243}
244
245CLOVER_API cl_int
246clGetSupportedImageFormats(cl_context d_ctx, cl_mem_flags flags,
247                           cl_mem_object_type type, cl_uint count,
248                           cl_image_format *r_buf, cl_uint *r_count) try {
249   auto &ctx = obj(d_ctx);
250   auto formats = supported_formats(ctx, type);
251
252   validate_flags(NULL, flags);
253
254   if (r_buf && !r_count)
255      throw error(CL_INVALID_VALUE);
256
257   if (r_buf)
258      std::copy_n(formats.begin(),
259                  std::min((cl_uint)formats.size(), count),
260                  r_buf);
261
262   if (r_count)
263      *r_count = formats.size();
264
265   return CL_SUCCESS;
266
267} catch (error &e) {
268   return e.get();
269}
270
271CLOVER_API cl_int
272clGetMemObjectInfo(cl_mem d_mem, cl_mem_info param,
273                   size_t size, void *r_buf, size_t *r_size) try {
274   property_buffer buf { r_buf, size, r_size };
275   auto &mem = obj(d_mem);
276
277   switch (param) {
278   case CL_MEM_TYPE:
279      buf.as_scalar<cl_mem_object_type>() = mem.type();
280      break;
281
282   case CL_MEM_FLAGS:
283      buf.as_scalar<cl_mem_flags>() = mem.flags();
284      break;
285
286   case CL_MEM_SIZE:
287      buf.as_scalar<size_t>() = mem.size();
288      break;
289
290   case CL_MEM_HOST_PTR:
291      buf.as_scalar<void *>() = mem.host_ptr();
292      break;
293
294   case CL_MEM_MAP_COUNT:
295      buf.as_scalar<cl_uint>() = 0;
296      break;
297
298   case CL_MEM_REFERENCE_COUNT:
299      buf.as_scalar<cl_uint>() = mem.ref_count();
300      break;
301
302   case CL_MEM_CONTEXT:
303      buf.as_scalar<cl_context>() = desc(mem.context());
304      break;
305
306   case CL_MEM_ASSOCIATED_MEMOBJECT: {
307      sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
308      buf.as_scalar<cl_mem>() = (sub ? desc(sub->parent()) : NULL);
309      break;
310   }
311   case CL_MEM_OFFSET: {
312      sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
313      buf.as_scalar<size_t>() = (sub ? sub->offset() : 0);
314      break;
315   }
316   default:
317      throw error(CL_INVALID_VALUE);
318   }
319
320   return CL_SUCCESS;
321
322} catch (error &e) {
323   return e.get();
324}
325
326CLOVER_API cl_int
327clGetImageInfo(cl_mem d_mem, cl_image_info param,
328               size_t size, void *r_buf, size_t *r_size) try {
329   property_buffer buf { r_buf, size, r_size };
330   auto &img = obj<image>(d_mem);
331
332   switch (param) {
333   case CL_IMAGE_FORMAT:
334      buf.as_scalar<cl_image_format>() = img.format();
335      break;
336
337   case CL_IMAGE_ELEMENT_SIZE:
338      buf.as_scalar<size_t>() = 0;
339      break;
340
341   case CL_IMAGE_ROW_PITCH:
342      buf.as_scalar<size_t>() = img.row_pitch();
343      break;
344
345   case CL_IMAGE_SLICE_PITCH:
346      buf.as_scalar<size_t>() = img.slice_pitch();
347      break;
348
349   case CL_IMAGE_WIDTH:
350      buf.as_scalar<size_t>() = img.width();
351      break;
352
353   case CL_IMAGE_HEIGHT:
354      buf.as_scalar<size_t>() = img.height();
355      break;
356
357   case CL_IMAGE_DEPTH:
358      buf.as_scalar<size_t>() = img.depth();
359      break;
360
361   default:
362      throw error(CL_INVALID_VALUE);
363   }
364
365   return CL_SUCCESS;
366
367} catch (error &e) {
368   return e.get();
369}
370
371CLOVER_API cl_int
372clRetainMemObject(cl_mem d_mem) try {
373   obj(d_mem).retain();
374   return CL_SUCCESS;
375
376} catch (error &e) {
377   return e.get();
378}
379
380CLOVER_API cl_int
381clReleaseMemObject(cl_mem d_mem) try {
382   if (obj(d_mem).release())
383      delete pobj(d_mem);
384
385   return CL_SUCCESS;
386
387} catch (error &e) {
388   return e.get();
389}
390
391CLOVER_API cl_int
392clSetMemObjectDestructorCallback(cl_mem d_mem,
393                                 void (CL_CALLBACK *pfn_notify)(cl_mem, void *),
394                                 void *user_data) try {
395   auto &mem = obj(d_mem);
396
397   if (!pfn_notify)
398      return CL_INVALID_VALUE;
399
400   mem.destroy_notify([=]{ pfn_notify(d_mem, user_data); });
401
402   return CL_SUCCESS;
403
404} catch (error &e) {
405   return e.get();
406}
407
408CLOVER_API cl_int
409clEnqueueFillBuffer(cl_command_queue command_queue, cl_mem buffer,
410                    const void *pattern, size_t pattern_size,
411                    size_t offset, size_t size,
412                    cl_uint num_events_in_wait_list,
413                    const cl_event *event_wait_list,
414                    cl_event *event) {
415   CLOVER_NOT_SUPPORTED_UNTIL("1.2");
416   return CL_INVALID_VALUE;
417}
418
419CLOVER_API cl_int
420clEnqueueFillImage(cl_command_queue command_queue, cl_mem image,
421                   const void *fill_color,
422                   const size_t *origin, const size_t *region,
423                   cl_uint num_events_in_wait_list,
424                   const cl_event *event_wait_list,
425                   cl_event *event) {
426   CLOVER_NOT_SUPPORTED_UNTIL("1.2");
427   return CL_INVALID_VALUE;
428}
429