// // 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 "common.h" #include "../../test_common/harness/mt19937.h" #include #include #if !defined(_WIN32) #include #endif typedef struct { std::atomic status; cl_uint num_svm_pointers; std::vector svm_pointers; } CallbackData; void generate_data(std::vector &data, size_t size, MTdata seed) { cl_uint randomData = genrand_int32(seed); cl_uint bitsLeft = 32; for( size_t i = 0; i < size; i++ ) { if( 0 == bitsLeft) { randomData = genrand_int32(seed); bitsLeft = 32; } data[i] = (cl_uchar)( randomData & 255 ); randomData >>= 8; randomData -= 8; } } //callback which will be passed to clEnqueueSVMFree command void CL_CALLBACK callback_svm_free(cl_command_queue queue, cl_uint num_svm_pointers, void * svm_pointers[], void * user_data) { CallbackData *data = (CallbackData *)user_data; data->num_svm_pointers = num_svm_pointers; data->svm_pointers.resize(num_svm_pointers, 0); cl_context context; if(clGetCommandQueueInfo(queue, CL_QUEUE_CONTEXT, sizeof(cl_context), &context, 0) != CL_SUCCESS) { log_error("clGetCommandQueueInfo failed in the callback\n"); return; } for (size_t i = 0; i < num_svm_pointers; ++i) { data->svm_pointers[i] = svm_pointers[i]; clSVMFree(context, svm_pointers[i]); } data->status.store(1, std::memory_order_release); } int test_enqueue_api(cl_device_id deviceID, cl_context c, cl_command_queue queue, int num_elements) { clContextWrapper context = NULL; clCommandQueueWrapper queues[MAXQ]; cl_uint num_devices = 0; const size_t elementNum = 1024; const size_t numSVMBuffers = 32; cl_int error = CL_SUCCESS; RandomSeed seed(0); error = create_cl_objects(deviceID, NULL, &context, NULL, &queues[0], &num_devices, CL_DEVICE_SVM_COARSE_GRAIN_BUFFER); if(error) return -1; queue = queues[0]; //all possible sizes of vectors and scalars size_t typeSizes[] = { sizeof(cl_uchar), sizeof(cl_uchar2), sizeof(cl_uchar3), sizeof(cl_uchar4), sizeof(cl_uchar8), sizeof(cl_uchar16), sizeof(cl_ushort), sizeof(cl_ushort2), sizeof(cl_ushort3), sizeof(cl_ushort4), sizeof(cl_ushort8), sizeof(cl_ushort16), sizeof(cl_uint), sizeof(cl_uint2), sizeof(cl_uint3), sizeof(cl_uint4), sizeof(cl_uint8), sizeof(cl_uint16), sizeof(cl_ulong), sizeof(cl_ulong2), sizeof(cl_ulong3), sizeof(cl_ulong4), sizeof(cl_ulong8), sizeof(cl_ulong16), }; for (size_t i = 0; i < ( sizeof(typeSizes) / sizeof(typeSizes[0]) ); ++i) { //generate initial data std::vector fillData0(typeSizes[i]), fillData1(typeSizes[i], 0), fillData2(typeSizes[i]); generate_data(fillData0, typeSizes[i], seed); generate_data(fillData2, typeSizes[i], seed); cl_uchar *srcBuffer = (cl_uchar *)clSVMAlloc(context, CL_MEM_READ_WRITE, elementNum * typeSizes[i], 0); cl_uchar *dstBuffer = (cl_uchar *)clSVMAlloc(context, CL_MEM_READ_WRITE, elementNum * typeSizes[i], 0); clEventWrapper userEvent = clCreateUserEvent(context, &error); test_error(error, "clCreateUserEvent failed"); clEventWrapper eventMemFill; error = clEnqueueSVMMemFill(queue, srcBuffer, &fillData0[0], typeSizes[i], elementNum * typeSizes[i], 1, &userEvent, &eventMemFill); test_error(error, "clEnqueueSVMMemFill failed"); clEventWrapper eventMemcpy; error = clEnqueueSVMMemcpy(queue, CL_FALSE, dstBuffer, srcBuffer, elementNum * typeSizes[i], 1, &eventMemFill, &eventMemcpy); test_error(error, "clEnqueueSVMMemcpy failed"); error = clSetUserEventStatus(userEvent, CL_COMPLETE); test_error(error, "clSetUserEventStatus failed"); clEventWrapper eventMap; error = clEnqueueSVMMap(queue, CL_FALSE, CL_MAP_READ | CL_MAP_WRITE, dstBuffer, elementNum * typeSizes[i], 1, &eventMemcpy, &eventMap); test_error(error, "clEnqueueSVMMap failed"); error = clWaitForEvents(1, &eventMap); test_error(error, "clWaitForEvents failed"); //data verification for (size_t j = 0; j < elementNum * typeSizes[i]; ++j) { if (dstBuffer[j] != fillData0[j % typeSizes[i]]) { log_error("Invalid data at index %ld, expected %d, got %d\n", j, fillData0[j % typeSizes[i]], dstBuffer[j]); return -1; } } clEventWrapper eventUnmap; error = clEnqueueSVMUnmap(queue, dstBuffer, 0, 0, &eventUnmap); test_error(error, "clEnqueueSVMUnmap failed"); error = clEnqueueSVMMemFill(queue, srcBuffer, &fillData2[0], typeSizes[i], elementNum * typeSizes[i] / 2, 0, 0, 0); test_error(error, "clEnqueueSVMMemFill failed"); error = clEnqueueSVMMemFill(queue, dstBuffer + elementNum * typeSizes[i] / 2, &fillData2[0], typeSizes[i], elementNum * typeSizes[i] / 2, 0, 0, 0); test_error(error, "clEnqueueSVMMemFill failed"); error = clEnqueueSVMMemcpy(queue, CL_FALSE, dstBuffer, srcBuffer, elementNum * typeSizes[i] / 2, 0, 0, 0); test_error(error, "clEnqueueSVMMemcpy failed"); error = clEnqueueSVMMemcpy(queue, CL_TRUE, dstBuffer + elementNum * typeSizes[i] / 2, srcBuffer + elementNum * typeSizes[i] / 2, elementNum * typeSizes[i] / 2, 0, 0, 0); test_error(error, "clEnqueueSVMMemcpy failed"); void *ptrs[] = {(void *)srcBuffer, (void *)dstBuffer}; clEventWrapper eventFree; error = clEnqueueSVMFree(queue, 2, ptrs, 0, 0, 0, 0, &eventFree); test_error(error, "clEnqueueSVMFree failed"); error = clWaitForEvents(1, &eventFree); test_error(error, "clWaitForEvents failed"); //event info verification for new SVM commands cl_command_type commandType; error = clGetEventInfo(eventMemFill, CL_EVENT_COMMAND_TYPE, sizeof(cl_command_type), &commandType, NULL); test_error(error, "clGetEventInfo failed"); if (commandType != CL_COMMAND_SVM_MEMFILL) { log_error("Invalid command type returned for clEnqueueSVMMemFill\n"); return -1; } error = clGetEventInfo(eventMemcpy, CL_EVENT_COMMAND_TYPE, sizeof(cl_command_type), &commandType, NULL); test_error(error, "clGetEventInfo failed"); if (commandType != CL_COMMAND_SVM_MEMCPY) { log_error("Invalid command type returned for clEnqueueSVMMemcpy\n"); return -1; } error = clGetEventInfo(eventMap, CL_EVENT_COMMAND_TYPE, sizeof(cl_command_type), &commandType, NULL); test_error(error, "clGetEventInfo failed"); if (commandType != CL_COMMAND_SVM_MAP) { log_error("Invalid command type returned for clEnqueueSVMMap\n"); return -1; } error = clGetEventInfo(eventUnmap, CL_EVENT_COMMAND_TYPE, sizeof(cl_command_type), &commandType, NULL); test_error(error, "clGetEventInfo failed"); if (commandType != CL_COMMAND_SVM_UNMAP) { log_error("Invalid command type returned for clEnqueueSVMUnmap\n"); return -1; } error = clGetEventInfo(eventFree, CL_EVENT_COMMAND_TYPE, sizeof(cl_command_type), &commandType, NULL); test_error(error, "clGetEventInfo failed"); if (commandType != CL_COMMAND_SVM_FREE) { log_error("Invalid command type returned for clEnqueueSVMFree\n"); return -1; } } std::vector buffers(numSVMBuffers, 0); for(size_t i = 0; i < numSVMBuffers; ++i) buffers[i] = clSVMAlloc(context, CL_MEM_READ_WRITE, elementNum, 0); //verify if callback is triggered correctly CallbackData data; data.status = 0; error = clEnqueueSVMFree(queue, buffers.size(), &buffers[0], callback_svm_free, &data, 0, 0, 0); test_error(error, "clEnqueueSVMFree failed"); error = clFinish(queue); test_error(error, "clFinish failed"); //wait for the callback while(data.status.load(std::memory_order_acquire) == 0) { usleep(1); } //check if number of SVM pointers returned in the callback matches with expected if (data.num_svm_pointers != buffers.size()) { log_error("Invalid number of SVM pointers returned in the callback, expected: %ld, got: %d\n", buffers.size(), data.num_svm_pointers); return -1; } //check if pointers returned in callback are correct for (size_t i = 0; i < buffers.size(); ++i) { if (data.svm_pointers[i] != buffers[i]) { log_error("Invalid SVM pointer returned in the callback, idx: %ld\n", i); return -1; } } return 0; }