1 //
2 // Copyright (c) 2023 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 BASIC_TEST_CONVERSIONS_H
17 #define BASIC_TEST_CONVERSIONS_H
18
19 #if !defined(_WIN32)
20 #include <unistd.h>
21 #endif
22
23 #include "harness/errorHelpers.h"
24 #include "harness/rounding_mode.h"
25
26 #include <stdio.h>
27 #if defined( __APPLE__ )
28 #include <OpenCL/opencl.h>
29 #else
30 #include <CL/opencl.h>
31 #endif
32
33 #include "harness/mt19937.h"
34 #include "harness/testHarness.h"
35 #include "harness/typeWrappers.h"
36
37 #include <memory>
38 #include <tuple>
39 #include <vector>
40
41 #include "conversions_data_info.h"
42
43 #define kVectorSizeCount 6
44 #define kMaxVectorSize 16
45 #define kPageSize 4096
46
47 #define BUFFER_SIZE (1024 * 1024)
48 #define EMBEDDED_REDUCTION_FACTOR 16
49 #define PERF_LOOP_COUNT 100
50
51 extern const char *gTypeNames[ kTypeCount ];
52 extern const char *gRoundingModeNames[ kRoundingModeCount ]; // { "", "_rte", "_rtp", "_rtn", "_rtz" }
53 extern const char *gSaturationNames[ kSaturationModeCount ]; // { "", "_sat" }
54 extern const char *gVectorSizeNames[kVectorSizeCount]; // { "", "2", "4", "8", "16" }
55 extern size_t gTypeSizes[ kTypeCount ];
56
57 //Functions for clamping floating point numbers into the representable range for the type
58 typedef float (*clampf)( float );
59 typedef double (*clampd)( double );
60
61 extern clampf gClampFloat[ kTypeCount ][kRoundingModeCount];
62 extern clampd gClampDouble[ kTypeCount ][kRoundingModeCount];
63
64 typedef void (*InitDataFunc)( void *dest, SaturationMode, RoundingMode, Type destType, uint64_t start, int count, MTdata d );
65 extern InitDataFunc gInitFunctions[ kTypeCount ];
66
67 typedef int (*CheckResults)( void *out1, void *out2, void *allowZ, uint32_t count, int vectorSize );
68 extern CheckResults gCheckResults[ kTypeCount ];
69
70 #define kCallStyleCount (kVectorSizeCount + 1 /* for implicit scalar */)
71
72 extern MTdata gMTdata;
73 extern cl_command_queue gQueue;
74 extern cl_context gContext;
75 extern cl_mem gInBuffer;
76 extern cl_mem gOutBuffers[];
77 extern int gHasDouble;
78 extern int gTestDouble;
79 extern int gWimpyMode;
80 extern int gWimpyReductionFactor;
81 extern int gSkipTesting;
82 extern int gMinVectorSize;
83 extern int gMaxVectorSize;
84 extern int gForceFTZ;
85 extern int gTimeResults;
86 extern int gReportAverageTimes;
87 extern int gStartTestNumber;
88 extern int gEndTestNumber;
89 extern int gIsRTZ;
90 extern void *gIn;
91 extern void *gRef;
92 extern void *gAllowZ;
93 extern void *gOut[];
94
95 extern const char **argList;
96 extern int argCount;
97
98 extern const char *sizeNames[];
99 extern int vectorSizes[];
100
101 extern size_t gComputeDevices;
102 extern uint32_t gDeviceFrequency;
103
104 namespace conv_test {
105
106 cl_program MakeProgram(Type outType, Type inType, SaturationMode sat,
107 RoundingMode round, int vectorSize,
108 cl_kernel *outKernel);
109
110 int RunKernel(cl_kernel kernel, void *inBuf, void *outBuf, size_t blockCount);
111
112 int GetTestCase(const char *name, Type *outType, Type *inType,
113 SaturationMode *sat, RoundingMode *round);
114
115 cl_int InitData(cl_uint job_id, cl_uint thread_id, void *p);
116 cl_int PrepareReference(cl_uint job_id, cl_uint thread_id, void *p);
117 uint64_t GetTime(void);
118
119 void WriteInputBufferComplete(void *);
120 void *FlushToZero(void);
121 void UnFlushToZero(void *);
122 }
123
124 struct CalcRefValsBase
125 {
126 virtual ~CalcRefValsBase() = default;
check_resultCalcRefValsBase127 virtual int check_result(void *, uint32_t, int) { return 0; }
128
129 // pointer back to the parent WriteInputBufferInfo struct
130 struct WriteInputBufferInfo *parent;
131 clKernelWrapper kernel; // the kernel for this vector size
132 clProgramWrapper program; // the program for this vector size
133 cl_uint vectorSize; // the vector size for this callback chain
134 void *p; // the pointer to mapped result data for this vector size
135 cl_int result;
136 };
137
138 template <typename InType, typename OutType>
139 struct CalcRefValsPat : CalcRefValsBase
140 {
141 int check_result(void *, uint32_t, int) override;
142 };
143
144 struct WriteInputBufferInfo
145 {
WriteInputBufferInfoWriteInputBufferInfo146 WriteInputBufferInfo()
147 : calcReferenceValues(nullptr), doneBarrier(nullptr), count(0),
148 outType(kuchar), inType(kuchar), barrierCount(0)
149 {}
150
151 volatile cl_event
152 calcReferenceValues; // user event which signals when main thread is
153 // done calculating reference values
154 volatile cl_event
155 doneBarrier; // user event which signals when worker threads are done
156 cl_uint count; // the number of elements in the array
157 Type outType; // the data type of the conversion result
158 Type inType; // the data type of the conversion input
159 volatile int barrierCount;
160
161 std::vector<std::unique_ptr<CalcRefValsBase>> calcInfo;
162 };
163
164 // Must be aligned with Type enums!
165 using TypeIter = std::tuple<cl_uchar, cl_char, cl_ushort, cl_short, cl_uint,
166 cl_int, cl_float, cl_double, cl_ulong, cl_long>;
167
168 // Helper test fixture for constructing OpenCL objects used in testing
169 // a variety of simple command-buffer enqueue scenarios.
170 struct ConversionsTest
171 {
172 virtual ~ConversionsTest() = default;
173
174 ConversionsTest(cl_device_id device, cl_context context,
175 cl_command_queue queue);
176
177 cl_int SetUp(int elements);
178
179 // Test body returning an OpenCL error code
180 cl_int Run();
181
182 template <typename InType, typename OutType>
183 int DoTest(Type outType, Type inType, SaturationMode sat,
184 RoundingMode round);
185
186 template <typename InType, typename OutType>
187 void TestTypesConversion(const Type &inType, const Type &outType, int &tn,
188 const int smvs);
189
190 protected:
191 cl_context context;
192 cl_device_id device;
193 cl_command_queue queue;
194
195 size_t num_elements;
196
197 TypeIter typeIterator;
198 };
199
200 struct CustomConversionsTest : ConversionsTest
201 {
CustomConversionsTestCustomConversionsTest202 CustomConversionsTest(cl_device_id device, cl_context context,
203 cl_command_queue queue)
204 : ConversionsTest(device, context, queue)
205 {}
206
207 cl_int Run();
208 };
209
210 template <class T>
MakeAndRunTest(cl_device_id device,cl_context context,cl_command_queue queue,int num_elements)211 int MakeAndRunTest(cl_device_id device, cl_context context,
212 cl_command_queue queue, int num_elements)
213 {
214 T test_fixture(device, context, queue);
215
216 cl_int error = test_fixture.SetUp(num_elements);
217 test_error_ret(error, "Error in test initialization", TEST_FAIL);
218
219 return test_fixture.Run();
220 }
221
222 struct TestType
223 {
testTypeTestType224 template <typename T> bool testType(Type in)
225 {
226 switch (in)
227 {
228 default: return false;
229 case kuchar: return std::is_same<cl_uchar, T>::value;
230 case kchar: return std::is_same<cl_char, T>::value;
231 case kushort: return std::is_same<cl_ushort, T>::value;
232 case kshort: return std::is_same<cl_short, T>::value;
233 case kuint: return std::is_same<cl_uint, T>::value;
234 case kint: return std::is_same<cl_int, T>::value;
235 case kfloat: return std::is_same<cl_float, T>::value;
236 case kdouble: return std::is_same<cl_double, T>::value;
237 case kulong: return std::is_same<cl_ulong, T>::value;
238 case klong: return std::is_same<cl_long, T>::value;
239 }
240 }
241 };
242
243 // Helper structures to iterate over all tuple attributes of different types
244 struct IterOverTypes : public TestType
245 {
IterOverTypesIterOverTypes246 IterOverTypes(const TypeIter &typeIter, ConversionsTest &test)
247 : inType((Type)0), outType((Type)0), typeIter(typeIter), test(test),
248 testNumber(-1), startMinVectorSize(gMinVectorSize)
249 {}
250
RunIterOverTypes251 void Run() { for_each_out_elem(typeIter); }
252
253 protected:
254 template <std::size_t Out = 0, typename OutType>
iterate_out_typeIterOverTypes255 void iterate_out_type(const OutType &t)
256 {
257 for_each_in_elem<0, Out, OutType>(typeIter);
258 outType = (Type)(outType + 1);
259 inType = (Type)0;
260 }
261
262 template <std::size_t In, std::size_t Out, typename OutType,
263 typename InType>
iterate_in_typeIterOverTypes264 void iterate_in_type(const InType &t)
265 {
266 if (!testType<InType>(inType)) vlog_error("Unexpected data type!\n");
267
268 if (!testType<OutType>(outType)) vlog_error("Unexpected data type!\n");
269
270 // run the conversions
271 test.TestTypesConversion<InType, OutType>(inType, outType, testNumber,
272 startMinVectorSize);
273 inType = (Type)(inType + 1);
274 }
275
276 template <std::size_t Out = 0, typename... Tp>
277 inline typename std::enable_if<Out == sizeof...(Tp), void>::type
for_each_out_elemIterOverTypes278 for_each_out_elem(
279 const std::tuple<Tp...> &) // Unused arguments are given no names.
280 {}
281
282 template <std::size_t Out = 0, typename... Tp>
283 inline typename std::enable_if < Out<sizeof...(Tp), void>::type
284 for_each_out_elem(const std::tuple<Tp...> &t)
285 {
286 iterate_out_type<Out>(std::get<Out>(t));
287 for_each_out_elem<Out + 1, Tp...>(t);
288 }
289
290 template <std::size_t In = 0, std::size_t Out, typename OutType,
291 typename... Tp>
292 inline typename std::enable_if<In == sizeof...(Tp), void>::type
for_each_in_elemIterOverTypes293 for_each_in_elem(
294 const std::tuple<Tp...> &) // Unused arguments are given no names.
295 {}
296
297 template <std::size_t In = 0, std::size_t Out, typename OutType,
298 typename... Tp>
299 inline typename std::enable_if < In<sizeof...(Tp), void>::type
300 for_each_in_elem(const std::tuple<Tp...> &t)
301 {
302 iterate_in_type<In, Out, OutType>(std::get<In>(t));
303 for_each_in_elem<In + 1, Out, OutType, Tp...>(t);
304 }
305
306 protected:
307 Type inType;
308 Type outType;
309 const TypeIter &typeIter;
310 ConversionsTest &test;
311 int testNumber;
312 int startMinVectorSize;
313 };
314
315
316 // Helper structures to select type 2 type conversion test case
317 struct IterOverSelectedTypes : public TestType
318 {
IterOverSelectedTypesIterOverSelectedTypes319 IterOverSelectedTypes(const TypeIter &typeIter, ConversionsTest &test,
320 const Type in, const Type out,
321 const RoundingMode round, const SaturationMode sat)
322 : inType(in), outType(out), rounding(round), saturation(sat),
323 typeIter(typeIter), test(test), testNumber(-1),
324 startMinVectorSize(gMinVectorSize)
325 {}
326
RunIterOverSelectedTypes327 void Run() { for_each_out_elem(typeIter); }
328
329 protected:
330 template <std::size_t Out = 0, typename OutType>
iterate_out_typeIterOverSelectedTypes331 void iterate_out_type(const OutType &t)
332 {
333 for_each_in_elem<0, Out, OutType>(typeIter);
334 }
335
336 template <std::size_t In, std::size_t Out, typename OutType,
337 typename InType>
iterate_in_typeIterOverSelectedTypes338 void iterate_in_type(const InType &t)
339 {
340 if (testType<InType>(inType) && testType<OutType>(outType))
341 {
342 // run selected conversion
343 // testing of the result will happen afterwards
344 test.DoTest<InType, OutType>(outType, inType, saturation, rounding);
345 }
346 }
347
348 template <std::size_t Out = 0, typename... Tp>
349 inline typename std::enable_if<Out == sizeof...(Tp), void>::type
for_each_out_elemIterOverSelectedTypes350 for_each_out_elem(const std::tuple<Tp...> &)
351 {}
352
353 template <std::size_t Out = 0, typename... Tp>
354 inline typename std::enable_if < Out<sizeof...(Tp), void>::type
355 for_each_out_elem(const std::tuple<Tp...> &t)
356 {
357 iterate_out_type<Out>(std::get<Out>(t));
358 for_each_out_elem<Out + 1, Tp...>(t);
359 }
360
361 template <std::size_t In = 0, std::size_t Out, typename OutType,
362 typename... Tp>
363 inline typename std::enable_if<In == sizeof...(Tp), void>::type
for_each_in_elemIterOverSelectedTypes364 for_each_in_elem(const std::tuple<Tp...> &)
365 {}
366
367 template <std::size_t In = 0, std::size_t Out, typename OutType,
368 typename... Tp>
369 inline typename std::enable_if < In<sizeof...(Tp), void>::type
370 for_each_in_elem(const std::tuple<Tp...> &t)
371 {
372 iterate_in_type<In, Out, OutType>(std::get<In>(t));
373 for_each_in_elem<In + 1, Out, OutType, Tp...>(t);
374 }
375
376 protected:
377 Type inType;
378 Type outType;
379 RoundingMode rounding;
380 SaturationMode saturation;
381
382 const TypeIter &typeIter;
383 ConversionsTest &test;
384 int testNumber;
385 int startMinVectorSize;
386 };
387
388
389 #endif /* BASIC_TEST_CONVERSIONS_H */
390
391