2010-02-19 10:05:26 -07:00
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
2011-02-13 19:18:03 -07:00
// TODO: Make a more centralized version of this (for now every backend that will use it will create its own context, which is weird). An object maybe?
2010-02-19 10:05:26 -07:00
# include "OpenCL.h"
# include "Common.h"
# include "Timer.h"
namespace OpenCL
{
cl_device_id device_id = NULL ;
cl_context g_context = NULL ;
cl_command_queue g_cmdq = NULL ;
bool g_bInitialized = false ;
bool Initialize ( )
{
if ( g_bInitialized )
return true ;
if ( g_context )
return false ;
2010-06-29 04:23:40 -06:00
int err ; // error code returned from api calls
2010-06-28 20:23:09 -06:00
2010-12-05 09:57:32 -07:00
# ifdef __APPLE__
// If OpenCL is weakly linked and not found, its symbols will be NULL
if ( clGetPlatformIDs = = NULL )
return false ;
# else
2010-06-29 04:23:40 -06:00
clrInit ( ) ;
if ( ! clrHasOpenCL ( ) )
return false ;
2010-06-28 20:23:09 -06:00
# endif
2010-02-19 10:05:26 -07:00
// Connect to a compute device
cl_uint numPlatforms ;
cl_platform_id platform = NULL ;
err = clGetPlatformIDs ( 0 , NULL , & numPlatforms ) ;
if ( err ! = CL_SUCCESS )
{
HandleCLError ( err , " clGetPlatformIDs failed. " ) ;
return false ;
}
2010-07-06 07:14:51 -06:00
if ( 0 < numPlatforms )
2010-02-19 10:05:26 -07:00
{
cl_platform_id * platforms = new cl_platform_id [ numPlatforms ] ;
err = clGetPlatformIDs ( numPlatforms , platforms , NULL ) ;
if ( err ! = CL_SUCCESS )
{
HandleCLError ( err , " clGetPlatformIDs failed. " ) ;
return false ;
}
char pbuf [ 100 ] ;
2010-12-05 02:04:34 -07:00
err = clGetPlatformInfo ( platforms [ 0 ] , CL_PLATFORM_VENDOR , sizeof ( pbuf ) ,
pbuf , NULL ) ;
2010-02-19 10:05:26 -07:00
if ( err ! = CL_SUCCESS )
{
HandleCLError ( err , " clGetPlatformInfo failed. " ) ;
return false ;
}
platform = platforms [ 0 ] ;
delete [ ] platforms ;
}
else
{
PanicAlert ( " No OpenCL platform found. " ) ;
return false ;
}
2010-12-05 02:04:34 -07:00
cl_context_properties cps [ 3 ] = { CL_CONTEXT_PLATFORM ,
( cl_context_properties ) platform , 0 } ;
2010-02-19 10:05:26 -07:00
cl_context_properties * cprops = ( NULL = = platform ) ? NULL : cps ;
2010-05-30 02:08:26 -06:00
err = clGetDeviceIDs ( platform , CL_DEVICE_TYPE_DEFAULT , 1 , & device_id , NULL ) ;
2010-02-19 10:05:26 -07:00
if ( err ! = CL_SUCCESS )
{
HandleCLError ( err , " Failed to create a device group! " ) ;
return false ;
}
2010-07-06 07:14:51 -06:00
// Create a compute context
2010-02-19 10:05:26 -07:00
g_context = clCreateContext ( cprops , 1 , & device_id , NULL , NULL , & err ) ;
if ( ! g_context )
{
HandleCLError ( err , " Failed to create a compute context! " ) ;
return false ;
}
// Create a command commands
g_cmdq = clCreateCommandQueue ( g_context , device_id , 0 , & err ) ;
if ( ! g_cmdq )
{
HandleCLError ( err , " Failed to create a command commands! " ) ;
return false ;
}
g_bInitialized = true ;
return true ;
}
cl_context GetContext ( )
{
return g_context ;
}
cl_command_queue GetCommandQueue ( )
{
return g_cmdq ;
}
cl_program CompileProgram ( const char * Kernel )
{
u32 compileStart = Common : : Timer : : GetTimeMs ( ) ;
2010-12-05 02:04:34 -07:00
cl_int err ;
2010-02-19 10:05:26 -07:00
cl_program program ;
2010-12-05 02:04:34 -07:00
program = clCreateProgramWithSource ( OpenCL : : g_context , 1 ,
( const char * * ) & Kernel , NULL , & err ) ;
2010-12-19 04:03:09 -07:00
2010-02-19 10:05:26 -07:00
if ( ! program )
{
HandleCLError ( err , " Error: Failed to create compute program! " ) ;
}
// Build the program executable
err = clBuildProgram ( program , 0 , NULL , NULL , NULL , NULL ) ;
if ( err ! = CL_SUCCESS ) {
2010-12-17 21:54:16 -07:00
HandleCLError ( err , " Error: failed to build program " ) ;
char * buildlog = NULL ;
size_t buildlog_size = 0 ;
clGetProgramBuildInfo ( program , OpenCL : : device_id , CL_PROGRAM_BUILD_LOG , 0 , NULL , & buildlog_size ) ;
buildlog = new char [ buildlog_size + 1 ] ;
err = clGetProgramBuildInfo ( program , OpenCL : : device_id , CL_PROGRAM_BUILD_LOG , buildlog_size , buildlog , NULL ) ;
buildlog [ buildlog_size ] = 0 ;
if ( err ! = CL_SUCCESS )
{
HandleCLError ( err , " Error: can't get build log " ) ;
} else
{
ERROR_LOG ( COMMON , " Error log: \n %s \n " , buildlog ) ;
}
delete [ ] buildlog ;
2010-02-19 10:05:26 -07:00
return NULL ;
}
2010-12-19 04:03:09 -07:00
INFO_LOG ( COMMON , " OpenCL CompileProgram took %.3f seconds " ,
2010-12-05 02:04:34 -07:00
( float ) ( Common : : Timer : : GetTimeMs ( ) - compileStart ) / 1000.0 ) ;
2010-02-19 10:05:26 -07:00
return program ;
}
cl_kernel CompileKernel ( cl_program program , const char * Function )
{
u32 compileStart = Common : : Timer : : GetTimeMs ( ) ;
int err ;
// Create the compute kernel in the program we wish to run
cl_kernel kernel = clCreateKernel ( program , Function , & err ) ;
if ( ! kernel | | err ! = CL_SUCCESS )
{
2010-07-06 07:14:51 -06:00
char buffer [ 1024 ] ;
sprintf ( buffer , " Failed to create compute kernel '%s' ! " , Function ) ;
2010-06-21 21:10:43 -06:00
HandleCLError ( err , buffer ) ;
2010-02-19 10:05:26 -07:00
return NULL ;
}
2010-12-19 04:03:09 -07:00
INFO_LOG ( COMMON , " OpenCL CompileKernel took %.3f seconds " ,
2010-12-05 02:04:34 -07:00
( float ) ( Common : : Timer : : GetTimeMs ( ) - compileStart ) / 1000.0 ) ;
2010-02-19 10:05:26 -07:00
return kernel ;
}
void Destroy ( )
{
2010-10-23 22:17:36 -06:00
if ( g_cmdq )
{
clReleaseCommandQueue ( g_cmdq ) ;
g_cmdq = NULL ;
}
if ( g_context )
{
clReleaseContext ( g_context ) ;
g_context = NULL ;
}
2010-02-19 10:05:26 -07:00
g_bInitialized = false ;
}
2010-05-26 16:40:06 -06:00
void HandleCLError ( cl_int error , const char * str )
2010-02-19 10:05:26 -07:00
{
2010-05-26 16:40:06 -06:00
const char * name ;
2010-02-19 10:05:26 -07:00
switch ( error )
{
# define CL_ERROR(x) case (x): name = #x; break
CL_ERROR ( CL_SUCCESS ) ;
CL_ERROR ( CL_DEVICE_NOT_FOUND ) ;
CL_ERROR ( CL_DEVICE_NOT_AVAILABLE ) ;
CL_ERROR ( CL_COMPILER_NOT_AVAILABLE ) ;
CL_ERROR ( CL_MEM_OBJECT_ALLOCATION_FAILURE ) ;
CL_ERROR ( CL_OUT_OF_RESOURCES ) ;
CL_ERROR ( CL_OUT_OF_HOST_MEMORY ) ;
CL_ERROR ( CL_PROFILING_INFO_NOT_AVAILABLE ) ;
CL_ERROR ( CL_MEM_COPY_OVERLAP ) ;
CL_ERROR ( CL_IMAGE_FORMAT_MISMATCH ) ;
CL_ERROR ( CL_IMAGE_FORMAT_NOT_SUPPORTED ) ;
CL_ERROR ( CL_BUILD_PROGRAM_FAILURE ) ;
CL_ERROR ( CL_MAP_FAILURE ) ;
CL_ERROR ( CL_INVALID_VALUE ) ;
CL_ERROR ( CL_INVALID_DEVICE_TYPE ) ;
CL_ERROR ( CL_INVALID_PLATFORM ) ;
CL_ERROR ( CL_INVALID_DEVICE ) ;
CL_ERROR ( CL_INVALID_CONTEXT ) ;
CL_ERROR ( CL_INVALID_QUEUE_PROPERTIES ) ;
CL_ERROR ( CL_INVALID_COMMAND_QUEUE ) ;
CL_ERROR ( CL_INVALID_HOST_PTR ) ;
CL_ERROR ( CL_INVALID_MEM_OBJECT ) ;
CL_ERROR ( CL_INVALID_IMAGE_FORMAT_DESCRIPTOR ) ;
CL_ERROR ( CL_INVALID_IMAGE_SIZE ) ;
CL_ERROR ( CL_INVALID_SAMPLER ) ;
CL_ERROR ( CL_INVALID_BINARY ) ;
CL_ERROR ( CL_INVALID_BUILD_OPTIONS ) ;
CL_ERROR ( CL_INVALID_PROGRAM ) ;
CL_ERROR ( CL_INVALID_PROGRAM_EXECUTABLE ) ;
CL_ERROR ( CL_INVALID_KERNEL_NAME ) ;
CL_ERROR ( CL_INVALID_KERNEL_DEFINITION ) ;
CL_ERROR ( CL_INVALID_KERNEL ) ;
CL_ERROR ( CL_INVALID_ARG_INDEX ) ;
CL_ERROR ( CL_INVALID_ARG_VALUE ) ;
CL_ERROR ( CL_INVALID_ARG_SIZE ) ;
CL_ERROR ( CL_INVALID_KERNEL_ARGS ) ;
CL_ERROR ( CL_INVALID_WORK_DIMENSION ) ;
CL_ERROR ( CL_INVALID_WORK_GROUP_SIZE ) ;
CL_ERROR ( CL_INVALID_WORK_ITEM_SIZE ) ;
CL_ERROR ( CL_INVALID_GLOBAL_OFFSET ) ;
CL_ERROR ( CL_INVALID_EVENT_WAIT_LIST ) ;
CL_ERROR ( CL_INVALID_EVENT ) ;
CL_ERROR ( CL_INVALID_OPERATION ) ;
CL_ERROR ( CL_INVALID_GL_OBJECT ) ;
CL_ERROR ( CL_INVALID_BUFFER_SIZE ) ;
CL_ERROR ( CL_INVALID_MIP_LEVEL ) ;
# undef CL_ERROR
default :
name = " Unknown error code " ;
}
if ( ! str )
str = " " ;
ERROR_LOG ( COMMON , " OpenCL error: %s %s (%d) " , str , name , error ) ;
}
}