#include "EmberCLPch.h" #include "OpenCLWrapper.h" namespace EmberCLns { /// /// Constructor that sets everything to an uninitialized state. /// No OpenCL setup is done here, the caller must explicitly do it. /// OpenCLWrapper::OpenCLWrapper() { m_Init = false; m_Shared = false; m_PlatformIndex = 0; m_DeviceIndex = 0; m_LocalMemSize = 0; cl::Platform::get(&m_Platforms); m_Devices.resize(m_Platforms.size()); for (size_t i = 0; i < m_Platforms.size(); i++) m_Platforms[i].getDevices(CL_DEVICE_TYPE_ALL, &m_Devices[i]); } /// /// Determine if OpenCL is available on the system. /// /// True if any OpenCL platform and at least 1 device within that platform exists on the system, else false. bool OpenCLWrapper::CheckOpenCL() { for (size_t i = 0; i < m_Platforms.size(); i++) for (size_t j = 0; j < m_Devices[i].size(); j++) return true; return false; } /// /// Initialize the specified platform and device. /// This can be shared with OpenGL. /// /// The index platform of the platform to use /// The index device of the device to use /// True if shared with OpenGL, else false. /// True if success, else false. bool OpenCLWrapper::Init(uint platform, uint device, bool shared) { cl_int err; m_Init = false; m_ErrorReport.clear(); if (m_Platforms.size() > 0) { if (platform < m_Platforms.size() && platform < m_Devices.size()) { m_PlatformIndex = platform;//Platform is ok, now do context. if (CreateContext(shared)) { //Context is ok, now do device. if (device < m_Devices[m_PlatformIndex].size()) { //At least one GPU device is present, so create a command queue. m_Queue = cl::CommandQueue(m_Context, m_Devices[m_PlatformIndex][device], 0, &err); if (CheckCL(err, "cl::CommandQueue()")) { m_DeviceIndex = device; m_Platform = m_Platforms[m_PlatformIndex]; m_Device = m_Devices[m_PlatformIndex][device]; m_DeviceVec.clear(); m_DeviceVec.push_back(m_Device); m_LocalMemSize = uint(GetInfo(m_PlatformIndex, m_DeviceIndex, CL_DEVICE_LOCAL_MEM_SIZE)); m_Shared = shared; m_Init = true;//Command queue is ok, it's now ok to begin building and running programs. } } } } } return m_Init; } /// /// Compile and add the program, using the specified entry point. /// If a program with the same name already exists then it will be replaced. /// /// The name of the program /// The program source /// The name of the entry point kernel function in the program /// True if success, else false. bool OpenCLWrapper::AddProgram(const string& name, const string& program, const string& entryPoint, bool doublePrecision) { Spk spk; if (CreateSPK(name, program, entryPoint, spk, doublePrecision)) { for (size_t i = 0; i < m_Programs.size(); i++) { if (name == m_Programs[i].m_Name) { m_Programs[i] = spk; return true; } } //Nothing was found, so add. m_Programs.push_back(spk); return true; } return false; } /// /// Clear the programs. /// void OpenCLWrapper::ClearPrograms() { m_Programs.clear(); } /// /// Add a buffer with the specified size and name. /// Three possible actions to take: /// Buffer didn't exist, so create and add. /// Buffer existed, but was a different size. Replace. /// Buffer existed with the same size, do nothing. /// /// The name of the buffer /// The size in bytes of the buffer /// The buffer flags. Default: CL_MEM_READ_WRITE. /// True if success, else false. bool OpenCLWrapper::AddBuffer(const string& name, size_t size, cl_mem_flags flags) { cl_int err; if (m_Init) { int bufferIndex = FindBufferIndex(name); if (bufferIndex == -1)//If the buffer didn't exist, create and add. { cl::Buffer buff(m_Context, flags, size, nullptr, &err); if (!CheckCL(err, "cl::Buffer()")) return false; NamedBuffer nb(buff, name); m_Buffers.push_back(nb); } else if (GetBufferSize(bufferIndex) != size)//If it did exist, only create and add if the sizes were different. { m_Buffers[bufferIndex] = NamedBuffer(cl::Buffer(m_Context, flags, 0, nullptr, &err), "emptybuffer");//First clear out the original so the two don't exist in memory at once. cl::Buffer buff(m_Context, flags, size, nullptr, &err);//Create the new buffer. if (!CheckCL(err, "cl::Buffer()")) return false; NamedBuffer nb(buff, name);//Make a named buffer out of the new buffer. m_Buffers[bufferIndex] = nb;//Finally, assign. } //If the buffer existed and the sizes were the same, take no action. return true; } return false; } /// /// Add and/or write a buffer of data with the specified name to the list of buffers. /// Three possible actions to take: /// Buffer didn't exist, so create and add. /// Buffer existed, but was a different size. Replace. /// Buffer existed with the same size, copy data. /// /// The name of the buffer /// A pointer to the buffer /// The size in bytes of the buffer /// The buffer flags. Default: CL_MEM_READ_WRITE. /// True if success, else false. bool OpenCLWrapper::AddAndWriteBuffer(const string& name, void* data, size_t size, cl_mem_flags flags) { bool b = false; if (AddBuffer(name, size, flags)) b = WriteBuffer(name, data, size); return b; } /// /// Write data to an existing buffer with the specified name. /// /// The name of the buffer /// A pointer to the buffer /// The size in bytes of the buffer /// True if success, else false. bool OpenCLWrapper::WriteBuffer(const string& name, void* data, size_t size) { int bufferIndex = FindBufferIndex(name); return bufferIndex != -1 ? WriteBuffer(bufferIndex, data, size) : false; } /// /// Write data to an existing buffer at the specified index. /// /// The index of the buffer /// A pointer to the buffer /// The size in bytes of the buffer /// True if success, else false. bool OpenCLWrapper::WriteBuffer(uint bufferIndex, void* data, size_t size) { if (m_Init && (bufferIndex < m_Buffers.size()) && (GetBufferSize(bufferIndex) == size)) { cl::Event e; cl_int err = m_Queue.enqueueWriteBuffer(m_Buffers[bufferIndex].m_Buffer, CL_TRUE, 0, size, data, nullptr, &e); e.wait(); m_Queue.finish(); if (CheckCL(err, "cl::CommandQueue::enqueueWriteBuffer()")) return true; } return false; } /// /// Read data from an existing buffer with the specified name. /// /// The name of the buffer /// A pointer to a buffer to copy the data to /// The size in bytes of the buffer /// True if success, else false. bool OpenCLWrapper::ReadBuffer(const string& name, void* data, size_t size) { int bufferIndex = FindBufferIndex(name); return bufferIndex != -1 ? ReadBuffer(bufferIndex, data, size) : false; } /// /// Read data from an existing buffer at the specified index. /// /// The index of the buffer /// A pointer to a buffer to copy the data to /// The size in bytes of the buffer /// True if success, else false. bool OpenCLWrapper::ReadBuffer(uint bufferIndex, void* data, size_t size) { if (m_Init && (bufferIndex < m_Buffers.size()) && (GetBufferSize(bufferIndex) == size)) { cl::Event e; cl_int err = m_Queue.enqueueReadBuffer(m_Buffers[bufferIndex].m_Buffer, CL_TRUE, 0, size, data, nullptr, &e); e.wait(); m_Queue.finish(); if (CheckCL(err, "cl::CommandQueue::enqueueReadBuffer()")) return true; } return false; } /// /// Find the index of the buffer with the specified name. /// /// The name of the buffer to search for /// The index if found, else -1. int OpenCLWrapper::FindBufferIndex(const string& name) { for (size_t i = 0; i < m_Buffers.size(); i++) if (m_Buffers[i].m_Name == name) return int(i); return -1; } /// /// Get the size of the buffer with the specified name. /// /// The name of the buffer to search for /// The size of the buffer if found, else 0. uint OpenCLWrapper::GetBufferSize(const string& name) { int bufferIndex = FindBufferIndex(name); return bufferIndex != -1 ? GetBufferSize(bufferIndex) : 0; } /// /// Get the size of the buffer at the specified index. /// /// The index of the buffer to get the size of /// The size of the buffer if found, else 0. uint OpenCLWrapper::GetBufferSize(uint bufferIndex) { if (m_Init && (bufferIndex < m_Buffers.size())) return uint(m_Buffers[bufferIndex].m_Buffer.getInfo(nullptr)); return 0; } /// /// Clear all buffers. /// void OpenCLWrapper::ClearBuffers() { m_Buffers.clear(); } /// /// Add and/or write a new 2D image. /// Three possible actions to take: /// Image didn't exist, so create and add. /// Image existed, but was a different size. Replace. /// Image existed with the same size, copy data. /// /// The name of the image to add/replace /// The memory flags /// The image format /// The width in pixels of the image /// The height in pixels of the image /// The row pitch (usually zero) /// The image data. Default: NULL. /// True if shared with an OpenGL texture, else false. Default: false. /// The texture ID of the shared OpenGL texture if shared. Default: 0. /// True if success, else false. bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, const cl::ImageFormat& format, ::size_t width, ::size_t height, ::size_t row_pitch, void* data, bool shared, GLuint texName) { cl_int err; if (m_Init) { int imageIndex = FindImageIndex(name, shared); if (imageIndex == -1)//If the image didn't exist, create and add. { if (shared) { //::wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()); IMAGEGL2D imageGL(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err); NamedImage2DGL namedImageGL(imageGL, name); if (CheckCL(err, "cl::ImageGL()")) { m_GLImages.push_back(namedImageGL); if (data) return WriteImage2D(uint(m_GLImages.size() - 1), true, width, height, row_pitch, data);//OpenGL images/textures require a separate write. else return true; } } else { NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name); if (CheckCL(err, "cl::Image2D()")) { m_Images.push_back(namedImage); return true; } } } else//It did exist, so create new if sizes are different. Write if data is not NULL. { if (shared) { IMAGEGL2D imageGL = m_GLImages[imageIndex].m_Image; if (!CompareImageParams(imageGL, flags, format, width, height, row_pitch)) { NamedImage2DGL namedImageGL(IMAGEGL2D(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err), name);//Sizes are different, so create new. if (CheckCL(err, "cl::ImageGL()")) { m_GLImages[imageIndex] = namedImageGL; } else return false; } //Write data to new image since OpenGL images/textures require a separate write, must match new size. if (data) return WriteImage2D(imageIndex, true, width, height, row_pitch, data); else return true; } else { if (!CompareImageParams(m_Images[imageIndex].m_Image, flags, format, width, height, row_pitch)) { m_Images[imageIndex] = NamedImage2D();//First clear out the original so the two don't exist in memory at once. NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name); if (CheckCL(err, "cl::Image2D()")) { m_Images[imageIndex] = namedImage; return true; } } else if (data) return WriteImage2D(imageIndex, false, width, height, row_pitch, data); else//Strange case: images were same dimensions but no data was passed in, so do nothing. return true; } } } return false; } /// /// Write data to an existing 2D image at the specified index. /// /// The index of the image /// True if shared with an OpenGL texture, else false. /// The width in pixels of the image /// The height in pixels of the image /// The row pitch (usually zero) /// The image data /// True if success, else false. bool OpenCLWrapper::WriteImage2D(uint index, bool shared, ::size_t width, ::size_t height, ::size_t row_pitch, void* data) { if (m_Init) { cl_int err; cl::Event e; cl::size_t<3> origin, region; origin[0] = 0; origin[1] = 0; origin[2] = 0; region[0] = width; region[1] = height; region[2] = 1; if (shared && index < m_GLImages.size()) { IMAGEGL2D imageGL = m_GLImages[index].m_Image; if (EnqueueAcquireGLObjects(imageGL)) { err = m_Queue.enqueueWriteImage(imageGL, CL_TRUE, origin, region, row_pitch, 0, data, nullptr, &e); e.wait(); m_Queue.finish(); bool b = EnqueueReleaseGLObjects(imageGL); return CheckCL(err, "cl::enqueueWriteImage()") && b; } } else if (!shared && index < m_Images.size()) { err = m_Queue.enqueueWriteImage(m_Images[index].m_Image, CL_TRUE, origin, region, row_pitch, 0, data, nullptr, &e); e.wait(); m_Queue.finish(); return CheckCL(err, "cl::enqueueWriteImage()"); } } return false; } /// /// Read data from an existing 2D image with the specified name. /// /// The name of the image /// The width in pixels of the image /// The height in pixels of the image /// The row pitch (usually zero) /// True if shared with an OpenGL texture, else false. /// A pointer to a buffer to copy the data to /// True if success, else false. bool OpenCLWrapper::ReadImage(const string& name, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data) { if (m_Init) { int imageIndex = FindImageIndex(name, shared); if (imageIndex != -1) return ReadImage(imageIndex, width, height, row_pitch, shared, data); } return false; } /// /// Read data from an existing 2D image at the specified index. /// /// The name of the image /// The width in pixels of the image /// The height in pixels of the image /// The row pitch (usually zero) /// True if shared with an OpenGL texture, else false. /// A pointer to a buffer to copy the data to /// True if success, else false. bool OpenCLWrapper::ReadImage(uint imageIndex, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data) { if (m_Init) { cl_int err; cl::Event e; cl::size_t<3> origin, region; origin[0] = 0; origin[1] = 0; origin[2] = 0; region[0] = width; region[1] = height; region[2] = 1; if (shared && imageIndex < m_GLImages.size()) { IMAGEGL2D imageGL = m_GLImages[imageIndex].m_Image; if (EnqueueAcquireGLObjects(imageGL)) { err = m_Queue.enqueueReadImage(m_GLImages[imageIndex].m_Image, true, origin, region, row_pitch, 0, data); bool b = EnqueueReleaseGLObjects(m_GLImages[imageIndex].m_Image); return CheckCL(err, "cl::enqueueReadImage()") && b; } } else if (!shared && imageIndex < m_Images.size()) { err = m_Queue.enqueueReadImage(m_Images[imageIndex].m_Image, true, origin, region, row_pitch, 0, data); return CheckCL(err, "cl::enqueueReadImage()"); } } return false; } /// /// Find the index of the 2D image with the specified name. /// /// The name of the image to search for /// True if shared with an OpenGL texture, else false. /// The index if found, else -1. int OpenCLWrapper::FindImageIndex(const string& name, bool shared) { if (shared) { for (size_t i = 0; i < m_GLImages.size(); i++) if (m_GLImages[i].m_Name == name) return int(i); } else { for (size_t i = 0; i < m_Images.size(); i++) if (m_Images[i].m_Name == name) return int(i); } return -1; } /// /// Get the size of the 2D image with the specified name. /// /// The name of the image to search for /// True if shared with an OpenGL texture, else false. /// The size of the 2D image if found, else 0. uint OpenCLWrapper::GetImageSize(const string& name, bool shared) { int imageIndex = FindImageIndex(name, shared); return GetImageSize(imageIndex, shared); } /// /// Get the size of the 2D image at the specified index. /// /// Index of the image to search for /// True if shared with an OpenGL texture, else false. /// The size of the 2D image if found, else 0. uint OpenCLWrapper::GetImageSize(uint imageIndex, bool shared) { size_t size = 0; if (m_Init) { if (shared && imageIndex < m_GLImages.size()) { vector images; images.push_back(m_GLImages[imageIndex].m_Image); IMAGEGL2D image = m_GLImages[imageIndex].m_Image; if (EnqueueAcquireGLObjects(&images)) size = image.getImageInfo(nullptr) * image.getImageInfo(nullptr) * image.getImageInfo(nullptr);//Should pitch be checked here? EnqueueReleaseGLObjects(&images); } else if (!shared && imageIndex < m_Images.size()) { cl::Image2D image = m_Images[imageIndex].m_Image; size = image.getImageInfo(nullptr) * image.getImageInfo(nullptr) * image.getImageInfo(nullptr);//Should pitch be checked here? } } return uint(size); } /// /// Compare the passed in image with the specified parameters. /// /// The image to compare /// The memory flags to compare (ommitted) /// The format to compare /// The width to compare /// The height to compare /// The row_pitch to compare (omitted) /// True if all parameters matched, else false. bool OpenCLWrapper::CompareImageParams(cl::Image& image, cl_mem_flags flags, const cl::ImageFormat& format, ::size_t width, ::size_t height, ::size_t row_pitch) { cl_image_format tempFormat = image.getImageInfo(nullptr); return (/*image.getImageInfo() == flags &&*/ tempFormat.image_channel_data_type == format.image_channel_data_type && tempFormat.image_channel_order == format.image_channel_order && image.getImageInfo(nullptr) == width && image.getImageInfo(nullptr) == height/* && image.getImageInfo() == row_pitch*/);//Pitch will be (width * bytes per pixel) + padding. } /// /// Clear all images. /// /// True to clear shared images, else clear regular images. void OpenCLWrapper::ClearImages(bool shared) { if (shared) m_GLImages.clear(); else m_Images.clear(); } /// /// Create a 2D image and store in the image passed in. /// /// The 2D image to store the newly created image in /// The memory flags to use /// The format to use /// The width in pixels of the image /// The height in pixels of the image /// The row pitch (usually zero) /// The image data. Default: NULL. /// True if success, else false. bool OpenCLWrapper::CreateImage2D(cl::Image2D& image2D, cl_mem_flags flags, cl::ImageFormat format, ::size_t width, ::size_t height, ::size_t row_pitch, void* data) { if (m_Init) { cl_int err; image2D = cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err); return CheckCL(err, "cl::Image2D()"); } return false; } /// /// Create a 2D image shared with an OpenGL texture and store in the image passed in. /// /// The 2D image to store the newly created image in /// The memory flags to use /// The target /// The mip map level /// The texture ID of the shared OpenGL texture /// True if success, else false. bool OpenCLWrapper::CreateImage2DGL(IMAGEGL2D& image2DGL, cl_mem_flags flags, GLenum target, GLint miplevel, GLuint texobj) { if (m_Init) { cl_int err; image2DGL = IMAGEGL2D(m_Context, flags, target, miplevel, texobj, &err); return CheckCL(err, "cl::ImageGL()"); } return false; } /// /// Acquire the shared 2D image with the specified name. /// /// The name of the image to acquire /// True if success, else false. bool OpenCLWrapper::EnqueueAcquireGLObjects(const string& name) { int index = FindImageIndex(name, true); if (index != -1) return EnqueueAcquireGLObjects(m_GLImages[index].m_Image); return false; } /// /// Acquire the shared 2D image. /// /// The image to acquire /// True if success, else false. bool OpenCLWrapper::EnqueueAcquireGLObjects(IMAGEGL2D& image) { if (m_Init && m_Shared) { vector images; images.push_back(image); cl_int err = m_Queue.enqueueAcquireGLObjects(&images); m_Queue.finish(); return CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()"); } return false; } /// /// Reelease the shared 2D image with the specified name. /// /// The name of the image to release /// True if success, else false. bool OpenCLWrapper::EnqueueReleaseGLObjects(const string& name) { int index = FindImageIndex(name, true); if (index != -1) return EnqueueReleaseGLObjects(m_GLImages[index].m_Image); return false; } /// /// Release the shared 2D image. /// /// The image to release /// True if success, else false. bool OpenCLWrapper::EnqueueReleaseGLObjects(IMAGEGL2D& image) { if (m_Init && m_Shared) { vector images; images.push_back(image); cl_int err = m_Queue.enqueueReleaseGLObjects(&images); m_Queue.finish(); return CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()"); } return false; } /// /// Acquire a vector of shared OpenGL memory objects. /// /// The memory objects to acquire /// True if success, else false. bool OpenCLWrapper::EnqueueAcquireGLObjects(const VECTOR_CLASS* memObjects) { if (m_Init && m_Shared) { cl_int err = m_Queue.enqueueAcquireGLObjects(memObjects); m_Queue.finish(); return CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()"); } return false; } /// /// Release a vector of shared OpenGL memory objects. /// /// The memory objects to release /// True if success, else false. bool OpenCLWrapper::EnqueueReleaseGLObjects(const VECTOR_CLASS* memObjects) { if (m_Init && m_Shared) { cl_int err = m_Queue.enqueueReleaseGLObjects(memObjects); m_Queue.finish(); return CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()"); } return false; } /// /// Create a texture sampler. /// /// The sampler to store the newly created sampler in /// True to use normalized coordinates, else don't. /// The addressing mode to use /// The filter mode to use /// True if success, else false. bool OpenCLWrapper::CreateSampler(cl::Sampler& sampler, cl_bool normalizedCoords, cl_addressing_mode addressingMode, cl_filter_mode filterMode) { cl_int err; sampler = cl::Sampler(m_Context, normalizedCoords, addressingMode, filterMode, &err); return CheckCL(err, "cl::Sampler()"); } /// /// Set the argument at the specified index for the kernel at the specified index to be /// the buffer with the specified name. /// /// Index of the kernel /// Index of the argument /// The name of the buffer /// True if success, else false. bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, const string& name) { int bufferIndex = OpenCLWrapper::FindBufferIndex(name); return bufferIndex != -1 ? SetBufferArg(kernelIndex, argIndex, bufferIndex) : false; } /// /// Set the argument at the specified index for the kernel at the specified index to be /// the buffer at the specified index. /// /// Index of the kernel /// Index of the argument /// Index of the buffer /// True if success, else false. bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, uint bufferIndex) { if (m_Init && bufferIndex < m_Buffers.size()) return SetArg(kernelIndex, argIndex, m_Buffers[bufferIndex].m_Buffer); return false; } /// /// Set the argument at the specified index for the kernel at the specified index to be /// the 2D image with the specified name. /// /// Index of the kernel /// Index of the argument /// True if shared with an OpenGL texture, else false /// The name of the 2D image /// True if success, else false. bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, const string& name) { if (m_Init) { int imageIndex = FindImageIndex(name, shared); return SetImageArg(kernelIndex, argIndex, shared, imageIndex); } return false; } /// /// Set the argument at the specified index for the kernel at the specified index to be /// the 2D image at the specified index. /// /// Index of the kernel /// Index of the argument /// True if shared with an OpenGL texture, else false /// Index of the 2D image /// True if success, else false. bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, uint imageIndex) { cl_int err; if (m_Init) { if (shared && imageIndex < m_GLImages.size()) { err = m_Programs[kernelIndex].m_Kernel.setArg(argIndex, m_GLImages[imageIndex].m_Image); return CheckCL(err, "cl::Kernel::setArg()"); } else if (!shared && imageIndex < m_Images.size()) { err = m_Programs[kernelIndex].m_Kernel.setArg(argIndex, m_Images[imageIndex].m_Image); return CheckCL(err, "cl::Kernel::setArg()"); } } return false; } /// /// Find the index of the kernel with the specified name. /// /// The name of the kernel to search for /// The index if found, else -1. int OpenCLWrapper::FindKernelIndex(const string& name) { for (size_t i = 0; i < m_Programs.size(); i++) if (m_Programs[i].m_Name == name) return int(i); return -1; } /// /// Run the kernel at the specified index, using the specified grid and block dimensions. /// /// Index of the kernel to run /// Total width of the grid /// Total height of the grid /// The total depth grid /// Width of each block /// Height of each block /// Depth of each block /// True if success, else false. bool OpenCLWrapper::RunKernel(uint kernelIndex, uint totalGridWidth, uint totalGridHeight, uint totalGridDepth, uint blockWidth, uint blockHeight, uint blockDepth) { if (m_Init && kernelIndex < m_Programs.size()) { cl::Event e; cl_int err = m_Queue.enqueueNDRangeKernel(m_Programs[kernelIndex].m_Kernel, cl::NullRange, cl::NDRange(totalGridWidth, totalGridHeight, totalGridDepth), cl::NDRange(blockWidth, blockHeight, blockDepth), nullptr, &e); e.wait(); m_Queue.finish(); return CheckCL(err, "cl::CommandQueue::enqueueNDRangeKernel()"); } return false; } /// /// Get device information for the specified field. /// Template argument expected to be cl_ulong, cl_uint or cl_int; /// /// The device field/feature to query /// The value of the field template T OpenCLWrapper::GetInfo(size_t platform, size_t device, cl_device_info name) const { T val; if (platform < m_Devices.size() && device < m_Devices[platform].size()) m_Devices[platform][device].getInfo(name, &val); return val; } /// /// Get the platform name at the specified index. /// /// The platform index to get the name of /// The platform name if found, else empty string string OpenCLWrapper::PlatformName(size_t platform) { if (platform < m_Platforms.size()) return m_Platforms[platform].getInfo(nullptr) + " " + m_Platforms[platform].getInfo(nullptr) + " " + m_Platforms[platform].getInfo(nullptr); else return ""; } /// /// Get all available platform names on the system as a vector of strings. /// /// All available platform names on the system as a vector of strings vector OpenCLWrapper::PlatformNames() { vector platforms; platforms.reserve(m_Platforms.size()); for (size_t i = 0; i < m_Platforms.size(); i++) platforms.push_back(PlatformName(i)); return platforms; } /// /// Get the device name at the specified index on the platform /// at the specified index. /// /// The platform index of the device /// The device index /// The name of the device if found, else empty string string OpenCLWrapper::DeviceName(size_t platform, size_t device) { string s; if (platform < m_Platforms.size() && platform < m_Devices.size()) if (device < m_Devices[platform].size()) s = m_Devices[platform][device].getInfo(nullptr) + " " + m_Devices[platform][device].getInfo(nullptr);// + " " + m_Devices[platform][device].getInfo(); return s; } /// /// Get all available device names on the platform at the specified index as a vector of strings. /// /// The platform index of the devices to query /// All available device names on the platform at the specified index as a vector of strings vector OpenCLWrapper::DeviceNames(size_t platform) { uint i = 0; string s; vector devices; do { s = DeviceName(platform, i); if (s != "") devices.push_back(s); i++; } while (s != ""); return devices; } /// /// Get all availabe device and platform names as one contiguous string. /// /// A string with all available device and platform names string OpenCLWrapper::DeviceAndPlatformNames() { ostringstream os; vector deviceNames; for (size_t platform = 0; platform < m_Platforms.size(); platform++) { os << PlatformName(platform) << endl; deviceNames = DeviceNames(platform); for (size_t device = 0; device < m_Devices[platform].size(); device++) os << "\t" << deviceNames[device] << endl; } return os.str(); } /// /// Get all information about the currently used device. /// /// A string with all information about the currently used device string OpenCLWrapper::DumpInfo() { ostringstream os; vector sizes; os.imbue(std::locale("")); for (size_t platform = 0; platform < m_Platforms.size(); platform++) { os << "Platform " << platform << ": " << PlatformName(platform) << endl; for (size_t device = 0; device < m_Devices[platform].size(); device++) { os << "Device " << device << ": " << DeviceName(platform, device) << endl; os << "CL_DEVICE_OPENCL_C_VERSION: " << GetInfo (platform, device, CL_DEVICE_OPENCL_C_VERSION) << endl; os << "CL_DEVICE_LOCAL_MEM_SIZE: " << GetInfo(platform, device, CL_DEVICE_LOCAL_MEM_SIZE) << endl; os << "CL_DEVICE_LOCAL_MEM_TYPE: " << GetInfo (platform, device, CL_DEVICE_LOCAL_MEM_TYPE) << endl; os << "CL_DEVICE_MAX_COMPUTE_UNITS: " << GetInfo (platform, device, CL_DEVICE_MAX_COMPUTE_UNITS) << endl; os << "CL_DEVICE_MAX_READ_IMAGE_ARGS: " << GetInfo (platform, device, CL_DEVICE_MAX_READ_IMAGE_ARGS) << endl; os << "CL_DEVICE_MAX_WRITE_IMAGE_ARGS: " << GetInfo (platform, device, CL_DEVICE_MAX_WRITE_IMAGE_ARGS) << endl; os << "CL_DEVICE_MAX_MEM_ALLOC_SIZE: " << GetInfo(platform, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE) << endl; os << "CL_DEVICE_ADDRESS_BITS: " << GetInfo (platform, device, CL_DEVICE_ADDRESS_BITS) << endl; os << "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE: " << GetInfo (platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE) << endl; os << "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: " << GetInfo (platform, device, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE) << endl; os << "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE: " << GetInfo(platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE) << endl; os << "CL_DEVICE_GLOBAL_MEM_SIZE: " << GetInfo(platform, device, CL_DEVICE_GLOBAL_MEM_SIZE) << endl; os << "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE: " << GetInfo(platform, device, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE) << endl; os << "CL_DEVICE_MAX_CONSTANT_ARGS: " << GetInfo (platform, device, CL_DEVICE_MAX_CONSTANT_ARGS) << endl; os << "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: " << GetInfo (platform, device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS) << endl; os << "CL_DEVICE_MAX_WORK_GROUP_SIZE: " << GetInfo<::size_t>(platform, device, CL_DEVICE_MAX_WORK_GROUP_SIZE) << endl; sizes = GetInfo>(platform, device, CL_DEVICE_MAX_WORK_ITEM_SIZES); os << "CL_DEVICE_MAX_WORK_ITEM_SIZES: " << sizes[0] << ", " << sizes[1] << ", " << sizes[2] << endl << endl; if (device != m_Devices[platform].size() - 1 && platform != m_Platforms.size() - 1) os << endl; } os << endl; } return os.str(); } /// /// OpenCL properties, getters only. /// bool OpenCLWrapper::Ok() const { return m_Init; } bool OpenCLWrapper::Shared() const { return m_Shared; } cl::Context OpenCLWrapper::Context() const { return m_Context; } uint OpenCLWrapper::PlatformIndex() const { return m_PlatformIndex; } uint OpenCLWrapper::DeviceIndex() const { return m_DeviceIndex; } size_t OpenCLWrapper::GlobalMemSize() const { return GetInfo(PlatformIndex(), DeviceIndex(), CL_DEVICE_GLOBAL_MEM_SIZE); } uint OpenCLWrapper::LocalMemSize() const { return m_LocalMemSize; } size_t OpenCLWrapper::MaxAllocSize() const { return GetInfo(PlatformIndex(), DeviceIndex(), CL_DEVICE_MAX_MEM_ALLOC_SIZE); } /// /// Makes the even grid dims. /// /// The block w. /// The block h. /// The grid w. /// The grid h. void OpenCLWrapper::MakeEvenGridDims(uint blockW, uint blockH, uint& gridW, uint& gridH) { if (gridW % blockW != 0) gridW += (blockW - (gridW % blockW)); if (gridH % blockH != 0) gridH += (blockH - (gridH % blockH)); } /// /// Create a context that is optionall shared with OpenGL. /// /// True if shared with OpenGL, else not shared. /// True if success, else false. bool OpenCLWrapper::CreateContext(bool shared) { cl_int err; if (shared) { //Define OS-specific context properties and create the OpenCL context. #if defined (__APPLE__) || defined(MACOSX) CGLContextObj kCGLContext = CGLGetCurrentContext(); CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext); cl_context_properties props[] = { CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, 0 }; m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);//May need to tinker with this on Mac. #else #if defined WIN32 cl_context_properties props[] = { CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), CL_CONTEXT_PLATFORM, reinterpret_cast((m_Platforms[m_PlatformIndex])()), 0 }; m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err); #else cl_context_properties props[] = { CL_GL_CONTEXT_KHR, cl_context_properties(glXGetCurrentContext()), CL_GLX_DISPLAY_KHR, cl_context_properties(glXGetCurrentDisplay()), CL_CONTEXT_PLATFORM, reinterpret_cast((m_Platforms[m_PlatformIndex])()), 0 }; m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err); #endif #endif } else { cl_context_properties props[3] = { CL_CONTEXT_PLATFORM, reinterpret_cast((m_Platforms[m_PlatformIndex])()), 0 }; m_Context = cl::Context(CL_DEVICE_TYPE_ALL, props, nullptr, nullptr, &err); } return CheckCL(err, "cl::Context()"); } /// /// Create an Spk object created by compiling the program arguments passed in. /// /// The name of the program /// The source of the program /// The name of the entry point kernel function in the program /// The Spk object to store the resulting compiled program in /// True if success, else false. bool OpenCLWrapper::CreateSPK(const string& name, const string& program, const string& entryPoint, Spk& spk, bool doublePrecision) { if (m_Init) { cl_int err; spk.m_Name = name; spk.m_Source = cl::Program::Sources(1, std::make_pair(program.c_str(), program.length() + 1)); spk.m_Program = cl::Program(m_Context, spk.m_Source); if (doublePrecision) err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable");//Tinker with other options later. else err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-no-signed-zeros -cl-single-precision-constant"); //err = spk.m_Program.build(m_DeviceVec, "-cl-single-precision-constant"); //err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-single-precision-constant"); //err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-no-signed-zeros -cl-fast-relaxed-math -cl-single-precision-constant");//This can cause some rounding. //err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-single-precision-constant"); if (CheckCL(err, "cl::Program::build()")) { //Building of program is ok, now create kernel with the specified entry point. spk.m_Kernel = cl::Kernel(spk.m_Program, entryPoint.c_str(), &err); if (CheckCL(err, "cl::Kernel()")) return true;//Everything is ok. } } return false; } /// /// Check an OpenCL return value for errors. /// /// The error code to inspect /// A description of where the value was gotten from /// True if success, else false. bool OpenCLWrapper::CheckCL(cl_int err, const char* name) { if (err != CL_SUCCESS) { ostringstream ss; ss << "ERROR: " << ErrorToStringCL(err) << " in " << name << "." << std::endl; m_ErrorReport.push_back(ss.str()); } return err == CL_SUCCESS; } /// /// Translate an OpenCL error code into a human readable string. /// /// The error code to translate /// A human readable description of the error passed in std::string OpenCLWrapper::ErrorToStringCL(cl_int err) { switch (err) { case CL_SUCCESS: return "Success"; case CL_DEVICE_NOT_FOUND: return "Device not found"; case CL_DEVICE_NOT_AVAILABLE: return "Device not available"; case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available"; case CL_MEM_OBJECT_ALLOCATION_FAILURE: return "Memory object allocation failure"; case CL_OUT_OF_RESOURCES: return "Out of resources"; case CL_OUT_OF_HOST_MEMORY: return "Out of host memory"; case CL_PROFILING_INFO_NOT_AVAILABLE: return "Profiling information not available"; case CL_MEM_COPY_OVERLAP: return "Memory copy overlap"; case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch"; case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported"; case CL_BUILD_PROGRAM_FAILURE: return "Program build failure"; case CL_MAP_FAILURE: return "Map failure"; case CL_MISALIGNED_SUB_BUFFER_OFFSET: return "Misaligned sub buffer offset"; case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: return "Exec status error for events in wait list"; case CL_INVALID_VALUE: return "Invalid value"; case CL_INVALID_DEVICE_TYPE: return "Invalid device type"; case CL_INVALID_PLATFORM: return "Invalid platform"; case CL_INVALID_DEVICE: return "Invalid device"; case CL_INVALID_CONTEXT: return "Invalid context"; case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties"; case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue"; case CL_INVALID_HOST_PTR: return "Invalid host pointer"; case CL_INVALID_MEM_OBJECT: return "Invalid memory object"; case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return "Invalid image format descriptor"; case CL_INVALID_IMAGE_SIZE: return "Invalid image size"; case CL_INVALID_SAMPLER: return "Invalid sampler"; case CL_INVALID_BINARY: return "Invalid binary"; case CL_INVALID_BUILD_OPTIONS: return "Invalid build options"; case CL_INVALID_PROGRAM: return "Invalid program"; case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable"; case CL_INVALID_KERNEL_NAME: return "Invalid kernel name"; case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition"; case CL_INVALID_KERNEL: return "Invalid kernel"; case CL_INVALID_ARG_INDEX: return "Invalid argument index"; case CL_INVALID_ARG_VALUE: return "Invalid argument value"; case CL_INVALID_ARG_SIZE: return "Invalid argument size"; case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments"; case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension"; case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size"; case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size"; case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset"; case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list"; case CL_INVALID_EVENT: return "Invalid event"; case CL_INVALID_OPERATION: return "Invalid operation"; case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object"; case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size"; case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level"; case CL_INVALID_GLOBAL_WORK_SIZE: return "Invalid global work size"; case CL_INVALID_PROPERTY: return "Invalid property"; default: { ostringstream ss; ss << " " << err; return ss.str(); } } } }