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 #include "../testBase.h"
17 
18 extern void read_image_pixel_float( void *imageData, image_descriptor *imageInfo, int x, int y, int z, float *outData );
19 
free_pitch_buffer(cl_mem image,void * buf)20 static void CL_CALLBACK free_pitch_buffer( cl_mem image, void *buf )
21 {
22     free( buf );
23 }
24 
create_image(cl_context context,cl_command_queue queue,BufferOwningPtr<char> & data,image_descriptor * imageInfo,int * error)25 cl_mem create_image( cl_context context, cl_command_queue queue, BufferOwningPtr<char>& data, image_descriptor *imageInfo, int *error )
26 {
27     cl_mem img;
28     cl_image_desc imageDesc;
29     cl_mem_flags mem_flags = CL_MEM_READ_ONLY;
30     void *host_ptr = NULL;
31 
32     memset(&imageDesc, 0x0, sizeof(cl_image_desc));
33     imageDesc.image_type = imageInfo->type;
34     imageDesc.image_width = imageInfo->width;
35     imageDesc.image_height = imageInfo->height;
36     imageDesc.image_depth = imageInfo->depth;
37     imageDesc.image_array_size = imageInfo->arraySize;
38     imageDesc.image_row_pitch = gEnablePitch ? imageInfo->rowPitch : 0;
39     imageDesc.image_slice_pitch = gEnablePitch ? imageInfo->slicePitch : 0;
40 
41     switch (imageInfo->type)
42     {
43         case CL_MEM_OBJECT_IMAGE1D:
44             if ( gDebugTrace )
45                 log_info( " - Creating 1D image %d ...\n", (int)imageInfo->width );
46             if ( gEnablePitch )
47                 host_ptr = malloc( imageInfo->rowPitch );
48             break;
49         case CL_MEM_OBJECT_IMAGE2D:
50             if ( gDebugTrace )
51                 log_info( " - Creating 2D image %d by %d ...\n", (int)imageInfo->width, (int)imageInfo->height );
52             if ( gEnablePitch )
53                 host_ptr = malloc( imageInfo->height * imageInfo->rowPitch );
54             break;
55         case CL_MEM_OBJECT_IMAGE3D:
56             if ( gDebugTrace )
57                 log_info( " - Creating 3D image %d by %d by %d...\n", (int)imageInfo->width, (int)imageInfo->height, (int)imageInfo->depth );
58             if ( gEnablePitch )
59                 host_ptr = malloc( imageInfo->depth * imageInfo->slicePitch );
60             break;
61         case CL_MEM_OBJECT_IMAGE1D_ARRAY:
62             if ( gDebugTrace )
63                 log_info( " - Creating 1D image array %d by %d...\n", (int)imageInfo->width, (int)imageInfo->arraySize );
64             if ( gEnablePitch )
65                 host_ptr = malloc( imageInfo->arraySize * imageInfo->slicePitch );
66             break;
67         case CL_MEM_OBJECT_IMAGE2D_ARRAY:
68             if ( gDebugTrace )
69                 log_info( " - Creating 2D image array %d by %d by %d...\n", (int)imageInfo->width, (int)imageInfo->height, (int)imageInfo->arraySize );
70             if ( gEnablePitch )
71                 host_ptr = malloc( imageInfo->arraySize * imageInfo->slicePitch );
72             break;
73     }
74 
75     if (gEnablePitch)
76     {
77         if ( NULL == host_ptr )
78         {
79             log_error( "ERROR: Unable to create backing store for pitched 3D image. %ld bytes\n",  imageInfo->depth * imageInfo->slicePitch );
80             return NULL;
81         }
82         mem_flags = CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR;
83     }
84 
85     img = clCreateImage(context, mem_flags, imageInfo->format, &imageDesc, host_ptr, error);
86 
87     if (gEnablePitch)
88     {
89         if ( *error == CL_SUCCESS )
90         {
91             int callbackError = clSetMemObjectDestructorCallback( img, free_pitch_buffer, host_ptr );
92             if ( CL_SUCCESS != callbackError )
93             {
94                 free( host_ptr );
95                 log_error( "ERROR: Unable to attach destructor callback to pitched 3D image. Err: %d\n", callbackError );
96                 clReleaseMemObject( img );
97                 return NULL;
98             }
99         }
100         else
101             free(host_ptr);
102     }
103 
104     if ( *error != CL_SUCCESS )
105     {
106         long long unsigned imageSize = get_image_size_mb( imageInfo );
107         switch (imageInfo->type)
108         {
109             case CL_MEM_OBJECT_IMAGE1D:
110                 log_error( "ERROR: Unable to create 1D image of size %d (%llu MB): %s\n", (int)imageInfo->width, imageSize, IGetErrorString( *error ) );
111                 break;
112             case CL_MEM_OBJECT_IMAGE2D:
113                 log_error( "ERROR: Unable to create 2D image of size %d x %d (%llu MB): %s\n", (int)imageInfo->width, (int)imageInfo->height, imageSize, IGetErrorString( *error ) );
114                 break;
115             case CL_MEM_OBJECT_IMAGE3D:
116                 log_error( "ERROR: Unable to create 3D image of size %d x %d x %d (%llu MB): %s\n", (int)imageInfo->width, (int)imageInfo->height, (int)imageInfo->depth, imageSize, IGetErrorString( *error ) );
117                 break;
118             case CL_MEM_OBJECT_IMAGE1D_ARRAY:
119                 log_error( "ERROR: Unable to create 1D image array of size %d x %d (%llu MB): %s\n", (int)imageInfo->width, (int)imageInfo->arraySize, imageSize, IGetErrorString( *error ) );
120                 break;
121                 break;
122             case CL_MEM_OBJECT_IMAGE2D_ARRAY:
123                 log_error( "ERROR: Unable to create 2D image array of size %d x %d x %d (%llu MB): %s\n", (int)imageInfo->width, (int)imageInfo->height, (int)imageInfo->arraySize, imageSize, IGetErrorString( *error ) );
124                 break;
125         }
126         return NULL;
127     }
128 
129     // Copy the specified data to the image via a Map operation.
130     size_t mappedRow, mappedSlice;
131     size_t height;
132     size_t depth;
133     size_t imageSize = 0;
134 
135     switch (imageInfo->type)
136     {
137         case CL_MEM_OBJECT_IMAGE1D_ARRAY:
138             height = imageInfo->arraySize;
139             depth = 1;
140             imageSize = imageInfo->rowPitch * imageInfo->arraySize;
141             break;
142         case CL_MEM_OBJECT_IMAGE1D:
143             height = depth = 1;
144             imageSize = imageInfo->rowPitch;
145             break;
146         case CL_MEM_OBJECT_IMAGE2D:
147             height = imageInfo->height;
148             depth = 1;
149             imageSize = imageInfo->rowPitch * imageInfo->height;
150             break;
151         case CL_MEM_OBJECT_IMAGE2D_ARRAY:
152             height = imageInfo->height;
153             depth = imageInfo->arraySize;
154             imageSize = imageInfo->slicePitch * imageInfo->arraySize;
155             break;
156         case CL_MEM_OBJECT_IMAGE3D:
157             height = imageInfo->height;
158             depth = imageInfo->depth;
159             imageSize = imageInfo->slicePitch * imageInfo->depth;
160             break;
161     }
162 
163     size_t origin[ 3 ] = { 0, 0, 0 };
164     size_t region[ 3 ] = { imageInfo->width, height, depth };
165 
166     void* mapped = (char*)clEnqueueMapImage(queue, img, CL_TRUE, CL_MAP_WRITE, origin, region, &mappedRow, &mappedSlice, 0, NULL, NULL, error);
167     if (*error != CL_SUCCESS || !mapped)
168     {
169         log_error( "ERROR: Unable to map image for writing: %s\n", IGetErrorString( *error ) );
170         return NULL;
171     }
172     size_t mappedSlicePad = mappedSlice - (mappedRow * height);
173 
174     // Copy the image.
175     size_t scanlineSize = imageInfo->rowPitch;
176     size_t sliceSize = imageInfo->slicePitch - scanlineSize * height;
177 
178     char* src = (char*)data;
179     char* dst = (char*)mapped;
180 
181     if ((mappedRow == scanlineSize) && ((mappedSlice == imageInfo->slicePitch) || (imageInfo->depth==0 && imageInfo->arraySize==0))) {
182         // Copy the whole image.
183         memcpy( dst, src, imageSize );
184     }
185     else {
186         // Else copy one scan line at a time.
187         size_t dstPitch2D = 0;
188         switch (imageInfo->type)
189         {
190             case CL_MEM_OBJECT_IMAGE3D:
191             case CL_MEM_OBJECT_IMAGE2D_ARRAY:
192             case CL_MEM_OBJECT_IMAGE2D:
193                 dstPitch2D = mappedRow;
194                 break;
195             case CL_MEM_OBJECT_IMAGE1D_ARRAY:
196             case CL_MEM_OBJECT_IMAGE1D:
197                 dstPitch2D = mappedSlice;
198                 break;
199         }
200 
201         for ( size_t z = 0; z < depth; z++ )
202         {
203             for ( size_t y = 0; y < height; y++ )
204             {
205                 memcpy( dst, src, imageInfo->width * get_pixel_size(imageInfo->format) );
206                 dst += dstPitch2D;
207                 src += scanlineSize;
208             }
209 
210             // mappedSlicePad is incorrect for 2D images here, but we will exit the z loop before this is a problem.
211             dst += mappedSlicePad;
212             src += sliceSize;
213         }
214     }
215 
216     // Unmap the image.
217     *error = clEnqueueUnmapMemObject(queue, img, mapped, 0, NULL, NULL);
218     if (*error != CL_SUCCESS)
219     {
220         log_error( "ERROR: Unable to unmap image after writing: %s\n", IGetErrorString( *error ) );
221         return NULL;
222     }
223 
224     return img;
225 }
226 
fill_region_with_value(image_descriptor * imageInfo,void * imageValues,void * value,const size_t origin[],const size_t region[])227 static void fill_region_with_value( image_descriptor *imageInfo, void *imageValues,
228     void *value, const size_t origin[], const size_t region[] )
229 {
230     size_t pixelSize = get_pixel_size( imageInfo->format );
231 
232     // Get initial pointer
233     char *destPtr   = (char *)imageValues + origin[ 2 ] * imageInfo->slicePitch
234         + origin[ 1 ] * imageInfo->rowPitch + pixelSize * origin[ 0 ];
235 
236     char *fillColor = (char *)malloc(pixelSize);
237     memcpy(fillColor, value, pixelSize);
238 
239     // Use pixel at origin to fill region.
240     for( size_t z = 0; z < ( region[ 2 ] > 0 ? region[ 2 ] : 1 ); z++ ) {
241         char *rowDestPtr = destPtr;
242         for( size_t y = 0; y < region[ 1 ]; y++ ) {
243             char *pixelDestPtr = rowDestPtr;
244 
245             for( size_t x = 0; x < region[ 0 ]; x++ ) {
246                 memcpy( pixelDestPtr, fillColor, pixelSize );
247                 pixelDestPtr += pixelSize;
248             }
249             rowDestPtr += imageInfo->rowPitch;
250         }
251         destPtr += imageInfo->slicePitch;
252     }
253 
254     free(fillColor);
255 }
256 
test_fill_image_generic(cl_context context,cl_command_queue queue,image_descriptor * imageInfo,const size_t origin[],const size_t region[],ExplicitType outputType,MTdata d)257 int test_fill_image_generic( cl_context context, cl_command_queue queue, image_descriptor *imageInfo,
258                              const size_t origin[], const size_t region[], ExplicitType outputType, MTdata d )
259 {
260     BufferOwningPtr<char> imgData;
261     BufferOwningPtr<char> imgHost;
262 
263     int error;
264     clMemWrapper image;
265 
266     if ( gDebugTrace )
267         log_info( " ++ Entering inner test loop...\n" );
268 
269     // Generate some data to test against
270     size_t dataBytes = 0;
271 
272     switch (imageInfo->type)
273     {
274         case CL_MEM_OBJECT_IMAGE1D:
275             dataBytes = imageInfo->rowPitch;
276             break;
277         case CL_MEM_OBJECT_IMAGE2D:
278             dataBytes = imageInfo->height * imageInfo->rowPitch;
279             break;
280         case CL_MEM_OBJECT_IMAGE3D:
281             dataBytes = imageInfo->depth * imageInfo->slicePitch;
282             break;
283         case CL_MEM_OBJECT_IMAGE1D_ARRAY:
284             dataBytes = imageInfo->arraySize * imageInfo->slicePitch;
285             break;
286         case CL_MEM_OBJECT_IMAGE2D_ARRAY:
287             dataBytes = imageInfo->arraySize * imageInfo->slicePitch;
288             break;
289     }
290 
291     if (dataBytes > imgData.getSize())
292     {
293         if ( gDebugTrace )
294             log_info( " - Resizing random image data...\n" );
295 
296         generate_random_image_data( imageInfo, imgData, d  );
297 
298         imgHost.reset( NULL ); // Free previously allocated memory first.
299         imgHost.reset(malloc(dataBytes),NULL,0,dataBytes);
300         if (imgHost == NULL)
301         {
302             log_error( "ERROR: Unable to malloc %lu bytes for imgHost\n", dataBytes );
303             return -1;
304         }
305     }
306 
307     // Reset the host verification copy of the data.
308     memcpy(imgHost, imgData, dataBytes);
309 
310     // Construct testing sources
311     if ( gDebugTrace )
312         log_info( " - Creating image...\n" );
313 
314     image = create_image( context, queue, imgData, imageInfo, &error );
315     if ( image == NULL )
316         return error;
317 
318     // Now fill the region defined by origin, region with the pixel value found at origin.
319     if ( gDebugTrace )
320         log_info( " - Filling at %d,%d,%d size %d,%d,%d\n", (int)origin[ 0 ], (int)origin[ 1 ], (int)origin[ 2 ],
321                  (int)region[ 0 ], (int)region[ 1 ], (int)region[ 2 ] );
322 
323     // We need to know the rounding mode, in the case of half to allow the
324     // pixel pack that generates the verification value to succeed.
325     if (imageInfo->format->image_channel_data_type == CL_HALF_FLOAT)
326         DetectFloatToHalfRoundingMode(queue);
327 
328     if( outputType == kFloat )
329     {
330         cl_float fillColor[ 4 ];
331         read_image_pixel_float( imgHost, imageInfo, origin[ 0 ], origin[ 1 ], origin[ 2 ], fillColor );
332         if ( gDebugTrace )
333             log_info( " - with value %g, %g, %g, %g\n", fillColor[ 0 ], fillColor[ 1 ], fillColor[ 2 ], fillColor[ 3 ] );
334         error = clEnqueueFillImage ( queue, image, fillColor, origin, region, 0, NULL, NULL );
335         if ( error != CL_SUCCESS )
336         {
337             log_error( "ERROR: Unable to fill image at %d,%d,%d size %d,%d,%d! (%s)\n",
338                       (int)origin[ 0 ], (int)origin[ 1 ], (int)origin[ 2 ],
339                       (int)region[ 0 ], (int)region[ 1 ], (int)region[ 2 ], IGetErrorString( error ) );
340             return error;
341         }
342 
343         // Write the approriate verification value to the correct region.
344         void* verificationValue = malloc(get_pixel_size(imageInfo->format));
345         pack_image_pixel(fillColor, imageInfo->format, verificationValue);
346         fill_region_with_value( imageInfo, imgHost, verificationValue, origin, region );
347         free(verificationValue);
348     }
349     else if( outputType == kInt )
350     {
351         cl_int fillColor[ 4 ];
352         read_image_pixel<cl_int>( imgHost, imageInfo, origin[ 0 ], origin[ 1 ], origin[ 2 ], fillColor );
353         if ( gDebugTrace )
354             log_info( " - with value %d, %d, %d, %d\n", fillColor[ 0 ], fillColor[ 1 ], fillColor[ 2 ], fillColor[ 3 ] );
355         error = clEnqueueFillImage ( queue, image, fillColor, origin, region, 0, NULL, NULL );
356         if ( error != CL_SUCCESS )
357         {
358             log_error( "ERROR: Unable to fill image at %d,%d,%d size %d,%d,%d! (%s)\n",
359                       (int)origin[ 0 ], (int)origin[ 1 ], (int)origin[ 2 ],
360                       (int)region[ 0 ], (int)region[ 1 ], (int)region[ 2 ], IGetErrorString( error ) );
361             return error;
362         }
363 
364         // Write the approriate verification value to the correct region.
365         void* verificationValue = malloc(get_pixel_size(imageInfo->format));
366         pack_image_pixel(fillColor, imageInfo->format, verificationValue);
367         fill_region_with_value( imageInfo, imgHost, verificationValue, origin, region );
368         free(verificationValue);
369     }
370     else // if( outputType == kUInt )
371     {
372         cl_uint fillColor[ 4 ];
373         read_image_pixel<cl_uint>( imgHost, imageInfo, origin[ 0 ], origin[ 1 ], origin[ 2 ], fillColor );
374         if ( gDebugTrace )
375             log_info( " - with value %u, %u, %u, %u\n", fillColor[ 0 ], fillColor[ 1 ], fillColor[ 2 ], fillColor[ 3 ] );
376         error = clEnqueueFillImage ( queue, image, fillColor, origin, region, 0, NULL, NULL );
377         if ( error != CL_SUCCESS )
378         {
379             log_error( "ERROR: Unable to fill image at %d,%d,%d size %d,%d,%d! (%s)\n",
380                       (int)origin[ 0 ], (int)origin[ 1 ], (int)origin[ 2 ],
381                       (int)region[ 0 ], (int)region[ 1 ], (int)region[ 2 ], IGetErrorString( error ) );
382             return error;
383         }
384 
385         // Write the approriate verification value to the correct region.
386         void* verificationValue = malloc(get_pixel_size(imageInfo->format));
387         pack_image_pixel(fillColor, imageInfo->format, verificationValue);
388         fill_region_with_value( imageInfo, imgHost, verificationValue, origin, region );
389         free(verificationValue);
390     }
391 
392     // Map the destination image to verify the results with the host
393     // copy. The contents of the entire buffer are compared.
394     if ( gDebugTrace )
395         log_info( " - Mapping results...\n" );
396 
397     size_t imageOrigin[ 3 ] = { 0, 0, 0 };
398     size_t imageRegion[ 3 ] = { imageInfo->width, 1, 1 };
399     switch (imageInfo->type)
400     {
401         case CL_MEM_OBJECT_IMAGE1D:
402             break;
403         case CL_MEM_OBJECT_IMAGE2D:
404             imageRegion[ 1 ] = imageInfo->height;
405             break;
406         case CL_MEM_OBJECT_IMAGE3D:
407             imageRegion[ 1 ] = imageInfo->height;
408             imageRegion[ 2 ] = imageInfo->depth;
409             break;
410         case CL_MEM_OBJECT_IMAGE1D_ARRAY:
411             imageRegion[ 1 ] = imageInfo->arraySize;
412             break;
413         case CL_MEM_OBJECT_IMAGE2D_ARRAY:
414             imageRegion[ 1 ] = imageInfo->height;
415             imageRegion[ 2 ] = imageInfo->arraySize;
416             break;
417     }
418 
419     size_t mappedRow, mappedSlice;
420     void* mapped = (char*)clEnqueueMapImage(queue, image, CL_TRUE, CL_MAP_READ, imageOrigin, imageRegion, &mappedRow, &mappedSlice, 0, NULL, NULL, &error);
421     if (error != CL_SUCCESS)
422     {
423         log_error( "ERROR: Unable to map image for verification: %s\n", IGetErrorString( error ) );
424         return -1;
425     }
426 
427     // Verify scanline by scanline, since the pitches are different
428     char *sourcePtr = imgHost;
429     char *destPtr = (char*)mapped;
430 
431     size_t scanlineSize = imageInfo->width * get_pixel_size( imageInfo->format );
432 
433     if ( gDebugTrace )
434         log_info( " - Scanline verification...\n" );
435 
436     size_t thirdDim = 1;
437     size_t secondDim = 1;
438 
439     switch (imageInfo->type) {
440       case CL_MEM_OBJECT_IMAGE1D:
441         secondDim = 1;
442         thirdDim = 1;
443         break;
444       case CL_MEM_OBJECT_IMAGE2D:
445         secondDim = imageInfo->height;
446         thirdDim = 1;
447         break;
448       case CL_MEM_OBJECT_IMAGE3D:
449         secondDim = imageInfo->height;
450         thirdDim = imageInfo->depth;
451         break;
452       case CL_MEM_OBJECT_IMAGE1D_ARRAY:
453         secondDim = imageInfo->arraySize;
454         thirdDim = 1;
455         break;
456       case CL_MEM_OBJECT_IMAGE2D_ARRAY:
457         secondDim = imageInfo->height;
458         thirdDim = imageInfo->arraySize;
459         break;
460       default:
461         log_error("Test error: unhandled image type at %s:%d\n",__FILE__,__LINE__);
462     };
463 
464     // Count the number of bytes successfully matched
465     size_t total_matched = 0;
466 
467     for ( size_t z = 0; z < thirdDim; z++ )
468     {
469         for ( size_t y = 0; y < secondDim; y++ )
470         {
471             // If the data type is 101010 ignore bits 31 and 32 when comparing the row
472             if (imageInfo->format->image_channel_data_type == CL_UNORM_INT_101010) {
473               for (size_t w=0;w!=scanlineSize/4;++w) {
474                 ((cl_uint*)sourcePtr)[w] &= 0x3FFFFFFF;
475                 ((cl_uint*)destPtr)[w] &= 0x3FFFFFFF;
476               }
477             }
478 
479             if (memcmp( sourcePtr, destPtr, scanlineSize ) != 0)
480             {
481                 // Find the first missing pixel
482                 size_t pixel_size = get_pixel_size( imageInfo->format );
483                 size_t where = 0;
484                 for ( where = 0; where < imageInfo->width; where++ )
485                     if ( memcmp( sourcePtr + pixel_size * where, destPtr + pixel_size * where, pixel_size) )
486                         break;
487 
488                 print_first_pixel_difference_error(
489                     where, sourcePtr + pixel_size * where,
490                     destPtr + pixel_size * where, imageInfo, y, thirdDim);
491                 return -1;
492             }
493 
494             total_matched += scanlineSize;
495             sourcePtr += imageInfo->rowPitch;
496             if((imageInfo->type == CL_MEM_OBJECT_IMAGE1D_ARRAY || imageInfo->type == CL_MEM_OBJECT_IMAGE1D))
497             destPtr += mappedSlice;
498             else
499             destPtr += mappedRow;
500         }
501 
502         sourcePtr += imageInfo->slicePitch - ( imageInfo->rowPitch * (imageInfo->height > 0 ? imageInfo->height : 1) );
503         destPtr += mappedSlice - ( mappedRow * (imageInfo->height > 0 ? imageInfo->height : 1) );
504     }
505 
506     // Unmap the image.
507     error = clEnqueueUnmapMemObject(queue, image, mapped, 0, NULL, NULL);
508     if (error != CL_SUCCESS)
509     {
510         log_error( "ERROR: Unable to unmap image after verify: %s\n", IGetErrorString( error ) );
511         return -1;
512     }
513 
514     imgHost.reset(0x0);
515     imgData.reset(0x0);
516 
517     size_t expected_bytes = scanlineSize * imageRegion[1] * imageRegion[2];
518     return (total_matched == expected_bytes) ? 0 : -1;
519 }
520