// // 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 "action_classes.h" #pragma mark -------------------- Base Action Class ------------------------- const cl_uint BufferSizeReductionFactor = 20; cl_int Action::IGetPreferredImageSize2D(cl_device_id device, size_t &outWidth, size_t &outHeight) { cl_ulong maxAllocSize; size_t maxWidth, maxHeight; cl_int error; // Get the largest possible buffer we could allocate error = clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(maxAllocSize), &maxAllocSize, NULL); error |= clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(maxWidth), &maxWidth, NULL); error |= clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(maxHeight), &maxHeight, NULL); test_error(error, "Unable to get device config"); // Create something of a decent size if (maxWidth * maxHeight * 4 > maxAllocSize / BufferSizeReductionFactor) { float rootSize = sqrtf((float)(maxAllocSize / (BufferSizeReductionFactor * 4))); if ((size_t)rootSize > maxWidth) outWidth = maxWidth; else outWidth = (size_t)rootSize; outHeight = (size_t)((maxAllocSize / (BufferSizeReductionFactor * 4)) / outWidth); if (outHeight > maxHeight) outHeight = maxHeight; } else { outWidth = maxWidth; outHeight = maxHeight; } outWidth /= 2; outHeight /= 2; if (outWidth > 2048) outWidth = 2048; if (outHeight > 2048) outHeight = 2048; log_info("\tImage size: %d x %d (%gMB)\n", (int)outWidth, (int)outHeight, (double)((int)outWidth * (int)outHeight * 4) / (1024.0 * 1024.0)); return CL_SUCCESS; } cl_int Action::IGetPreferredImageSize3D(cl_device_id device, size_t &outWidth, size_t &outHeight, size_t &outDepth) { cl_ulong maxAllocSize; size_t maxWidth, maxHeight, maxDepth; cl_int error; // Get the largest possible buffer we could allocate error = clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(maxAllocSize), &maxAllocSize, NULL); error |= clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_WIDTH, sizeof(maxWidth), &maxWidth, NULL); error |= clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_HEIGHT, sizeof(maxHeight), &maxHeight, NULL); error |= clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_DEPTH, sizeof(maxDepth), &maxDepth, NULL); test_error(error, "Unable to get device config"); // Create something of a decent size if ((cl_ulong)maxWidth * maxHeight * maxDepth > maxAllocSize / (BufferSizeReductionFactor * 4)) { float rootSize = cbrtf((float)(maxAllocSize / (BufferSizeReductionFactor * 4))); if ((size_t)rootSize > maxWidth) outWidth = maxWidth; else outWidth = (size_t)rootSize; if ((size_t)rootSize > maxHeight) outHeight = maxHeight; else outHeight = (size_t)rootSize; outDepth = (size_t)((maxAllocSize / (BufferSizeReductionFactor * 4)) / (outWidth * outHeight)); if (outDepth > maxDepth) outDepth = maxDepth; } else { outWidth = maxWidth; outHeight = maxHeight; outDepth = maxDepth; } outWidth /= 2; outHeight /= 2; outDepth /= 2; if (outWidth > 512) outWidth = 512; if (outHeight > 512) outHeight = 512; if (outDepth > 512) outDepth = 512; log_info("\tImage size: %d x %d x %d (%gMB)\n", (int)outWidth, (int)outHeight, (int)outDepth, (double)((int)outWidth * (int)outHeight * (int)outDepth * 4) / (1024.0 * 1024.0)); return CL_SUCCESS; } #pragma mark -------------------- Execution Sub-Classes ------------------------- cl_int NDRangeKernelAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { const char *long_kernel[] = { "__kernel void sample_test(__global float *src, __global int *dst)\n" "{\n" " int tid = get_global_id(0);\n" " int i;\n" "\n" " for( i = 0; i < 100000; i++ )\n" " {\n" " dst[tid] = (int)src[tid] * 3;\n" " }\n" "\n" "}\n" }; size_t threads[1] = { 1000 }; int error; if (create_single_kernel_helper(context, &mProgram, &mKernel, 1, long_kernel, "sample_test")) { return -1; } error = get_max_common_work_group_size(context, mKernel, threads[0], &mLocalThreads[0]); test_error(error, "Unable to get work group size to use"); mStreams[0] = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(cl_float) * 1000, NULL, &error); test_error(error, "Creating test array failed"); mStreams[1] = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(cl_int) * 1000, NULL, &error); test_error(error, "Creating test array failed"); /* Set the arguments */ error = clSetKernelArg(mKernel, 0, sizeof(mStreams[0]), &mStreams[0]); test_error(error, "Unable to set kernel arguments"); error = clSetKernelArg(mKernel, 1, sizeof(mStreams[1]), &mStreams[1]); test_error(error, "Unable to set kernel arguments"); return CL_SUCCESS; } cl_int NDRangeKernelAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t threads[1] = { 1000 }; cl_int error = clEnqueueNDRangeKernel(queue, mKernel, 1, NULL, threads, mLocalThreads, numWaits, waits, outEvent); test_error(error, "Unable to execute kernel"); return CL_SUCCESS; } #pragma mark -------------------- Buffer Sub-Classes ------------------------- cl_int BufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; cl_ulong maxAllocSize; // Get the largest possible buffer we could allocate error = clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(maxAllocSize), &maxAllocSize, NULL); // Don't create a buffer quite that big, just so we have some space left // over for other work mSize = (size_t)(maxAllocSize / BufferSizeReductionFactor); // Cap at 128M so tests complete in a reasonable amount of time. if (mSize > 128 << 20) mSize = 128 << 20; mSize /= 2; log_info("\tBuffer size: %gMB\n", (double)mSize / (1024.0 * 1024.0)); mBuffer = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, mSize, NULL, &error); test_error(error, "Unable to create buffer to test against"); mOutBuffer = malloc(mSize); if (mOutBuffer == NULL) { log_error("ERROR: Unable to allocate temp buffer (out of memory)\n"); return CL_OUT_OF_RESOURCES; } return CL_SUCCESS; } cl_int ReadBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { return BufferAction::Setup(device, context, queue); } cl_int ReadBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { cl_int error = clEnqueueReadBuffer(queue, mBuffer, CL_FALSE, 0, mSize, mOutBuffer, numWaits, waits, outEvent); test_error(error, "Unable to enqueue buffer read"); return CL_SUCCESS; } cl_int WriteBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { return BufferAction::Setup(device, context, queue); } cl_int WriteBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { cl_int error = clEnqueueWriteBuffer(queue, mBuffer, CL_FALSE, 0, mSize, mOutBuffer, numWaits, waits, outEvent); test_error(error, "Unable to enqueue buffer write"); return CL_SUCCESS; } MapBufferAction::~MapBufferAction() { if (mQueue) clEnqueueUnmapMemObject(mQueue, mBuffer, mMappedPtr, 0, NULL, NULL); } cl_int MapBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { return BufferAction::Setup(device, context, queue); } cl_int MapBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { cl_int error; mQueue = queue; mMappedPtr = clEnqueueMapBuffer(queue, mBuffer, CL_FALSE, CL_MAP_READ, 0, mSize, numWaits, waits, outEvent, &error); test_error(error, "Unable to enqueue buffer map"); return CL_SUCCESS; } cl_int UnmapBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error = BufferAction::Setup(device, context, queue); if (error != CL_SUCCESS) return error; mMappedPtr = clEnqueueMapBuffer(queue, mBuffer, CL_TRUE, CL_MAP_READ, 0, mSize, 0, NULL, NULL, &error); test_error(error, "Unable to enqueue buffer map"); return CL_SUCCESS; } cl_int UnmapBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { cl_int error = clEnqueueUnmapMemObject(queue, mBuffer, mMappedPtr, numWaits, waits, outEvent); test_error(error, "Unable to enqueue buffer unmap"); return CL_SUCCESS; } #pragma mark -------------------- Read/Write Image Classes ------------------------- cl_int ReadImage2DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mImage = create_image_2d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mOutput = malloc(mWidth * mHeight * 4); if (mOutput == NULL) { log_error("ERROR: Unable to allocate buffer: out of memory\n"); return CL_OUT_OF_RESOURCES; } return CL_SUCCESS; } cl_int ReadImage2DAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, 1 }; cl_int error = clEnqueueReadImage(queue, mImage, CL_FALSE, origin, region, 0, 0, mOutput, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image read"); return CL_SUCCESS; } cl_int ReadImage3DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mOutput = malloc(mWidth * mHeight * mDepth * 4); if (mOutput == NULL) { log_error("ERROR: Unable to allocate buffer: out of memory\n"); return CL_OUT_OF_RESOURCES; } return CL_SUCCESS; } cl_int ReadImage3DAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, mDepth }; cl_int error = clEnqueueReadImage(queue, mImage, CL_FALSE, origin, region, 0, 0, mOutput, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image read"); return CL_SUCCESS; } cl_int WriteImage2DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mImage = create_image_2d(context, CL_MEM_WRITE_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mOutput = malloc(mWidth * mHeight * 4); if (mOutput == NULL) { log_error("ERROR: Unable to allocate buffer: out of memory\n"); return CL_OUT_OF_RESOURCES; } return CL_SUCCESS; } cl_int WriteImage2DAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, 1 }; cl_int error = clEnqueueWriteImage(queue, mImage, CL_FALSE, origin, region, 0, 0, mOutput, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image write"); return CL_SUCCESS; } cl_int WriteImage3DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mOutput = malloc(mWidth * mHeight * mDepth * 4); if (mOutput == NULL) { log_error("ERROR: Unable to allocate buffer: out of memory\n"); return CL_OUT_OF_RESOURCES; } return CL_SUCCESS; } cl_int WriteImage3DAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, mDepth }; cl_int error = clEnqueueWriteImage(queue, mImage, CL_FALSE, origin, region, 0, 0, mOutput, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image write"); return CL_SUCCESS; } #pragma mark -------------------- Copy Image Classes ------------------------- cl_int CopyImageAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, mDepth }; cl_int error = clEnqueueCopyImage(queue, mSrcImage, mDstImage, origin, origin, region, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image copy"); return CL_SUCCESS; } cl_int CopyImage2Dto2DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; mWidth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_2d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstImage = create_image_2d(context, CL_MEM_WRITE_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDepth = 1; return CL_SUCCESS; } cl_int CopyImage2Dto3DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; mDepth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_2d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDepth = 1; return CL_SUCCESS; } cl_int CopyImage3Dto2DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; mDepth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstImage = create_image_2d(context, CL_MEM_WRITE_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDepth = 1; return CL_SUCCESS; } cl_int CopyImage3Dto3DAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; mDepth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); return CL_SUCCESS; } #pragma mark -------------------- Copy Image/Buffer Classes ------------------------- cl_int Copy2DImageToBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; mWidth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_2d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, mWidth * mHeight * 4, NULL, &error); test_error(error, "Unable to create buffer to test against"); return CL_SUCCESS; } cl_int Copy2DImageToBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, 1 }; cl_int error = clEnqueueCopyImageToBuffer(queue, mSrcImage, mDstBuffer, origin, region, 0, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image to buffer copy"); return CL_SUCCESS; } cl_int Copy3DImageToBufferAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; mDepth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); mDstBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, mWidth * mHeight * mDepth * 4, NULL, &error); test_error(error, "Unable to create buffer to test against"); return CL_SUCCESS; } cl_int Copy3DImageToBufferAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, mDepth }; cl_int error = clEnqueueCopyImageToBuffer(queue, mSrcImage, mDstBuffer, origin, region, 0, numWaits, waits, outEvent); test_error(error, "Unable to enqueue image to buffer copy"); return CL_SUCCESS; } cl_int CopyBufferTo2DImageAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; mWidth /= 2; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mSrcBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY, mWidth * mHeight * 4, NULL, &error); test_error(error, "Unable to create buffer to test against"); mDstImage = create_image_2d(context, CL_MEM_WRITE_ONLY, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); return CL_SUCCESS; } cl_int CopyBufferTo2DImageAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, 1 }; cl_int error = clEnqueueCopyBufferToImage(queue, mSrcBuffer, mDstImage, 0, origin, region, numWaits, waits, outEvent); test_error(error, "Unable to enqueue buffer to image copy"); return CL_SUCCESS; } cl_int CopyBufferTo3DImageAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize3D(device, mWidth, mHeight, mDepth))) return error; mDepth /= 2; mSrcBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY, mWidth * mHeight * mDepth * 4, NULL, &error); test_error(error, "Unable to create buffer to test against"); cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mDstImage = create_image_3d(context, CL_MEM_READ_ONLY, &format, mWidth, mHeight, mDepth, 0, 0, NULL, &error); test_error(error, "Unable to create image to test against"); return CL_SUCCESS; } cl_int CopyBufferTo3DImageAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, mDepth }; cl_int error = clEnqueueCopyBufferToImage(queue, mSrcBuffer, mDstImage, 0, origin, region, numWaits, waits, outEvent); test_error(error, "Unable to enqueue buffer to image copy"); return CL_SUCCESS; } #pragma mark -------------------- Map Image Class ------------------------- MapImageAction::~MapImageAction() { if (mQueue) clEnqueueUnmapMemObject(mQueue, mImage, mMappedPtr, 0, NULL, NULL); } cl_int MapImageAction::Setup(cl_device_id device, cl_context context, cl_command_queue queue) { cl_int error; if ((error = IGetPreferredImageSize2D(device, mWidth, mHeight))) return error; cl_image_format format = { CL_RGBA, CL_SIGNED_INT8 }; mImage = create_image_2d(context, CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR, &format, mWidth, mHeight, 0, NULL, &error); test_error(error, "Unable to create image to test against"); return CL_SUCCESS; } cl_int MapImageAction::Execute(cl_command_queue queue, cl_uint numWaits, cl_event *waits, cl_event *outEvent) { cl_int error; size_t origin[3] = { 0, 0, 0 }, region[3] = { mWidth, mHeight, 1 }; size_t outPitch; mQueue = queue; mMappedPtr = clEnqueueMapImage(queue, mImage, CL_FALSE, CL_MAP_READ, origin, region, &outPitch, NULL, numWaits, waits, outEvent, &error); test_error(error, "Unable to enqueue image map"); return CL_SUCCESS; }