• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2017 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //    http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #ifndef TEST_CONFORMANCE_CLCPP_PIPES_TEST_PIPES_HPP
17 #define TEST_CONFORMANCE_CLCPP_PIPES_TEST_PIPES_HPP
18 
19 #include <sstream>
20 #include <string>
21 #include <tuple>
22 #include <vector>
23 #include <algorithm>
24 
25 // Common for all OpenCL C++ tests
26 #include "../common.hpp"
27 
28 
29 namespace test_pipes {
30 
31 enum class pipe_source
32 {
33     param,
34     storage
35 };
36 
37 enum class pipe_operation
38 {
39     work_item,
40     work_item_reservation,
41     work_group_reservation,
42     sub_group_reservation
43 };
44 
45 struct test_options
46 {
47     pipe_operation operation;
48     pipe_source source;
49     int max_packets;
50     int num_packets;
51 };
52 
53 struct output_type
54 {
55     cl_uint write_reservation_is_valid;
56     cl_uint write_success;
57 
58     cl_uint num_packets;
59     cl_uint max_packets;
60     cl_uint read_reservation_is_valid;
61     cl_uint read_success;
62 
63     cl_uint value;
64 };
65 
66 const std::string source_common = R"(
67 struct output_type
68 {
69     uint write_reservation_is_valid;
70     uint write_success;
71 
72     uint num_packets;
73     uint max_packets;
74     uint read_reservation_is_valid;
75     uint read_success;
76 
77     uint value;
78 };
79 )";
80 
81 // -----------------------------------------------------------------------------------
82 // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------
83 // -----------------------------------------------------------------------------------
84 #if defined(DEVELOPMENT) && defined(USE_OPENCLC_KERNELS)
generate_source(test_options options)85 std::string generate_source(test_options options)
86 {
87     std::stringstream s;
88     s << source_common;
89     if (options.operation == pipe_operation::work_item)
90     {
91         s << R"(
92     kernel void producer(write_only pipe uint out_pipe, global struct output_type *output)
93     {
94         const ulong gid = get_global_id(0);
95 
96         output[gid].write_reservation_is_valid = 1;
97 
98         uint value = gid;
99         output[gid].write_success = write_pipe(out_pipe, &value) == 0;
100     }
101 
102     kernel void consumer(read_only pipe uint in_pipe, global struct output_type *output)
103     {
104         const ulong gid = get_global_id(0);
105 
106         output[gid].num_packets = get_pipe_num_packets(in_pipe);
107         output[gid].max_packets = get_pipe_max_packets(in_pipe);
108 
109         output[gid].read_reservation_is_valid = 1;
110 
111         uint value;
112         output[gid].read_success = read_pipe(in_pipe, &value) == 0;
113         output[gid].value = value;
114     }
115     )";
116     }
117     else if (options.operation == pipe_operation::work_item_reservation)
118     {
119         s << R"(
120     kernel void producer(write_only pipe uint out_pipe, global struct output_type *output)
121     {
122         const ulong gid = get_global_id(0);
123         if (gid % 2 == 1) return;
124 
125         reserve_id_t reservation = reserve_write_pipe(out_pipe, 2);
126         output[gid + 0].write_reservation_is_valid = is_valid_reserve_id(reservation);
127         output[gid + 1].write_reservation_is_valid = is_valid_reserve_id(reservation);
128 
129         uint value0 = gid + 0;
130         uint value1 = gid + 1;
131         output[gid + 0].write_success = write_pipe(out_pipe, reservation, 0, &value0) == 0;
132         output[gid + 1].write_success = write_pipe(out_pipe, reservation, 1, &value1) == 0;
133         commit_write_pipe(out_pipe, reservation);
134     }
135 
136     kernel void consumer(read_only pipe uint in_pipe, global struct output_type *output)
137     {
138         const ulong gid = get_global_id(0);
139         if (gid % 2 == 1) return;
140 
141         output[gid + 0].num_packets = get_pipe_num_packets(in_pipe);
142         output[gid + 0].max_packets = get_pipe_max_packets(in_pipe);
143         output[gid + 1].num_packets = get_pipe_num_packets(in_pipe);
144         output[gid + 1].max_packets = get_pipe_max_packets(in_pipe);
145 
146         reserve_id_t reservation = reserve_read_pipe(in_pipe, 2);
147         output[gid + 0].read_reservation_is_valid = is_valid_reserve_id(reservation);
148         output[gid + 1].read_reservation_is_valid = is_valid_reserve_id(reservation);
149 
150         uint value0;
151         uint value1;
152         output[gid + 0].read_success = read_pipe(in_pipe, reservation, 1, &value0) == 0;
153         output[gid + 1].read_success = read_pipe(in_pipe, reservation, 0, &value1) == 0;
154         commit_read_pipe(in_pipe, reservation);
155         output[gid + 0].value = value0;
156         output[gid + 1].value = value1;
157     }
158     )";
159     }
160     else if (options.operation == pipe_operation::work_group_reservation)
161     {
162         s << R"(
163     kernel void producer(write_only pipe uint out_pipe, global struct output_type *output)
164     {
165         const ulong gid = get_global_id(0);
166 
167         reserve_id_t reservation = work_group_reserve_write_pipe(out_pipe, get_local_size(0));
168         output[gid].write_reservation_is_valid = is_valid_reserve_id(reservation);
169 
170         uint value = gid;
171         output[gid].write_success = write_pipe(out_pipe, reservation, get_local_id(0), &value) == 0;
172         work_group_commit_write_pipe(out_pipe, reservation);
173     }
174 
175     kernel void consumer(read_only pipe uint in_pipe, global struct output_type *output)
176     {
177         const ulong gid = get_global_id(0);
178 
179         output[gid].num_packets = get_pipe_num_packets(in_pipe);
180         output[gid].max_packets = get_pipe_max_packets(in_pipe);
181 
182         reserve_id_t reservation = work_group_reserve_read_pipe(in_pipe, get_local_size(0));
183         output[gid].read_reservation_is_valid = is_valid_reserve_id(reservation);
184 
185         uint value;
186         output[gid].read_success = read_pipe(in_pipe, reservation, get_local_size(0) - 1 - get_local_id(0), &value) == 0;
187         work_group_commit_read_pipe(in_pipe, reservation);
188         output[gid].value = value;
189     }
190     )";
191     }
192     else if (options.operation == pipe_operation::sub_group_reservation)
193     {
194         s << R"(
195     #pragma OPENCL EXTENSION cl_khr_subgroups : enable
196 
197     kernel void producer(write_only pipe uint out_pipe, global struct output_type *output)
198     {
199         const ulong gid = get_global_id(0);
200 
201         reserve_id_t reservation = sub_group_reserve_write_pipe(out_pipe, get_sub_group_size());
202         output[gid].write_reservation_is_valid = is_valid_reserve_id(reservation);
203 
204         uint value = gid;
205         output[gid].write_success = write_pipe(out_pipe, reservation, get_sub_group_local_id(), &value) == 0;
206         sub_group_commit_write_pipe(out_pipe, reservation);
207     }
208 
209     kernel void consumer(read_only pipe uint in_pipe, global struct output_type *output)
210     {
211         const ulong gid = get_global_id(0);
212 
213         output[gid].num_packets = get_pipe_num_packets(in_pipe);
214         output[gid].max_packets = get_pipe_max_packets(in_pipe);
215 
216         reserve_id_t reservation = sub_group_reserve_read_pipe(in_pipe, get_sub_group_size());
217         output[gid].read_reservation_is_valid = is_valid_reserve_id(reservation);
218 
219         uint value;
220         output[gid].read_success = read_pipe(in_pipe, reservation, get_sub_group_size() - 1 - get_sub_group_local_id(), &value) == 0;
221         sub_group_commit_read_pipe(in_pipe, reservation);
222         output[gid].value = value;
223     }
224     )";
225     }
226 
227     return s.str();
228 }
229 #else
generate_source(test_options options)230 std::string generate_source(test_options options)
231 {
232     std::stringstream s;
233     s << R"(
234     #include <opencl_memory>
235     #include <opencl_common>
236     #include <opencl_work_item>
237     #include <opencl_synchronization>
238     #include <opencl_pipe>
239     using namespace cl;
240     )";
241 
242     s << source_common;
243 
244     std::string init_out_pipe;
245     std::string init_in_pipe;
246     if (options.source == pipe_source::param)
247     {
248         init_out_pipe = "auto out_pipe = pipe_param;";
249         init_in_pipe = "auto in_pipe = pipe_param;";
250     }
251     else if (options.source == pipe_source::storage)
252     {
253         s << "pipe_storage<uint, " << std::to_string(options.max_packets) << "> storage;";
254         init_out_pipe = "auto out_pipe = storage.get<pipe_access::write>();";
255         init_in_pipe = "auto in_pipe = make_pipe(storage);";
256     }
257 
258     if (options.operation == pipe_operation::work_item)
259     {
260         s << R"(
261     kernel void producer(pipe<uint, pipe_access::write> pipe_param, global_ptr<output_type[]> output)
262     {
263         )" << init_out_pipe << R"(
264         const ulong gid = get_global_id(0);
265 
266         output[gid].write_reservation_is_valid = 1;
267 
268         uint value = gid;
269         output[gid].write_success = out_pipe.write(value);
270     }
271 
272     kernel void consumer(pipe<uint, pipe_access::read> pipe_param, global_ptr<output_type[]> output)
273     {
274         )" << init_in_pipe << R"(
275         const ulong gid = get_global_id(0);
276 
277         output[gid].num_packets = in_pipe.num_packets();
278         output[gid].max_packets = in_pipe.max_packets();
279 
280         output[gid].read_reservation_is_valid = 1;
281 
282         uint value;
283         output[gid].read_success = in_pipe.read(value);
284         output[gid].value = value;
285     }
286     )";
287     }
288     else if (options.operation == pipe_operation::work_item_reservation)
289     {
290         s << R"(
291     kernel void producer(pipe<uint, pipe_access::write> pipe_param, global_ptr<output_type[]> output)
292     {
293         )" << init_out_pipe << R"(
294         const ulong gid = get_global_id(0);
295         if (gid % 2 == 1) return;
296 
297         auto reservation = out_pipe.reserve(2);
298         output[gid + 0].write_reservation_is_valid = reservation.is_valid();
299         output[gid + 1].write_reservation_is_valid = reservation.is_valid();
300 
301         uint value0 = gid + 0;
302         uint value1 = gid + 1;
303         output[gid + 0].write_success = reservation.write(0, value0);
304         output[gid + 1].write_success = reservation.write(1, value1);
305         reservation.commit();
306     }
307 
308     kernel void consumer(pipe<uint, pipe_access::read> pipe_param, global_ptr<output_type[]> output)
309     {
310         )" << init_in_pipe << R"(
311         const ulong gid = get_global_id(0);
312         if (gid % 2 == 1) return;
313 
314         output[gid + 0].num_packets = in_pipe.num_packets();
315         output[gid + 0].max_packets = in_pipe.max_packets();
316         output[gid + 1].num_packets = in_pipe.num_packets();
317         output[gid + 1].max_packets = in_pipe.max_packets();
318 
319         auto reservation = in_pipe.reserve(2);
320         output[gid + 0].read_reservation_is_valid = reservation.is_valid();
321         output[gid + 1].read_reservation_is_valid = reservation.is_valid();
322 
323         uint value0;
324         uint value1;
325         output[gid + 0].read_success = reservation.read(1, value0);
326         output[gid + 1].read_success = reservation.read(0, value1);
327         reservation.commit();
328         output[gid + 0].value = value0;
329         output[gid + 1].value = value1;
330     }
331     )";
332     }
333     else if (options.operation == pipe_operation::work_group_reservation)
334     {
335         s << R"(
336     kernel void producer(pipe<uint, pipe_access::write> pipe_param, global_ptr<output_type[]> output)
337     {
338         )" << init_out_pipe << R"(
339         const ulong gid = get_global_id(0);
340 
341         auto reservation = out_pipe.work_group_reserve(get_local_size(0));
342         output[gid].write_reservation_is_valid = reservation.is_valid();
343 
344         uint value = gid;
345         output[gid].write_success = reservation.write(get_local_id(0), value);
346         reservation.commit();
347     }
348 
349     kernel void consumer(pipe<uint, pipe_access::read> pipe_param, global_ptr<output_type[]> output)
350     {
351         )" << init_in_pipe << R"(
352         const ulong gid = get_global_id(0);
353 
354         output[gid].num_packets = in_pipe.num_packets();
355         output[gid].max_packets = in_pipe.max_packets();
356 
357         auto reservation = in_pipe.work_group_reserve(get_local_size(0));
358         output[gid].read_reservation_is_valid = reservation.is_valid();
359 
360         uint value;
361         output[gid].read_success = reservation.read(get_local_size(0) - 1 - get_local_id(0), value);
362         reservation.commit();
363         output[gid].value = value;
364     }
365     )";
366     }
367     else if (options.operation == pipe_operation::sub_group_reservation)
368     {
369         s << R"(
370     kernel void producer(pipe<uint, pipe_access::write> pipe_param, global_ptr<output_type[]> output)
371     {
372         )" << init_out_pipe << R"(
373         const ulong gid = get_global_id(0);
374 
375         auto reservation = out_pipe.sub_group_reserve(get_sub_group_size());
376         output[gid].write_reservation_is_valid = reservation.is_valid();
377 
378         uint value = gid;
379         output[gid].write_success = reservation.write(get_sub_group_local_id(), value);
380         reservation.commit();
381     }
382 
383     kernel void consumer(pipe<uint, pipe_access::read> pipe_param, global_ptr<output_type[]> output)
384     {
385         )" << init_in_pipe << R"(
386         const ulong gid = get_global_id(0);
387 
388         output[gid].num_packets = in_pipe.num_packets();
389         output[gid].max_packets = in_pipe.max_packets();
390 
391         auto reservation = in_pipe.sub_group_reserve(get_sub_group_size());
392         output[gid].read_reservation_is_valid = reservation.is_valid();
393 
394         uint value;
395         output[gid].read_success = reservation.read(get_sub_group_size() - 1 - get_sub_group_local_id(), value);
396         reservation.commit();
397         output[gid].value = value;
398     }
399     )";
400     }
401 
402     return s.str();
403 }
404 #endif
405 
test(cl_device_id device,cl_context context,cl_command_queue queue,test_options options)406 int test(cl_device_id device, cl_context context, cl_command_queue queue, test_options options)
407 {
408     int error = CL_SUCCESS;
409 
410     if (options.num_packets % 2 != 0 || options.max_packets < options.num_packets)
411     {
412         RETURN_ON_ERROR_MSG(-1, "Invalid test options")
413     }
414 
415 #if defined(DEVELOPMENT) && defined(USE_OPENCLC_KERNELS)
416     if (options.operation == pipe_operation::sub_group_reservation && !is_extension_available(device, "cl_khr_subgroups"))
417     {
418         log_info("SKIPPED: Extension `cl_khr_subgroups` is not supported. Skipping tests.\n");
419         return CL_SUCCESS;
420     }
421 #endif
422 
423     cl_program program;
424     cl_kernel producer_kernel;
425     cl_kernel consumer_kernel;
426 
427     std::string producer_kernel_name = "producer";
428     std::string consumer_kernel_name = "consumer";
429     std::string source = generate_source(options);
430 
431 // -----------------------------------------------------------------------------------
432 // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------
433 // -----------------------------------------------------------------------------------
434 // Only OpenCL C++ to SPIR-V compilation
435 #if defined(DEVELOPMENT) && defined(ONLY_SPIRV_COMPILATION)
436     error = create_opencl_kernel(
437         context, &program, &producer_kernel,
438         source, producer_kernel_name
439     );
440     RETURN_ON_ERROR(error)
441     return error;
442 // Use OpenCL C kernels instead of OpenCL C++ kernels (test C++ host code)
443 #elif defined(DEVELOPMENT) && defined(USE_OPENCLC_KERNELS)
444     error = create_opencl_kernel(
445         context, &program, &producer_kernel,
446         source, producer_kernel_name, "-cl-std=CL2.0", false
447     );
448     RETURN_ON_ERROR(error)
449     consumer_kernel = clCreateKernel(program, consumer_kernel_name.c_str(), &error);
450     RETURN_ON_CL_ERROR(error, "clCreateKernel")
451 // Normal run
452 #else
453     error = create_opencl_kernel(
454         context, &program, &producer_kernel,
455         source, producer_kernel_name
456     );
457     RETURN_ON_ERROR(error)
458     consumer_kernel = clCreateKernel(program, consumer_kernel_name.c_str(), &error);
459     RETURN_ON_CL_ERROR(error, "clCreateKernel")
460 #endif
461 
462     size_t max_work_group_size;
463     error = clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &max_work_group_size, NULL);
464     RETURN_ON_CL_ERROR(error, "clGetDeviceInfo")
465 
466     const size_t count = options.num_packets;
467     const size_t local_size = (std::min)((size_t)256, max_work_group_size);
468     const size_t global_size = count;
469 
470     const cl_uint packet_size = sizeof(cl_uint);
471 
472     cl_mem pipe = clCreatePipe(context, 0, packet_size, options.max_packets, NULL, &error);
473     RETURN_ON_CL_ERROR(error, "clCreatePipe")
474 
475     cl_mem output_buffer;
476     output_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(output_type) * count, NULL, &error);
477     RETURN_ON_CL_ERROR(error, "clCreateBuffer")
478 
479     const char pattern = 0;
480     error = clEnqueueFillBuffer(queue, output_buffer, &pattern, sizeof(pattern), 0, sizeof(output_type) * count, 0, NULL, NULL);
481     RETURN_ON_CL_ERROR(error, "clEnqueueFillBuffer")
482 
483     error = clSetKernelArg(producer_kernel, 0, sizeof(cl_mem), &pipe);
484     RETURN_ON_CL_ERROR(error, "clSetKernelArg")
485     error = clSetKernelArg(producer_kernel, 1, sizeof(output_buffer), &output_buffer);
486     RETURN_ON_CL_ERROR(error, "clSetKernelArg")
487 
488     error = clEnqueueNDRangeKernel(queue, producer_kernel, 1, NULL, &global_size, NULL, 0, NULL, NULL);
489     RETURN_ON_CL_ERROR(error, "clEnqueueNDRangeKernel")
490 
491     error = clSetKernelArg(consumer_kernel, 0, sizeof(cl_mem), &pipe);
492     RETURN_ON_CL_ERROR(error, "clSetKernelArg")
493     error = clSetKernelArg(consumer_kernel, 1, sizeof(output_buffer), &output_buffer);
494     RETURN_ON_CL_ERROR(error, "clSetKernelArg")
495 
496     error = clEnqueueNDRangeKernel(queue, consumer_kernel, 1, NULL, &global_size, &local_size, 0, NULL, NULL);
497     RETURN_ON_CL_ERROR(error, "clEnqueueNDRangeKernel")
498 
499     std::vector<output_type> output(count);
500     error = clEnqueueReadBuffer(
501         queue, output_buffer, CL_TRUE,
502         0, sizeof(output_type) * count,
503         static_cast<void *>(output.data()),
504         0, NULL, NULL
505     );
506     RETURN_ON_CL_ERROR(error, "clEnqueueReadBuffer")
507 
508     std::vector<bool> existing_values(count, false);
509     for (size_t gid = 0; gid < count; gid++)
510     {
511         const output_type &o = output[gid];
512 
513         if (!o.write_reservation_is_valid)
514         {
515             RETURN_ON_ERROR_MSG(-1, "write reservation is not valid")
516         }
517         if (!o.write_success)
518         {
519             RETURN_ON_ERROR_MSG(-1, "write did not succeed")
520         }
521 
522         if (o.num_packets == 0 || o.num_packets > options.num_packets)
523         {
524             RETURN_ON_ERROR_MSG(-1, "num_packets did not return correct value")
525         }
526         if (o.max_packets != options.max_packets)
527         {
528             RETURN_ON_ERROR_MSG(-1, "max_packets did not return correct value")
529         }
530         if (!o.read_reservation_is_valid)
531         {
532             RETURN_ON_ERROR_MSG(-1, "read reservation is not valid")
533         }
534         if (!o.read_success)
535         {
536             RETURN_ON_ERROR_MSG(-1, "read did not succeed")
537         }
538 
539         // Every value must be presented once in any order
540         if (o.value >= count || existing_values[o.value])
541         {
542             RETURN_ON_ERROR_MSG(-1, "kernel did not return correct value")
543         }
544         existing_values[o.value] = true;
545     }
546 
547     clReleaseMemObject(pipe);
548     clReleaseMemObject(output_buffer);
549     clReleaseKernel(producer_kernel);
550     clReleaseKernel(consumer_kernel);
551     clReleaseProgram(program);
552     return error;
553 }
554 
555 const pipe_operation pipe_operations[] = {
556     pipe_operation::work_item,
557     pipe_operation::work_item_reservation,
558     pipe_operation::work_group_reservation,
559     pipe_operation::sub_group_reservation
560 };
561 
562 const std::tuple<int, int> max_and_num_packets[] = {
563     std::make_tuple<int, int>(2, 2),
564     std::make_tuple<int, int>(10, 8),
565     std::make_tuple<int, int>(256, 254),
566     std::make_tuple<int, int>(1 << 16, 1 << 16),
567     std::make_tuple<int, int>((1 << 16) + 5, 1 << 16),
568     std::make_tuple<int, int>(12345, 12344),
569     std::make_tuple<int, int>(1 << 18, 1 << 18)
570 };
571 
AUTO_TEST_CASE(test_pipes_pipe)572 AUTO_TEST_CASE(test_pipes_pipe)
573 (cl_device_id device, cl_context context, cl_command_queue queue, int num_elements)
574 {
575     std::vector<std::tuple<int, int>> ps;
576     for (auto p : max_and_num_packets)
577     {
578         if (std::get<0>(p) < num_elements)
579             ps.push_back(p);
580     }
581     ps.push_back(std::tuple<int, int>(num_elements, num_elements));
582 
583     int error = CL_SUCCESS;
584 
585     for (auto operation : pipe_operations)
586     for (auto p : ps)
587     {
588         test_options options;
589         options.source = pipe_source::param;
590         options.max_packets = std::get<0>(p);
591         options.num_packets = std::get<1>(p);
592         options.operation = operation;
593 
594         error = test(device, context, queue, options);
595         RETURN_ON_ERROR(error)
596     }
597 
598     return error;
599 }
600 
AUTO_TEST_CASE(test_pipes_pipe_storage)601 AUTO_TEST_CASE(test_pipes_pipe_storage)
602 (cl_device_id device, cl_context context, cl_command_queue queue, int num_elements)
603 {
604     std::vector<std::tuple<int, int>> ps;
605     for (auto p : max_and_num_packets)
606     {
607         if (std::get<0>(p) < num_elements)
608             ps.push_back(p);
609     }
610     ps.push_back(std::tuple<int, int>(num_elements, num_elements));
611 
612     int error = CL_SUCCESS;
613 
614     for (auto operation : pipe_operations)
615     for (auto p : ps)
616     {
617         test_options options;
618         options.source = pipe_source::storage;
619         options.max_packets = std::get<0>(p);
620         options.num_packets = std::get<1>(p);
621         options.operation = operation;
622 
623         error = test(device, context, queue, options);
624         RETURN_ON_ERROR(error)
625     }
626 
627     return error;
628 }
629 
630 } // namespace
631 
632 #endif // TEST_CONFORMANCE_CLCPP_PIPES_TEST_PIPES_HPP
633