xref: /aosp_15_r20/external/OpenCL-CTS/test_conformance/basic/test_bufferreadwriterect.cpp (revision 6467f958c7de8070b317fc65bcb0f6472e388d82)
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 "harness/compat.h"
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 
24 #include "procs.h"
25 
26 #define CL_EXIT_ERROR(cmd,format,...)                \
27 {                                \
28 if ((cmd) != CL_SUCCESS) {                    \
29 log_error("CL ERROR: %s %u: ", __FILE__,__LINE__);    \
30 log_error(format,## __VA_ARGS__ );            \
31 log_error("\n");                        \
32 /*abort();*/                \
33 }                                \
34 }
35 
36 typedef unsigned char BufferType;
37 
38 // Globals for test
39 cl_command_queue queue;
40 
41 // Width and height of each pair of images.
42 enum { TotalImages = 8 };
43 size_t width  [TotalImages];
44 size_t height [TotalImages];
45 size_t depth  [TotalImages];
46 
47 // cl buffer and host buffer.
48 cl_mem buffer [TotalImages];
49 BufferType* verify[TotalImages];
50 BufferType* backing[TotalImages];
51 
52 // Temporary buffer used for read and write operations.
53 BufferType* tmp_buffer;
54 size_t tmp_buffer_size;
55 
56 size_t num_tries   = 50; // Number of randomly selected operations to perform.
57 size_t alloc_scale = 2;   // Scale term applied buffer allocation size.
58 MTdata mt;
59 
60 // Initialize a buffer in host memory containing random values of the specified size.
initialize_image(BufferType * ptr,size_t w,size_t h,size_t d,MTdata mt)61 static void initialize_image(BufferType* ptr, size_t w, size_t h, size_t d, MTdata mt)
62 {
63     enum { ElementSize = sizeof(BufferType)/sizeof(unsigned char) };
64 
65     unsigned char* buf = (unsigned char*)ptr;
66     size_t size = w*h*d*ElementSize;
67 
68     for (size_t i = 0; i != size; i++) {
69         buf[i] = (unsigned char)(genrand_int32(mt) % 0xff);
70     }
71 }
72 
73 // This function prints the contents of a buffer to standard error.
print_buffer(BufferType * buf,size_t w,size_t h,size_t d)74 void print_buffer(BufferType* buf, size_t w, size_t h, size_t d) {
75     log_error("Size = %lux%lux%lu (%lu total)\n",w,h,d,w*h*d);
76     for (unsigned k=0; k!=d;++k) {
77         log_error("Slice: %u\n",k);
78         for (unsigned j=0; j!=h;++j) {
79             for (unsigned i=0;i!=w;++i) {
80                 log_error("%02x",buf[k*(w*h)+j*w+i]);
81             }
82             log_error("\n");
83         }
84         log_error("\n");
85     }
86 }
87 
88 // Returns true if the two specified regions overlap.
check_overlap_rect(size_t src_offset[3],size_t dst_offset[3],size_t region[3],size_t row_pitch,size_t slice_pitch)89 bool check_overlap_rect(size_t src_offset[3],
90                         size_t dst_offset[3],
91                         size_t region[3],
92                         size_t row_pitch,
93                         size_t slice_pitch)
94 {
95     const size_t src_min[] = { src_offset[0], src_offset[1], src_offset[2] };
96     const size_t src_max[] = { src_offset[0] + region[0], src_offset[1] + region[1], src_offset[2] + region[2] };
97 
98     const size_t dst_min[] = { dst_offset[0], dst_offset[1], dst_offset[2] };
99     const size_t dst_max[] = { dst_offset[0] + region[0],
100                                dst_offset[1] + region[1],
101                                dst_offset[2] + region[2]};
102 // Check for overlap
103         bool overlap = true;
104         unsigned i;
105         for (i = 0; i != 3; ++i)
106         {
107             overlap = overlap && (src_min[i] < dst_max[i]) && (src_max[i] > dst_min[i]);
108         }
109 
110     size_t dst_start = dst_offset[2] * slice_pitch + dst_offset[1] * row_pitch + dst_offset[0];
111     size_t dst_end = dst_start + (region[2] * slice_pitch +
112                                   region[1] * row_pitch + region[0]);
113     size_t src_start = src_offset[2] * slice_pitch + src_offset[1] * row_pitch + src_offset[0];
114     size_t src_end = src_start + (region[2] * slice_pitch +
115                                   region[1] * row_pitch + region[0]);
116     if (!overlap) {
117         size_t delta_src_x = (src_offset[0] + region[0] > row_pitch) ?
118             src_offset[0] + region[0] - row_pitch : 0; size_t delta_dst_x = (dst_offset[0] + region[0] > row_pitch) ?
119             dst_offset[0] + region[0] - row_pitch : 0;
120         if ((delta_src_x > 0 && delta_src_x > dst_offset[0]) ||
121             (delta_dst_x > 0 && delta_dst_x > src_offset[0])) {
122             if ((src_start <= dst_start && dst_start < src_end) || (dst_start <= src_start && src_start < dst_end)) overlap = true;
123         }
124         if (region[2] > 1) {
125             size_t src_height = slice_pitch / row_pitch; size_t dst_height = slice_pitch / row_pitch;
126             size_t delta_src_y = (src_offset[1] + region[1] > src_height) ? src_offset[1] + region[1] - src_height : 0;
127             size_t delta_dst_y = (dst_offset[1] + region[1] > dst_height) ? dst_offset[1] + region[1] - dst_height : 0;
128             if ((delta_src_y > 0 && delta_src_y > dst_offset[1]) ||
129                 (delta_dst_y > 0 && delta_dst_y > src_offset[1])) {
130                 if ((src_start <= dst_start && dst_start < src_end) || (dst_start <= src_start && src_start < dst_end))
131                     overlap = true;
132             }
133         }
134     }
135     return overlap;
136 }
137 
138 
139 
140 // This function invokes the CopyBufferRect CL command and then mirrors the operation on the host side verify buffers.
copy_region(size_t src,size_t soffset[3],size_t sregion[3],size_t dst,size_t doffset[3],size_t dregion[3])141 int copy_region(size_t src, size_t soffset[3], size_t sregion[3], size_t dst, size_t doffset[3], size_t dregion[3]) {
142 
143     // Copy between cl buffers.
144     size_t src_slice_pitch = (width[src]*height[src] != 1) ? width[src]*height[src] : 0;
145     size_t dst_slice_pitch = (width[dst]*height[dst] != 1) ? width[dst]*height[dst] : 0;
146     size_t src_row_pitch = width[src];
147 
148     cl_int err;
149     if (check_overlap_rect(soffset,doffset,sregion,src_row_pitch, src_slice_pitch)) {
150         log_info( "Copy overlap reported, skipping copy buffer rect\n" );
151         return CL_SUCCESS;
152     } else {
153         if ((err = clEnqueueCopyBufferRect(queue,
154                                          buffer[src],buffer[dst],
155                                          soffset, doffset,
156                                          sregion,/*dregion,*/
157                                          width[src], src_slice_pitch,
158                                          width[dst], dst_slice_pitch,
159                                          0, NULL, NULL)) != CL_SUCCESS)
160         {
161             CL_EXIT_ERROR(err, "clEnqueueCopyBufferRect failed between %u and %u",(unsigned)src,(unsigned)dst);
162         }
163     }
164 
165     // Copy between host buffers.
166     size_t total = sregion[0] * sregion[1] * sregion[2];
167 
168     size_t spitch = width[src];
169     size_t sslice = width[src]*height[src];
170 
171     size_t dpitch = width[dst];
172     size_t dslice = width[dst]*height[dst];
173 
174     for (size_t i = 0; i != total; ++i) {
175 
176         // Compute the coordinates of the element within the source and destination regions.
177         size_t rslice = sregion[0]*sregion[1];
178         size_t sz = i / rslice;
179         size_t sy = (i % rslice) / sregion[0];
180         size_t sx = (i % rslice) % sregion[0];
181 
182         size_t dz = sz;
183         size_t dy = sy;
184         size_t dx = sx;
185 
186         // Compute the offset in bytes of the source and destination.
187         size_t s_idx = (soffset[2]+sz)*sslice + (soffset[1]+sy)*spitch + soffset[0]+sx;
188         size_t d_idx = (doffset[2]+dz)*dslice + (doffset[1]+dy)*dpitch + doffset[0]+dx;
189 
190         verify[dst][d_idx] = verify[src][s_idx];
191     }
192 
193     return 0;
194 }
195 
196 // This function compares the destination region in the buffer pointed
197 // to by device, to the source region of the specified verify buffer.
verify_region(BufferType * device,size_t src,size_t soffset[3],size_t sregion[3],size_t dst,size_t doffset[3])198 int verify_region(BufferType* device, size_t src, size_t soffset[3], size_t sregion[3], size_t dst, size_t doffset[3]) {
199 
200     // Copy between host buffers.
201     size_t spitch = width[src];
202     size_t sslice = width[src]*height[src];
203 
204     size_t dpitch = width[dst];
205     size_t dslice = width[dst]*height[dst];
206 
207     size_t total = sregion[0] * sregion[1] * sregion[2];
208     for (size_t i = 0; i != total; ++i) {
209 
210         // Compute the coordinates of the element within the source and destination regions.
211         size_t rslice = sregion[0]*sregion[1];
212         size_t sz = i / rslice;
213         size_t sy = (i % rslice) / sregion[0];
214         size_t sx = (i % rslice) % sregion[0];
215 
216         // Compute the offset in bytes of the source and destination.
217         size_t s_idx = (soffset[2]+sz)*sslice + (soffset[1]+sy)*spitch + soffset[0]+sx;
218         size_t d_idx = (doffset[2]+sz)*dslice + (doffset[1]+sy)*dpitch + doffset[0]+sx;
219 
220         if (device[d_idx] != verify[src][s_idx]) {
221             log_error("Verify failed on comparsion %lu: coordinate (%lu, %lu, %lu) of region\n",i,sx,sy,sz);
222             log_error("0x%02x != 0x%02x\n", device[d_idx], verify[src][s_idx]);
223 #if 0
224             // Uncomment this section to print buffers.
225             log_error("Device (copy): [%lu]\n",dst);
226             print_buffer(device,width[dst],height[dst],depth[dst]);
227             log_error("\n");
228             log_error("Verify: [%lu]\n",src);
229             print_buffer(verify[src],width[src],height[src],depth[src]);
230             log_error("\n");
231             abort();
232 #endif
233             return -1;
234         }
235     }
236 
237     return 0;
238 }
239 
240 
241 // This function invokes ReadBufferRect to read a region from the
242 // specified source buffer into a temporary destination buffer. The
243 // contents of the temporary buffer are then compared to the source
244 // region of the corresponding verify buffer.
read_verify_region(size_t src,size_t soffset[3],size_t sregion[3],size_t dst,size_t doffset[3],size_t dregion[3])245 int read_verify_region(size_t src, size_t soffset[3], size_t sregion[3], size_t dst, size_t doffset[3], size_t dregion[3]) {
246 
247     // Clear the temporary destination host buffer.
248     memset(tmp_buffer, 0xff, tmp_buffer_size);
249 
250     size_t src_slice_pitch = (width[src]*height[src] != 1) ? width[src]*height[src] : 0;
251     size_t dst_slice_pitch = (width[dst]*height[dst] != 1) ? width[dst]*height[dst] : 0;
252 
253     // Copy the source region of the cl buffer, to the destination region of the temporary buffer.
254     CL_EXIT_ERROR(clEnqueueReadBufferRect(queue,
255                                           buffer[src],
256                                           CL_TRUE,
257                                           soffset,doffset,
258                                           sregion,
259                                           width[src], src_slice_pitch,
260                                           width[dst], dst_slice_pitch,
261                                           tmp_buffer,
262                                           0, NULL, NULL), "clEnqueueCopyBufferRect failed between %u and %u",(unsigned)src,(unsigned)dst);
263 
264     return verify_region(tmp_buffer,src,soffset,sregion,dst,doffset);
265 }
266 
267 // This function performs the same verification check as
268 // read_verify_region, except a MapBuffer command is used to access the
269 // device buffer data instead of a ReadBufferRect, and the whole
270 // buffer is checked.
map_verify_region(size_t src)271 int map_verify_region(size_t src) {
272 
273     size_t size_bytes = width[src]*height[src]*depth[src]*sizeof(BufferType);
274 
275     // Copy the source region of the cl buffer, to the destination region of the temporary buffer.
276     cl_int err;
277     BufferType* mapped = (BufferType*)clEnqueueMapBuffer(queue,buffer[src],CL_TRUE,CL_MAP_READ,0,size_bytes,0,NULL,NULL,&err);
278     CL_EXIT_ERROR(err, "clEnqueueMapBuffer failed for buffer %u",(unsigned)src);
279 
280     size_t soffset[] = { 0, 0, 0 };
281     size_t sregion[] = { width[src], height[src], depth[src] };
282 
283     int ret = verify_region(mapped,src,soffset,sregion,src,soffset);
284 
285     CL_EXIT_ERROR(clEnqueueUnmapMemObject(queue,buffer[src],mapped,0,NULL,NULL),
286                   "clEnqueueUnmapMemObject failed for buffer %u",(unsigned)src);
287 
288     return ret;
289 }
290 
291 // This function generates a new temporary buffer and then writes a
292 // region of it to a region in the specified destination buffer.
write_region(size_t src,size_t soffset[3],size_t sregion[3],size_t dst,size_t doffset[3],size_t dregion[3])293 int write_region(size_t src, size_t soffset[3], size_t sregion[3], size_t dst, size_t doffset[3], size_t dregion[3]) {
294 
295     initialize_image(tmp_buffer, tmp_buffer_size, 1, 1, mt);
296     // memset(tmp_buffer, 0xf0, tmp_buffer_size);
297 
298     size_t src_slice_pitch = (width[src]*height[src] != 1) ? width[src]*height[src] : 0;
299     size_t dst_slice_pitch = (width[dst]*height[dst] != 1) ? width[dst]*height[dst] : 0;
300 
301     // Copy the source region of the cl buffer, to the destination region of the temporary buffer.
302     CL_EXIT_ERROR(clEnqueueWriteBufferRect(queue,
303                                            buffer[dst],
304                                            CL_TRUE,
305                                            doffset,soffset,
306     /*sregion,*/dregion,
307                                            width[dst], dst_slice_pitch,
308                                            width[src], src_slice_pitch,
309                                            tmp_buffer,
310                                            0, NULL, NULL), "clEnqueueWriteBufferRect failed between %u and %u",(unsigned)src,(unsigned)dst);
311 
312     // Copy from the temporary buffer to the host buffer.
313     size_t spitch = width[src];
314     size_t sslice = width[src]*height[src];
315     size_t dpitch = width[dst];
316     size_t dslice = width[dst]*height[dst];
317 
318     size_t total = sregion[0] * sregion[1] * sregion[2];
319     for (size_t i = 0; i != total; ++i) {
320 
321         // Compute the coordinates of the element within the source and destination regions.
322         size_t rslice = sregion[0]*sregion[1];
323         size_t sz = i / rslice;
324         size_t sy = (i % rslice) / sregion[0];
325         size_t sx = (i % rslice) % sregion[0];
326 
327         size_t dz = sz;
328         size_t dy = sy;
329         size_t dx = sx;
330 
331         // Compute the offset in bytes of the source and destination.
332         size_t s_idx = (soffset[2]+sz)*sslice + (soffset[1]+sy)*spitch + soffset[0]+sx;
333         size_t d_idx = (doffset[2]+dz)*dslice + (doffset[1]+dy)*dpitch + doffset[0]+dx;
334 
335         verify[dst][d_idx] = tmp_buffer[s_idx];
336     }
337     return 0;
338 }
339 
mem_obj_destructor_callback(cl_mem,void * data)340 void CL_CALLBACK mem_obj_destructor_callback( cl_mem, void *data )
341 {
342     free( data );
343 }
344 
345 // This is the main test function for the conformance test.
346 int
test_bufferreadwriterect(cl_device_id device,cl_context context,cl_command_queue queue_,int num_elements)347 test_bufferreadwriterect(cl_device_id device, cl_context context, cl_command_queue queue_, int num_elements)
348 {
349     queue = queue_;
350     cl_int err;
351 
352     // Initialize the random number generator.
353     mt = init_genrand( gRandomSeed );
354 
355     // Compute a maximum buffer size based on the number of test images and the device maximum.
356     cl_ulong max_mem_alloc_size = 0;
357     CL_EXIT_ERROR(clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_mem_alloc_size, NULL),"Could not get device info");
358     log_info("CL_DEVICE_MAX_MEM_ALLOC_SIZE = %llu bytes.\n", max_mem_alloc_size);
359 
360     // Confirm that the maximum allocation size is not zero.
361     if (max_mem_alloc_size == 0) {
362         log_error("Error: CL_DEVICE_MAX_MEM_ALLOC_SIZE is zero bytes\n");
363         return -1;
364     }
365 
366     // Guess at a reasonable maximum dimension.
367     size_t max_mem_alloc_dim = (size_t)cbrt((double)(max_mem_alloc_size/sizeof(BufferType)))/alloc_scale;
368     if (max_mem_alloc_dim == 0) {
369         max_mem_alloc_dim = max_mem_alloc_size;
370     }
371 
372     log_info("Using maximum dimension      = %lu.\n", max_mem_alloc_dim);
373 
374     // Create pairs of cl buffers and host buffers on which operations will be mirrored.
375     log_info("Creating %u pairs of random sized host and cl buffers.\n", TotalImages);
376 
377     size_t max_size = 0;
378     size_t total_bytes = 0;
379 
380     for (unsigned i=0; i != TotalImages; ++i) {
381 
382         // Determine a width and height for this buffer.
383         size_t size_bytes;
384         size_t tries = 0;
385         size_t max_tries = 1048576;
386         do {
387             width[i]   = get_random_size_t(1, max_mem_alloc_dim, mt);
388             height[i]  = get_random_size_t(1, max_mem_alloc_dim, mt);
389             depth[i]   = get_random_size_t(1, max_mem_alloc_dim, mt);
390             ++tries;
391         } while ((tries < max_tries) && (size_bytes = width[i]*height[i]*depth[i]*sizeof(BufferType)) > max_mem_alloc_size);
392 
393         // Check to see if adequately sized buffers were found.
394         if (tries >= max_tries) {
395             log_error("Error: Could not find random buffer sized less than %llu bytes in %lu tries.\n",
396                       max_mem_alloc_size, max_tries);
397             return -1;
398         }
399 
400         // Keep track of the dimensions of the largest buffer.
401         max_size = (size_bytes > max_size) ? size_bytes : max_size;
402         total_bytes += size_bytes;
403 
404         log_info("Buffer[%u] is (%lu,%lu,%lu) = %lu MB (truncated)\n",i,width[i],height[i],depth[i],(size_bytes)/1048576);
405     }
406 
407     log_info( "Total size: %lu MB (truncated)\n", total_bytes/1048576 );
408 
409     // Allocate a temporary buffer for read and write operations.
410     tmp_buffer_size  = max_size;
411     tmp_buffer = (BufferType*)malloc(tmp_buffer_size);
412 
413     // Initialize cl buffers
414     log_info( "Initializing buffers\n" );
415     for (unsigned i=0; i != TotalImages; ++i) {
416 
417         size_t size_bytes = width[i]*height[i]*depth[i]*sizeof(BufferType);
418 
419         // Allocate a host copy of the buffer for verification.
420         verify[i] = (BufferType*)malloc(size_bytes);
421         CL_EXIT_ERROR(verify[i] ? CL_SUCCESS : -1, "malloc of host buffer failed for buffer %u", i);
422 
423         // Allocate the buffer in host memory.
424         backing[i] = (BufferType*)malloc(size_bytes);
425         CL_EXIT_ERROR(backing[i] ? CL_SUCCESS : -1, "malloc of backing buffer failed for buffer %u", i);
426 
427         // Generate a random buffer.
428         log_info( "Initializing buffer %u\n", i );
429         initialize_image(verify[i], width[i], height[i], depth[i], mt);
430 
431         // Copy the image into a buffer which will passed to CL.
432         memcpy(backing[i], verify[i], size_bytes);
433 
434         // Create the CL buffer.
435         buffer[i] = clCreateBuffer (context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE, size_bytes, backing[i], &err);
436         CL_EXIT_ERROR(err,"clCreateBuffer failed for buffer %u", i);
437 
438         // Make sure buffer is cleaned up appropriately if we encounter an error in the rest of the calls.
439         err = clSetMemObjectDestructorCallback( buffer[i], mem_obj_destructor_callback, backing[i] );
440         CL_EXIT_ERROR(err, "Unable to set mem object destructor callback" );
441     }
442 
443     // Main test loop, run num_tries times.
444     log_info( "Executing %u test operations selected at random.\n", (unsigned)num_tries );
445     for (size_t iter = 0; iter < num_tries; ++iter) {
446 
447         // Determine a source and a destination.
448         size_t src = get_random_size_t(0,TotalImages,mt);
449         size_t dst = get_random_size_t(0,TotalImages,mt);
450 
451         // Determine the minimum dimensions.
452         size_t min_width = width[src] < width[dst] ? width[src] : width[dst];
453         size_t min_height = height[src] < height[dst] ? height[src] : height[dst];
454         size_t min_depth = depth[src] < depth[dst] ? depth[src] : depth[dst];
455 
456         // Generate a random source rectangle within the minimum dimensions.
457         size_t mx = get_random_size_t(0, min_width-1, mt);
458         size_t my = get_random_size_t(0, min_height-1, mt);
459         size_t mz = get_random_size_t(0, min_depth-1, mt);
460 
461         size_t sw = get_random_size_t(1, (min_width - mx), mt);
462         size_t sh = get_random_size_t(1, (min_height - my), mt);
463         size_t sd = get_random_size_t(1, (min_depth - mz), mt);
464 
465         size_t sx = get_random_size_t(0, width[src]-sw, mt);
466         size_t sy = get_random_size_t(0, height[src]-sh, mt);
467         size_t sz = get_random_size_t(0, depth[src]-sd, mt);
468 
469         size_t soffset[] = { sx, sy, sz };
470         size_t sregion[] = { sw, sh, sd };
471 
472         // Generate a destination rectangle of the same size.
473         size_t dw = sw;
474         size_t dh = sh;
475         size_t dd = sd;
476 
477         // Generate a random destination offset within the buffer.
478         size_t dx = get_random_size_t(0, (width[dst] - dw), mt);
479         size_t dy = get_random_size_t(0, (height[dst] - dh), mt);
480         size_t dz = get_random_size_t(0, (depth[dst] - dd), mt);
481         size_t doffset[] = { dx, dy, dz };
482         size_t dregion[] = { dw, dh, dd };
483 
484         // Execute one of three operations:
485         // - Copy: Copies between src and dst within each set of host, buffer, and images.
486         // - Read & verify: Reads src region from buffer and image, and compares to host.
487         // - Write: Generates new buffer with src dimensions, and writes to cl buffer and image.
488 
489         enum { TotalOperations = 3 };
490         size_t operation = get_random_size_t(0,TotalOperations,mt);
491 
492         switch (operation) {
493             case 0:
494                 log_info("%lu Copy %lu offset (%lu,%lu,%lu) -> %lu offset (%lu,%lu,%lu) region (%lux%lux%lu = %lu)\n",
495                          iter,
496                          src, soffset[0], soffset[1], soffset[2],
497                          dst, doffset[0], doffset[1], doffset[2],
498                          sregion[0], sregion[1], sregion[2],
499                          sregion[0]*sregion[1]*sregion[2]);
500                 if ((err = copy_region(src, soffset, sregion, dst, doffset, dregion)))
501                     return err;
502                 break;
503             case 1:
504                 log_info("%lu Read %lu offset (%lu,%lu,%lu) -> %lu offset (%lu,%lu,%lu) region (%lux%lux%lu = %lu)\n",
505                          iter,
506                          src, soffset[0], soffset[1], soffset[2],
507                          dst, doffset[0], doffset[1], doffset[2],
508                          sregion[0], sregion[1], sregion[2],
509                          sregion[0]*sregion[1]*sregion[2]);
510                 if ((err = read_verify_region(src, soffset, sregion, dst, doffset, dregion)))
511                     return err;
512                 break;
513             case 2:
514                 log_info("%lu Write %lu offset (%lu,%lu,%lu) -> %lu offset (%lu,%lu,%lu) region (%lux%lux%lu = %lu)\n",
515                          iter,
516                          src, soffset[0], soffset[1], soffset[2],
517                          dst, doffset[0], doffset[1], doffset[2],
518                          sregion[0], sregion[1], sregion[2],
519                          sregion[0]*sregion[1]*sregion[2]);
520                 if ((err = write_region(src, soffset, sregion, dst, doffset, dregion)))
521                     return err;
522                 break;
523         }
524 
525 #if 0
526         // Uncomment this section to verify each operation.
527         // If commented out, verification won't occur until the end of the
528         // test, and it will not be possible to determine which operation failed.
529         log_info("Verify src %lu offset (%u,%u,%u) region (%lux%lux%lu)\n", src, 0, 0, 0, width[src], height[src], depth[src]);
530         if (err = map_verify_region(src))
531             return err;
532 
533         log_info("Verify dst %lu offset (%u,%u,%u) region (%lux%lux%lu)\n", dst, 0, 0, 0, width[dst], height[dst], depth[dst]);
534         if (err = map_verify_region(dst))
535             return err;
536 
537 
538 #endif
539 
540     } // end main for loop.
541 
542     for (unsigned i=0;i<TotalImages;++i) {
543         log_info("Verify %u offset (%u,%u,%u) region (%lux%lux%lu)\n", i, 0, 0, 0, width[i], height[i], depth[i]);
544         if ((err = map_verify_region(i)))
545             return err;
546     }
547 
548     // Clean-up.
549     free_mtdata(mt);
550     for (unsigned i=0;i<TotalImages;++i) {
551         free( verify[i] );
552         clReleaseMemObject( buffer[i] );
553     }
554     free( tmp_buffer );
555 
556     if (!err) {
557         log_info("RECT read, write test passed\n");
558     }
559 
560     return err;
561 }
562 
563 
564 
565