2023-04-25 19:59:54 -04:00
# include "EmberCLPch.h"
# include "OpenCLWrapper.h"
namespace EmberCLns
{
/// <summary>
/// Constructor that sets everything to an uninitialized state.
/// No OpenCL setup is done here other than what's done in the
/// global OpenCLInfo object. The caller must explicitly do it.
/// </summary>
OpenCLWrapper : : OpenCLWrapper ( )
{
//Pre-allocate some space to avoid temporary copying.
m_Programs . reserve ( 4 ) ;
m_Buffers . reserve ( 4 ) ;
m_Images . reserve ( 4 ) ;
m_GLImages . reserve ( 4 ) ;
}
/// <summary>
/// Initialize the specified platform and device.
/// This can be shared with OpenGL.
/// </summary>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : Init ( size_t platformIndex , size_t deviceIndex , bool shared )
{
cl_int err ;
auto & platforms = m_Info - > Platforms ( ) ;
auto & devices = m_Info - > Devices ( ) ;
m_Init = false ;
ClearErrorReport ( ) ;
if ( m_Info - > Ok ( ) )
{
if ( platformIndex < platforms . size ( ) & & platformIndex < devices . size ( ) )
{
cl : : Context context ;
if ( m_Info - > CreateContext ( platforms [ platformIndex ] , context , shared ) ) //Platform index is within range, now do context.
{
if ( deviceIndex < devices [ platformIndex ] . size ( ) ) //Context is ok, now do device.
{
auto q = cl : : CommandQueue ( context , devices [ platformIndex ] [ deviceIndex ] , 0 , & err ) ; //At least one GPU device is present, so create a command queue.
if ( m_Info - > CheckCL ( err , " cl::CommandQueue() " ) ) //Everything was successful so assign temporaries to members.
{
m_Platform = platforms [ platformIndex ] ;
m_Device = devices [ platformIndex ] [ deviceIndex ] ;
m_Context = context ;
m_Queue = q ;
m_PlatformIndex = platformIndex ;
m_DeviceIndex = deviceIndex ;
m_DeviceVec . clear ( ) ;
m_DeviceVec . push_back ( m_Device ) ;
m_LocalMemSize = size_t ( m_Info - > GetInfo < cl_ulong > ( m_PlatformIndex , m_DeviceIndex , CL_DEVICE_LOCAL_MEM_SIZE ) ) ;
m_GlobalMemSize = size_t ( m_Info - > GetInfo < cl_ulong > ( m_PlatformIndex , m_DeviceIndex , CL_DEVICE_GLOBAL_MEM_SIZE ) ) ;
m_MaxAllocSize = size_t ( m_Info - > GetInfo < cl_ulong > ( m_PlatformIndex , m_DeviceIndex , CL_DEVICE_MAX_MEM_ALLOC_SIZE ) ) ;
m_Shared = shared ;
m_Init = true ; //Command queue is ok, it's now ok to begin building and running programs.
}
}
}
}
}
return m_Init ;
}
/// <summary>
/// Compile and add the program, using the specified entry point.
/// If a program with the same name already exists then it will be replaced.
/// </summary>
/// <param name="name">The name of the program</param>
/// <param name="program">The program source</param>
/// <param name="entryPoint">The name of the entry point kernel function in the program</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : AddProgram ( const string & name , const string & program , const string & entryPoint , bool doublePrecision )
{
Spk spk ;
if ( CreateSPK ( name , program , entryPoint , spk , doublePrecision ) )
{
for ( auto & p : m_Programs )
{
if ( name = = p . m_Name )
{
p = spk ;
return true ;
}
}
//Nothing was found, so add.
m_Programs . push_back ( spk ) ;
return true ;
}
return false ;
}
/// <summary>
/// Clear the programs.
/// </summary>
void OpenCLWrapper : : ClearPrograms ( )
{
m_Programs . clear ( ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="name">The name of the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <param name="flags">The buffer flags. Default: CL_MEM_READ_WRITE.</param>
/// <returns>True if success, else false.</returns>
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 ( ! m_Info - > 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 , size_t ( 1 ) , nullptr , & err ) , " emptybuffer " ) ; //First clear out the original so the two don't exist in memory at once.
char ch = 0 ;
if ( ! WriteBuffer ( " emptybuffer " , & ch , 1 ) ) //Write the dummy buffer at least once because OpenCL seems to do a lazy instantiation of buffers.
return false ;
cl : : Buffer buff ( m_Context , flags , size , nullptr , & err ) ; //Create the new buffer.
if ( ! m_Info - > 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 ; //Either operation succeeded.
}
return false ;
}
/// <summary>
/// Add a host side buffer with the specified name, size and host data pointer.
/// Three possible actions to take:
/// Buffer didn't exist, so create and add.
/// Buffer existed, but was a different size or pointer, replace.
/// Buffer existed with the same size and pointer, do nothing.
/// </summary>
/// <param name="name">The name of the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <param name="data">The pointer to the beginning of the host side data.</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : AddHostBuffer ( const string & name , size_t size , void * data )
{
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 , CL_MEM_USE_HOST_PTR , size , data , & err ) ;
if ( ! m_Info - > 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...
data ! = m_Buffers [ bufferIndex ] . m_Buffer . getInfo < CL_MEM_HOST_PTR > ( nullptr ) ) //...or addresses were different.
{
m_Buffers [ bufferIndex ] = NamedBuffer ( cl : : Buffer ( m_Context , CL_MEM_USE_HOST_PTR , size_t ( 1 ) , data , & err ) , " emptybuffer " ) ; //First clear out the original so the two don't exist in memory at once.
char ch = 0 ;
if ( ! WriteBuffer ( " emptybuffer " , & ch , 1 ) ) //Write the dummy buffer at least once because OpenCL seems to do a lazy instantiation of buffers.
return false ;
cl : : Buffer buff ( m_Context , CL_MEM_USE_HOST_PTR , size , data , & err ) ; //Create the new buffer.
if ( ! m_Info - > 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 and pointers were the same, take no action.
return true ; //Either operation succeeded.
}
return false ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="name">The name of the buffer</param>
/// <param name="data">A pointer to the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <param name="flags">The buffer flags. Default: CL_MEM_READ_WRITE.</param>
/// <returns>True if success, else false.</returns>
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 ;
}
/// <summary>
/// Write data to an existing buffer with the specified name.
/// </summary>
/// <param name="name">The name of the buffer</param>
/// <param name="data">A pointer to the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : WriteBuffer ( const string & name , void * data , size_t size )
{
int bufferIndex = FindBufferIndex ( name ) ;
return bufferIndex ! = - 1 ? WriteBuffer ( bufferIndex , data , size ) : false ;
}
/// <summary>
/// Write data to an existing buffer at the specified index.
/// </summary>
/// <param name="bufferIndex">The index of the buffer</param>
/// <param name="data">A pointer to the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : WriteBuffer ( size_t bufferIndex , void * data , size_t size )
{
if ( m_Init & & ( bufferIndex < m_Buffers . size ( ) ) & & ( GetBufferSize ( bufferIndex ) = = size ) )
{
cl : : Event e ;
const auto err = m_Queue . enqueueWriteBuffer ( m_Buffers [ bufferIndex ] . m_Buffer , CL_TRUE , 0 , size , data , nullptr , & e ) ;
e . wait ( ) ;
m_Queue . finish ( ) ;
if ( m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueWriteBuffer() " ) )
return true ;
}
return false ;
}
/// <summary>
/// Read data from an existing buffer with the specified name.
/// </summary>
/// <param name="name">The name of the buffer</param>
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : ReadBuffer ( const string & name , void * data , size_t size )
{
int bufferIndex = FindBufferIndex ( name ) ;
return bufferIndex ! = - 1 ? ReadBuffer ( bufferIndex , data , size ) : false ;
}
/// <summary>
/// Read data from an existing buffer at the specified index.
/// </summary>
/// <param name="bufferIndex">The index of the buffer</param>
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : ReadBuffer ( size_t bufferIndex , void * data , size_t size )
{
if ( m_Init & & ( bufferIndex < m_Buffers . size ( ) ) & & ( GetBufferSize ( bufferIndex ) = = size ) )
{
cl : : Event e ;
const auto err = m_Queue . enqueueReadBuffer ( m_Buffers [ bufferIndex ] . m_Buffer , CL_TRUE , 0 , size , data , nullptr , & e ) ;
e . wait ( ) ;
m_Queue . finish ( ) ;
if ( m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueReadBuffer() " ) )
return true ;
}
return false ;
}
/// <summary>
/// Find the index of the buffer with the specified name.
/// </summary>
/// <param name="name">The name of the buffer to search for</param>
/// <returns>The index if found, else -1.</returns>
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 ;
}
/// <summary>
/// Get the size of the buffer with the specified name.
/// </summary>
/// <param name="name">The name of the buffer to search for</param>
/// <returns>The size of the buffer if found, else 0.</returns>
size_t OpenCLWrapper : : GetBufferSize ( const string & name )
{
int bufferIndex = FindBufferIndex ( name ) ;
return bufferIndex ! = - 1 ? GetBufferSize ( bufferIndex ) : 0 ;
}
/// <summary>
/// Get the size of the buffer at the specified index.
/// </summary>
/// <param name="name">The index of the buffer to get the size of</param>
/// <returns>The size of the buffer if found, else 0.</returns>
size_t OpenCLWrapper : : GetBufferSize ( size_t bufferIndex )
{
if ( m_Init & & ( bufferIndex < m_Buffers . size ( ) ) )
return m_Buffers [ bufferIndex ] . m_Buffer . getInfo < CL_MEM_SIZE > ( nullptr ) ;
return 0 ;
}
/// <summary>
/// Clear all buffers.
/// </summary>
void OpenCLWrapper : : ClearBuffers ( )
{
m_Buffers . clear ( ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="name">The name of the image to add/replace</param>
/// <param name="flags">The memory flags</param>
/// <param name="format">The image format</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="data">The image data. Default: NULL.</param>
/// <param name="shared">True if shared with an OpenGL texture, else false. Default: false.</param>
/// <param name="texName">The texture ID of the shared OpenGL texture if shared. Default: 0.</param>
/// <returns>True if success, else false.</returns>
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 )
{
cl : : ImageGL imageGL ( m_Context , flags , GL_TEXTURE_2D , 0 , texName , & err ) ;
NamedImage2DGL namedImageGL ( imageGL , name ) ;
if ( m_Info - > CheckCL ( err , " cl::ImageGL() " ) )
{
m_GLImages . push_back ( namedImageGL ) ;
imageIndex = int ( m_GLImages . size ( ) ) - 1 ;
}
else
return false ;
}
else
{
NamedImage2D namedImage ( cl : : Image2D ( m_Context , flags , format , width , height , row_pitch , data , & err ) , name ) ;
if ( m_Info - > CheckCL ( err , " cl::Image2D() " ) )
{
m_Images . push_back ( namedImage ) ;
imageIndex = int ( m_Images . size ( ) ) - 1 ;
}
else
return false ;
}
}
else //It did exist, so create new if sizes are different. Write if data is not NULL.
{
if ( shared )
{
cl : : ImageGL imageGL = m_GLImages [ imageIndex ] . m_Image ;
if ( ! CompareImageParams ( imageGL , flags , format , width , height , row_pitch ) )
{
NamedImage2DGL namedImageGL ( cl : : ImageGL ( m_Context , flags , GL_TEXTURE_2D , 0 , texName , & err ) , name ) ; //Sizes are different, so create new.
if ( m_Info - > CheckCL ( err , " cl::ImageGL() " ) )
m_GLImages [ imageIndex ] = namedImageGL ;
else
return false ;
}
}
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 ( m_Info - > CheckCL ( err , " cl::Image2D() " ) )
m_Images [ imageIndex ] = namedImage ;
else
return false ;
}
}
}
if ( data )
return WriteImage2D ( imageIndex , shared , 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 ;
}
/// <summary>
/// Write data to an existing 2D image at the specified index.
/// </summary>
/// <param name="index">The index of the image</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="data">The image data</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : WriteImage2D ( size_t index , bool shared , : : size_t width , : : size_t height , : : size_t row_pitch , void * data )
{
if ( m_Init )
{
cl_int err ;
2023-07-19 16:09:46 -04:00
cl : : Event e ;
2023-07-18 21:50:43 -04:00
# ifdef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
cl : : size_t < 3 > origin , region ;
2023-07-18 21:50:43 -04:00
# else
2023-07-19 16:09:46 -04:00
cl : : array < cl : : size_type , 3 > origin , region ;
2023-07-18 21:50:43 -04:00
# endif
2023-04-25 19:59:54 -04:00
origin [ 0 ] = 0 ;
origin [ 1 ] = 0 ;
origin [ 2 ] = 0 ;
region [ 0 ] = width ;
region [ 1 ] = height ;
region [ 2 ] = 1 ;
if ( shared & & index < m_GLImages . size ( ) )
{
cl : : ImageGL 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 m_Info - > 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 m_Info - > CheckCL ( err , " cl::enqueueWriteImage() " ) ;
}
}
return false ;
}
/// <summary>
/// Read data from an existing 2D image with the specified name.
/// </summary>
/// <param name="name">The name of the image</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <returns>True if success, else false.</returns>
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 ;
}
/// <summary>
/// Read data from an existing 2D image at the specified index.
/// </summary>
/// <param name="name">The name of the image</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : ReadImage ( size_t imageIndex , : : size_t width , : : size_t height , : : size_t row_pitch , bool shared , void * data )
{
if ( m_Init )
{
cl_int err ;
cl : : Event e ;
2023-07-18 21:50:43 -04:00
# ifdef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
cl : : size_t < 3 > origin , region ;
2023-07-18 21:50:43 -04:00
# else
2023-07-19 16:09:46 -04:00
cl : : array < cl : : size_type , 3 > origin , region ;
2023-07-18 21:50:43 -04:00
# endif
2023-04-25 19:59:54 -04:00
origin [ 0 ] = 0 ;
origin [ 1 ] = 0 ;
origin [ 2 ] = 0 ;
region [ 0 ] = width ;
region [ 1 ] = height ;
region [ 2 ] = 1 ;
if ( shared & & imageIndex < m_GLImages . size ( ) )
{
cl : : ImageGL 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 m_Info - > 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 m_Info - > CheckCL ( err , " cl::enqueueReadImage() " ) ;
}
}
return false ;
}
/// <summary>
/// Find the index of the 2D image with the specified name.
/// </summary>
/// <param name="name">The name of the image to search for</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <returns>The index if found, else -1.</returns>
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 ;
}
/// <summary>
/// Get the size of the 2D image with the specified name.
/// </summary>
/// <param name="name">The name of the image to search for</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <returns>The size of the 2D image if found, else 0.</returns>
size_t OpenCLWrapper : : GetImageSize ( const string & name , bool shared )
{
int imageIndex = FindImageIndex ( name , shared ) ;
return GetImageSize ( imageIndex , shared ) ;
}
/// <summary>
/// Get the size of the 2D image at the specified index.
/// </summary>
/// <param name="imageIndex">Index of the image to search for</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <returns>The size of the 2D image if found, else 0.</returns>
size_t OpenCLWrapper : : GetImageSize ( size_t imageIndex , bool shared )
{
size_t size = 0 ;
if ( m_Init )
{
if ( shared & & imageIndex < m_GLImages . size ( ) )
{
vector < cl : : Memory > images ;
images . push_back ( m_GLImages [ imageIndex ] . m_Image ) ;
cl : : ImageGL image = m_GLImages [ imageIndex ] . m_Image ;
if ( EnqueueAcquireGLObjects ( & images ) )
size = image . getImageInfo < CL_IMAGE_WIDTH > ( nullptr ) * image . getImageInfo < CL_IMAGE_HEIGHT > ( nullptr ) * image . getImageInfo < CL_IMAGE_ELEMENT_SIZE > ( 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 < CL_IMAGE_WIDTH > ( nullptr ) * image . getImageInfo < CL_IMAGE_HEIGHT > ( nullptr ) * image . getImageInfo < CL_IMAGE_ELEMENT_SIZE > ( nullptr ) ; //Should pitch be checked here?
}
}
return size ;
}
/// <summary>
/// Compare the passed in image with the specified parameters.
/// </summary>
/// <param name="image">The image to compare</param>
/// <param name="flags">The memory flags to compare (ommitted)</param>
/// <param name="format">The format to compare</param>
/// <param name="width">The width to compare</param>
/// <param name="height">The height to compare</param>
/// <param name="row_pitch">The row_pitch to compare (omitted)</param>
/// <returns>True if all parameters matched, else false.</returns>
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 < CL_IMAGE_FORMAT > ( nullptr ) ;
return ( /*image.getImageInfo<CL_MEM_FLAGS>() == flags &&*/
tempFormat . image_channel_data_type = = format . image_channel_data_type & &
tempFormat . image_channel_order = = format . image_channel_order & &
image . getImageInfo < CL_IMAGE_WIDTH > ( nullptr ) = = width & &
image . getImageInfo < CL_IMAGE_HEIGHT > ( nullptr ) = = height /* &&
image . getImageInfo < CL_IMAGE_ROW_PITCH > ( ) = = row_pitch */ ) ; //Pitch will be (width * bytes per pixel) + padding.
}
/// <summary>
/// Clear all images.
/// </summary>
/// <param name="shared">True to clear shared images, else clear regular images.</param>
void OpenCLWrapper : : ClearImages ( bool shared )
{
if ( shared )
m_GLImages . clear ( ) ;
else
m_Images . clear ( ) ;
}
/// <summary>
/// Create a 2D image and store in the image passed in.
/// </summary>
/// <param name="image2D">The 2D image to store the newly created image in</param>
/// <param name="flags">The memory flags to use</param>
/// <param name="format">The format to use</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="data">The image data. Default: NULL.</param>
/// <returns>True if success, else false.</returns>
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 m_Info - > CheckCL ( err , " cl::Image2D() " ) ;
}
return false ;
}
/// <summary>
/// Create a 2D image shared with an OpenGL texture and store in the image passed in.
/// </summary>
/// <param name="image2DGL">The 2D image to store the newly created image in</param>
/// <param name="flags">The memory flags to use</param>
/// <param name="target">The target</param>
/// <param name="miplevel">The mip map level</param>
/// <param name="texobj">The texture ID of the shared OpenGL texture</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : CreateImage2DGL ( cl : : ImageGL & image2DGL , cl_mem_flags flags , GLenum target , GLint miplevel , GLuint texobj )
{
if ( m_Init )
{
cl_int err ;
image2DGL = cl : : ImageGL ( m_Context ,
flags ,
target ,
miplevel ,
texobj ,
& err ) ;
return m_Info - > CheckCL ( err , " cl::ImageGL() " ) ;
}
return false ;
}
/// <summary>
/// Acquire the shared 2D image with the specified name.
/// </summary>
/// <param name="name">The name of the image to acquire</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : EnqueueAcquireGLObjects ( const string & name )
{
int index = FindImageIndex ( name , true ) ;
if ( index ! = - 1 )
return EnqueueAcquireGLObjects ( m_GLImages [ index ] . m_Image ) ;
return false ;
}
/// <summary>
/// Acquire the shared 2D image.
/// </summary>
/// <param name="image">The image to acquire</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : EnqueueAcquireGLObjects ( cl : : ImageGL & image )
{
if ( m_Init & & m_Shared )
{
vector < cl : : Memory > images ;
images . push_back ( image ) ;
const auto err = m_Queue . enqueueAcquireGLObjects ( & images ) ;
m_Queue . finish ( ) ;
return m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueAcquireGLObjects() " ) ;
}
return false ;
}
/// <summary>
/// Reelease the shared 2D image with the specified name.
/// </summary>
/// <param name="name">The name of the image to release</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : EnqueueReleaseGLObjects ( const string & name )
{
int index = FindImageIndex ( name , true ) ;
if ( index ! = - 1 )
return EnqueueReleaseGLObjects ( m_GLImages [ index ] . m_Image ) ;
return false ;
}
/// <summary>
/// Release the shared 2D image.
/// </summary>
/// <param name="image">The image to release</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : EnqueueReleaseGLObjects ( cl : : ImageGL & image )
{
if ( m_Init & & m_Shared )
{
vector < cl : : Memory > images ;
images . push_back ( image ) ;
const auto err = m_Queue . enqueueReleaseGLObjects ( & images ) ;
m_Queue . finish ( ) ;
return m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueReleaseGLObjects() " ) ;
}
return false ;
}
/// <summary>
/// Acquire a vector of shared OpenGL memory objects.
/// </summary>
/// <param name="memObjects">The memory objects to acquire</param>
/// <returns>True if success, else false.</returns>
2023-07-18 21:50:43 -04:00
# ifdef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
bool OpenCLWrapper : : EnqueueAcquireGLObjects ( const VECTOR_CLASS < cl : : Memory > * memObjects )
2023-07-18 21:50:43 -04:00
# else
2023-07-19 16:09:46 -04:00
bool OpenCLWrapper : : EnqueueAcquireGLObjects ( const cl : : vector < cl : : Memory > * memObjects )
2023-07-18 21:50:43 -04:00
# endif
2023-04-25 19:59:54 -04:00
{
if ( m_Init & & m_Shared )
{
const auto err = m_Queue . enqueueAcquireGLObjects ( memObjects ) ;
m_Queue . finish ( ) ;
return m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueAcquireGLObjects() " ) ;
}
return false ;
}
/// <summary>
/// Release a vector of shared OpenGL memory objects.
/// </summary>
/// <param name="memObjects">The memory objects to release</param>
/// <returns>True if success, else false.</returns>
2023-07-18 21:50:43 -04:00
# ifdef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
bool OpenCLWrapper : : EnqueueReleaseGLObjects ( const VECTOR_CLASS < cl : : Memory > * memObjects )
2023-07-18 21:50:43 -04:00
# else
2023-07-19 16:09:46 -04:00
bool OpenCLWrapper : : EnqueueReleaseGLObjects ( const cl : : vector < cl : : Memory > * memObjects )
2023-07-18 21:50:43 -04:00
# endif
2023-04-25 19:59:54 -04:00
{
if ( m_Init & & m_Shared )
{
const auto err = m_Queue . enqueueReleaseGLObjects ( memObjects ) ;
m_Queue . finish ( ) ;
return m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueReleaseGLObjects() " ) ;
}
return false ;
}
/// <summary>
/// Create a texture sampler.
/// </summary>
/// <param name="sampler">The sampler to store the newly created sampler in</param>
/// <param name="normalizedCoords">True to use normalized coordinates, else don't.</param>
/// <param name="addressingMode">The addressing mode to use</param>
/// <param name="filterMode">The filter mode to use</param>
/// <returns>True if success, else false.</returns>
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 m_Info - > CheckCL ( err , " cl::Sampler() " ) ;
}
/// <summary>
/// Set the argument at the specified index for the kernel at the specified index to be
/// the buffer with the specified name.
/// </summary>
/// <param name="kernelIndex">Index of the kernel</param>
/// <param name="argIndex">Index of the argument</param>
/// <param name="name">The name of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : SetBufferArg ( size_t kernelIndex , cl_uint argIndex , const string & name )
{
int bufferIndex = OpenCLWrapper : : FindBufferIndex ( name ) ;
return bufferIndex ! = - 1 ? SetBufferArg ( kernelIndex , argIndex , bufferIndex ) : false ;
}
/// <summary>
/// Set the argument at the specified index for the kernel at the specified index to be
/// the buffer at the specified index.
/// </summary>
/// <param name="kernelIndex">Index of the kernel</param>
/// <param name="argIndex">Index of the argument</param>
/// <param name="bufferIndex">Index of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : SetBufferArg ( size_t kernelIndex , cl_uint argIndex , size_t bufferIndex )
{
if ( m_Init & & bufferIndex < m_Buffers . size ( ) )
return SetArg < cl : : Buffer > ( kernelIndex , argIndex , m_Buffers [ bufferIndex ] . m_Buffer ) ;
return false ;
}
/// <summary>
/// Set the argument at the specified index for the kernel at the specified index to be
/// the 2D image with the specified name.
/// </summary>
/// <param name="kernelIndex">Index of the kernel</param>
/// <param name="argIndex">Index of the argument</param>
/// <param name="shared">True if shared with an OpenGL texture, else false</param>
/// <param name="name">The name of the 2D image</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : SetImageArg ( size_t kernelIndex , cl_uint argIndex , bool shared , const string & name )
{
if ( m_Init )
{
int imageIndex = FindImageIndex ( name , shared ) ;
return SetImageArg ( kernelIndex , argIndex , shared , imageIndex ) ;
}
return false ;
}
/// <summary>
/// Set the argument at the specified index for the kernel at the specified index to be
/// the 2D image at the specified index.
/// </summary>
/// <param name="kernelIndex">Index of the kernel</param>
/// <param name="argIndex">Index of the argument</param>
/// <param name="shared">True if shared with an OpenGL texture, else false</param>
/// <param name="imageIndex">Index of the 2D image</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : SetImageArg ( size_t kernelIndex , cl_uint argIndex , bool shared , size_t 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 m_Info - > 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 m_Info - > CheckCL ( err , " cl::Kernel::setArg() " ) ;
}
}
return false ;
}
/// <summary>
/// Find the index of the kernel with the specified name.
/// </summary>
/// <param name="name">The name of the kernel to search for</param>
/// <returns>The index if found, else -1.</returns>
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 ;
}
/// <summary>
/// Run the kernel at the specified index, using the specified grid and block dimensions.
/// </summary>
/// <param name="kernelIndex">Index of the kernel to run</param>
/// <param name="totalGridWidth">Total width of the grid</param>
/// <param name="totalGridHeight">Total height of the grid</param>
/// <param name="totalGridDepth">The total depth grid</param>
/// <param name="blockWidth">Width of each block</param>
/// <param name="blockHeight">Height of each block</param>
/// <param name="blockDepth">Depth of each block</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : RunKernel ( size_t kernelIndex , size_t totalGridWidth , size_t totalGridHeight , size_t totalGridDepth ,
size_t blockWidth , size_t blockHeight , size_t blockDepth )
{
if ( m_Init & & kernelIndex < m_Programs . size ( ) )
{
cl : : Event e ;
const auto 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 m_Info - > CheckCL ( err , " cl::CommandQueue::enqueueNDRangeKernel() " ) ;
}
return false ;
}
/// <summary>
/// OpenCL properties, getters only.
/// </summary>
bool OpenCLWrapper : : Ok ( ) const { return m_Init ; }
bool OpenCLWrapper : : Shared ( ) const { return m_Shared ; }
const cl : : Context & OpenCLWrapper : : Context ( ) const { return m_Context ; }
size_t OpenCLWrapper : : PlatformIndex ( ) const { return m_PlatformIndex ; }
size_t OpenCLWrapper : : DeviceIndex ( ) const { return m_DeviceIndex ; }
size_t OpenCLWrapper : : TotalDeviceIndex ( ) const { return m_Info - > TotalDeviceIndex ( m_PlatformIndex , m_DeviceIndex ) ; }
const string & OpenCLWrapper : : DeviceName ( ) const { return m_Info - > DeviceName ( m_PlatformIndex , m_DeviceIndex ) ; }
size_t OpenCLWrapper : : LocalMemSize ( ) const { return m_LocalMemSize ; }
size_t OpenCLWrapper : : GlobalMemSize ( ) const { return m_GlobalMemSize ; }
size_t OpenCLWrapper : : MaxAllocSize ( ) const { return m_MaxAllocSize ; }
/// <summary>
/// Clear the error report for this class as well as the global OpenCLInfo instance.
/// </summary>
void OpenCLWrapper : : ClearErrorReport ( ) noexcept
{
EmberReport : : ClearErrorReport ( ) ;
m_Info - > ClearErrorReport ( ) ;
}
/// <summary>
/// Concatenate and return the error report for this class and the
/// global OpenCLInfo instance as a single string.
/// </summary>
/// <returns>The concatenated error report string</returns>
string OpenCLWrapper : : ErrorReportString ( )
{
auto s = EmberReport : : ErrorReportString ( ) ;
return s + m_Info - > ErrorReportString ( ) ;
}
/// <summary>
/// Concatenate and return the error report for this class and the
/// global OpenCLInfo instance as a vector of strings.
/// </summary>
/// <returns>The concatenated error report vector of strings</returns>
vector < string > OpenCLWrapper : : ErrorReport ( )
{
auto ours = EmberReport : : ErrorReport ( ) ;
auto s = m_Info - > ErrorReport ( ) ;
ours . insert ( ours . end ( ) , s . begin ( ) , s . end ( ) ) ;
return ours ;
}
/// <summary>
/// Make even grid dimensions.
/// The size of the blocks in terms of threads must divide evenly into the total number of threads in the grid.
/// In the case of a remainder, expand the width and height of the grid to the next highest evenly divisible value.
/// Ex:
/// blockW = 5, blockH = 5
/// gridW = 18, gridH = 27
///
/// To make these even:
/// gridW = 20, gridH = 30
/// </summary>
/// <param name="blockW">The width of each block in terms of threads.</param>
/// <param name="blockH">The height of each block in terms of threads.</param>
/// <param name="gridW">The width of the entire grid in terms of threads.</param>
/// <param name="gridH">The width of the entire grid in terms of threads.</param>
void OpenCLWrapper : : MakeEvenGridDims ( size_t blockW , size_t blockH , size_t & gridW , size_t & gridH )
{
if ( gridW % blockW ! = 0 )
gridW + = ( blockW - ( gridW % blockW ) ) ;
if ( gridH % blockH ! = 0 )
gridH + = ( blockH - ( gridH % blockH ) ) ;
}
/// <summary>
/// Create an Spk object created by compiling the program arguments passed in.
/// </summary>
/// <param name="name">The name of the program</param>
/// <param name="program">The source of the program</param>
/// <param name="entryPoint">The name of the entry point kernel function in the program</param>
/// <param name="spk">The Spk object to store the resulting compiled program in</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper : : CreateSPK ( const string & name , const string & program , const string & entryPoint , Spk & spk , bool doublePrecision )
{
if ( m_Init )
{
cl_int err ;
2023-07-18 21:50:43 -04:00
# ifndef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
vector < std : : string > programvec { program } ;
2023-07-18 21:50:43 -04:00
# endif
2023-07-19 16:09:46 -04:00
spk . m_Name = name ;
2023-07-18 21:50:43 -04:00
# ifdef OCL_USE_1_2_V
2023-07-19 16:09:46 -04:00
spk . m_Source = cl : : Program : : Sources ( 1 , std : : make_pair ( program . c_str ( ) , program . length ( ) + 1 ) ) ;
2023-07-18 21:50:43 -04:00
# else
2023-07-19 16:09:46 -04:00
spk . m_Source = cl : : Program : : Sources ( programvec ) ;
2023-07-18 21:50:43 -04:00
# endif
2023-04-25 19:59:54 -04:00
spk . m_Program = cl : : Program ( m_Context , spk . m_Source ) ;
if ( doublePrecision )
err = spk . m_Program . build ( m_DeviceVec , " -cl-mad-enable -cl-no-signed-zeros -cl-denorms-are-zero " ) ; //Tinker with other options later.
else
err = spk . m_Program . build ( m_DeviceVec , " -cl-mad-enable -cl-no-signed-zeros -cl-denorms-are-zero -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-no-signed-zeros -cl-single-precision-constant -cl-denorms-are-zero");
//err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-single-precision-constant");
if ( m_Info - > 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 ( m_Info - > CheckCL ( err , " cl::Kernel() " ) )
return true ; //Everything is ok.
}
else
{
for ( auto & i : m_DeviceVec )
AddToReport ( spk . m_Program . getBuildInfo < CL_PROGRAM_BUILD_LOG > ( i , nullptr ) ) ;
}
}
return false ;
}
}