| // |
| // Copyright (c) 2017 The Khronos Group Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| #include "allocation_functions.h" |
| #include "allocation_fill.h" |
| |
| |
| static cl_image_format image_format = { CL_RGBA, CL_UNSIGNED_INT32 }; |
| |
| int allocate_buffer(cl_context context, cl_command_queue *queue, cl_device_id device_id, cl_mem *mem, size_t size_to_allocate, cl_bool blocking_write) { |
| int error; |
| // log_info("\t\tAttempting to allocate a %gMB array and fill with %s writes.\n", (size_to_allocate/(1024.0*1024.0)), (blocking_write ? "blocking" : "non-blocking")); |
| *mem = clCreateBuffer(context, CL_MEM_READ_WRITE, size_to_allocate, NULL, &error); |
| return check_allocation_error(context, device_id, error, queue); |
| } |
| |
| |
| int find_good_image_size(cl_device_id device_id, size_t size_to_allocate, size_t *width, size_t *height, size_t* max_size) { |
| size_t max_width, max_height, num_pixels, found_width, found_height; |
| int error; |
| |
| if (checkForImageSupport(device_id)) { |
| log_info("Can not allocate an image on this device because it does not support images."); |
| return FAILED_ABORT; |
| } |
| |
| if (size_to_allocate == 0) { |
| log_error("Trying to allcoate a zero sized image.\n"); |
| return FAILED_ABORT; |
| } |
| |
| error = clGetDeviceInfo( device_id, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof( max_width ), &max_width, NULL ); |
| test_error_abort(error, "clGetDeviceInfo failed."); |
| error = clGetDeviceInfo( device_id, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof( max_height ), &max_height, NULL ); |
| test_error_abort(error, "clGetDeviceInfo failed."); |
| |
| num_pixels = size_to_allocate / (sizeof(cl_uint)*4); |
| |
| // Use a 64-bit variable to avoid overflow in 32-bit architectures |
| long long unsigned max_pixels = (long long unsigned)max_width * max_height; |
| |
| if (num_pixels > max_pixels) { |
| if(NULL != max_size) { |
| *max_size = max_width * max_height * sizeof(cl_uint) * 4; |
| } |
| return FAILED_TOO_BIG; |
| } |
| |
| // We want a close-to-square aspect ratio. |
| // Note that this implicitly assumes that max width >= max height |
| found_width = (int)sqrt( (double) num_pixels ); |
| if( found_width > max_width ) { |
| found_width = max_width; |
| } |
| if (found_width == 0) |
| found_width = 1; |
| |
| found_height = (size_t)num_pixels/found_width; |
| if (found_height > max_height) { |
| found_height = max_height; |
| } |
| if (found_height == 0) |
| found_height = 1; |
| |
| *width = found_width; |
| *height = found_height; |
| |
| if(NULL != max_size) { |
| *max_size = found_width * found_height * sizeof(cl_uint) * 4; |
| } |
| |
| return SUCCEEDED; |
| } |
| |
| |
| int allocate_image2d_read(cl_context context, cl_command_queue *queue, cl_device_id device_id, cl_mem *mem, size_t size_to_allocate, cl_bool blocking_write) { |
| size_t width, height; |
| int error; |
| |
| error = find_good_image_size(device_id, size_to_allocate, &width, &height, NULL); |
| if (error != SUCCEEDED) |
| return error; |
| |
| log_info("\t\tAttempting to allocate a %gMB read-only image (%d x %d) and fill with %s writes.\n", |
| (size_to_allocate/(1024.0*1024.0)), (int)width, (int)height, (blocking_write ? "blocking" : "non-blocking")); |
| *mem = create_image_2d(context, CL_MEM_READ_ONLY, &image_format, width, height, 0, NULL, &error); |
| |
| return check_allocation_error(context, device_id, error, queue); |
| } |
| |
| |
| int allocate_image2d_write(cl_context context, cl_command_queue *queue, cl_device_id device_id, cl_mem *mem, size_t size_to_allocate, cl_bool blocking_write) { |
| size_t width, height; |
| int error; |
| |
| error = find_good_image_size(device_id, size_to_allocate, &width, &height, NULL); |
| if (error != SUCCEEDED) |
| return error; |
| |
| //log_info("\t\tAttempting to allocate a %gMB write-only image (%d x %d) and fill with %s writes.\n", |
| //(size_to_allocate/(1024.0*1024.0)), (int)width, (int)height, (blocking_write ? "blocking" : "non-blocking")); |
| *mem = create_image_2d(context, CL_MEM_WRITE_ONLY, &image_format, width, height, 0, NULL, &error); |
| |
| return check_allocation_error(context, device_id, error, queue); |
| } |
| |
| int do_allocation(cl_context context, cl_command_queue *queue, cl_device_id device_id, size_t size_to_allocate, int type, cl_mem *mem) { |
| if (type == BUFFER) return allocate_buffer(context, queue, device_id, mem, size_to_allocate, true); |
| if (type == IMAGE_READ) return allocate_image2d_read(context, queue, device_id, mem, size_to_allocate, true); |
| if (type == IMAGE_WRITE) return allocate_image2d_write(context, queue, device_id, mem, size_to_allocate, true); |
| if (type == BUFFER_NON_BLOCKING) return allocate_buffer(context, queue, device_id, mem, size_to_allocate, false); |
| if (type == IMAGE_READ_NON_BLOCKING) return allocate_image2d_read(context, queue, device_id, mem, size_to_allocate, false); |
| if (type == IMAGE_WRITE_NON_BLOCKING) return allocate_image2d_write(context, queue, device_id, mem, size_to_allocate, false); |
| log_error("Invalid allocation type: %d\n", type); |
| return FAILED_ABORT; |
| } |
| |
| |
| int allocate_size(cl_context context, cl_command_queue *queue, cl_device_id device_id, int multiple_allocations, size_t size_to_allocate, |
| int type, cl_mem mems[], int *number_of_mems, size_t *final_size, int force_fill, MTdata d) { |
| |
| cl_ulong max_individual_allocation_size, global_mem_size; |
| int error, result; |
| size_t amount_allocated; |
| size_t reduction_amount; |
| int current_allocation; |
| size_t allocation_this_time, actual_allocation; |
| |
| // Set the number of mems used to 0 so if we fail to create even a single one we don't end up returning a garbage value |
| *number_of_mems = 0; |
| |
| error = clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(max_individual_allocation_size), &max_individual_allocation_size, NULL); |
| test_error_abort( error, "clGetDeviceInfo failed for CL_DEVICE_MAX_MEM_ALLOC_SIZE"); |
| error = clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(global_mem_size), &global_mem_size, NULL); |
| test_error_abort( error, "clGetDeviceInfo failed for CL_DEVICE_GLOBAL_MEM_SIZE"); |
| |
| if (global_mem_size > (cl_ulong)SIZE_MAX) { |
| global_mem_size = (cl_ulong)SIZE_MAX; |
| } |
| |
| // log_info("Device reports CL_DEVICE_MAX_MEM_ALLOC_SIZE=%llu bytes (%gMB), CL_DEVICE_GLOBAL_MEM_SIZE=%llu bytes (%gMB).\n", |
| // max_individual_allocation_size, toMB(max_individual_allocation_size), |
| // global_mem_size, toMB(global_mem_size)); |
| |
| if (size_to_allocate > global_mem_size) { |
| log_error("Can not allocate more than the global memory size.\n"); |
| return FAILED_ABORT; |
| } |
| |
| amount_allocated = 0; |
| current_allocation = 0; |
| |
| // If allocating for images, reduce the maximum allocation size to the maximum image size. |
| // If we don't do this, then the value of CL_DEVICE_MAX_MEM_ALLOC_SIZE / 4 can be higher |
| // than the maximum image size on systems with 16GB or RAM or more. In this case, we |
| // succeed in allocating an image but its size is less than CL_DEVICE_MAX_MEM_ALLOC_SIZE / 4 |
| // (min_allocation_allowed) and thus we fail the allocation below. |
| if(type == IMAGE_READ || type == IMAGE_READ_NON_BLOCKING || type == IMAGE_WRITE || type == IMAGE_WRITE_NON_BLOCKING) { |
| size_t width; |
| size_t height; |
| size_t max_size; |
| error = find_good_image_size(device_id, size_to_allocate, &width, &height, &max_size); |
| if (!(error == SUCCEEDED || error == FAILED_TOO_BIG)) |
| return error; |
| if(max_size < max_individual_allocation_size) |
| max_individual_allocation_size = max_size; |
| } |
| |
| reduction_amount = (size_t)max_individual_allocation_size/16; |
| |
| if (type == BUFFER || type == BUFFER_NON_BLOCKING) log_info("\tAttempting to allocate a buffer of size %gMB.\n", toMB(size_to_allocate)); |
| else if (type == IMAGE_READ || type == IMAGE_READ_NON_BLOCKING) log_info("\tAttempting to allocate a read-only image of size %gMB.\n", toMB(size_to_allocate)); |
| else if (type == IMAGE_WRITE || type == IMAGE_WRITE_NON_BLOCKING) log_info("\tAttempting to allocate a write-only image of size %gMB.\n", toMB(size_to_allocate)); |
| |
| // log_info("\t\t(Reduction size is %gMB per iteration, minimum allowable individual allocation size is %gMB.)\n", |
| // toMB(reduction_amount), toMB(min_allocation_allowed)); |
| // if (force_fill && type != IMAGE_WRITE && type != IMAGE_WRITE_NON_BLOCKING) log_info("\t\t(Allocations will be filled with random data for checksum calculation.)\n"); |
| |
| // If we are only doing a single allocation, only allow 1 |
| int max_to_allocate = multiple_allocations ? MAX_NUMBER_TO_ALLOCATE : 1; |
| |
| // Make sure that the maximum number of images allocated is constrained by the |
| // maximum that may be passed to a kernel |
| if (type != BUFFER && type != BUFFER_NON_BLOCKING) { |
| cl_device_info param_name = (type == IMAGE_READ || type == IMAGE_READ_NON_BLOCKING) ? |
| CL_DEVICE_MAX_READ_IMAGE_ARGS : CL_DEVICE_MAX_WRITE_IMAGE_ARGS; |
| |
| cl_uint max_image_args; |
| error = clGetDeviceInfo(device_id, param_name, sizeof(max_image_args), &max_image_args, NULL); |
| test_error( error, "clGetDeviceInfo failed for CL_DEVICE_MAX IMAGE_ARGS"); |
| |
| if ((int)max_image_args < max_to_allocate) { |
| log_info("\t\tMaximum number of images per kernel limited to %d\n",(int)max_image_args); |
| max_to_allocate = max_image_args; |
| } |
| } |
| |
| |
| // Try to allocate the requested amount. |
| while (amount_allocated != size_to_allocate && current_allocation < max_to_allocate) { |
| |
| // Determine how much more is needed |
| allocation_this_time = size_to_allocate - amount_allocated; |
| |
| // Bound by the individual allocation size |
| if (allocation_this_time > max_individual_allocation_size) |
| allocation_this_time = (size_t)max_individual_allocation_size; |
| |
| // Allocate the largest object possible |
| result = FAILED_TOO_BIG; |
| //log_info("\t\tTrying sub-allocation %d at size %gMB.\n", current_allocation, toMB(allocation_this_time)); |
| while (result == FAILED_TOO_BIG && allocation_this_time != 0) { |
| |
| // Create the object |
| result = do_allocation(context, queue, device_id, allocation_this_time, type, &mems[current_allocation]); |
| if (result == SUCCEEDED) { |
| // Allocation succeeded, another memory object was added to the array |
| *number_of_mems = (current_allocation+1); |
| |
| // Verify the size is correct to within 1MB. |
| actual_allocation = get_actual_allocation_size(mems[current_allocation]); |
| if (fabs((double)allocation_this_time - (double)actual_allocation) > 1024.0*1024.0) { |
| log_error("Allocation not of expected size. Expected %gMB, got %gMB.\n", toMB(allocation_this_time), toMB( actual_allocation)); |
| return FAILED_ABORT; |
| } |
| |
| // If we are filling the allocation for verification do so |
| if (force_fill) { |
| //log_info("\t\t\tWriting random values to object and calculating checksum.\n"); |
| cl_bool blocking_write = true; |
| if (type == BUFFER_NON_BLOCKING || type == IMAGE_READ_NON_BLOCKING || type == IMAGE_WRITE_NON_BLOCKING) { |
| blocking_write = false; |
| } |
| result = fill_mem_with_data(context, device_id, queue, mems[current_allocation], d, blocking_write); |
| } |
| } |
| |
| // If creation failed, try to create a smaller object |
| if (result == FAILED_TOO_BIG) { |
| //log_info("\t\t\tAllocation %d failed at size %gMB. Trying smaller.\n", current_allocation, toMB(allocation_this_time)); |
| if (allocation_this_time > reduction_amount) |
| allocation_this_time -= reduction_amount; |
| else if (reduction_amount > 1) { |
| reduction_amount /= 2; |
| } |
| else { |
| allocation_this_time = 0; |
| } |
| |
| } |
| } |
| |
| if (result == FAILED_ABORT) { |
| log_error("\t\tAllocation failed.\n"); |
| return FAILED_ABORT; |
| } |
| |
| if (!allocation_this_time) { |
| log_info("\t\tFailed to allocate %gMB across several objects.\n", toMB(size_to_allocate)); |
| return FAILED_TOO_BIG; |
| } |
| |
| // Otherwise we succeeded |
| if (result != SUCCEEDED) { |
| log_error("Test logic error."); |
| exit(-1); |
| } |
| amount_allocated += allocation_this_time; |
| |
| *final_size = amount_allocated; |
| |
| current_allocation++; |
| } |
| |
| log_info("\t\tSucceeded in allocating %gMB using %d memory objects.\n", toMB(amount_allocated), current_allocation); |
| return SUCCEEDED; |
| } |