// // Copyright 2010 Advanced Micro Devices, Inc. All rights reserved. // #ifdef _WIN32 #include #include #include // This is necessary since there are common GL/D3D10 functions #include "cl_d3d9_amd.hpp" #include "cl_d3d10_amd.hpp" #include "cl_d3d11_amd.hpp" #endif //_WIN32 #include #include #include #include #include #include "cl_common.hpp" #include "cl_gl_amd.hpp" #include "device/device.hpp" /* The pixel internal format for DOPP texture defined in gl_enum.h */ #define GL_BGR8_ATI 0x8083 #define GL_BGRA8_ATI 0x8088 #include #include /*! \addtogroup API * @{ * * \addtogroup CL_GL_Interops * * This section discusses OpenCL functions that allow applications to * use OpenGL buffer/texture/render-buffer objects as OpenCL memory * objects. This allows efficient sharing of data between these OpenCL * and OpenGL. The OpenCL API can be used to execute kernels that read * and/or write memory objects that are also an OpenGL buffer object * or a texture. An OpenCL image object can be created from an OpenGL * texture or renderbuffer object. An OpenCL buffer object can be * created from an OpenGL buffer object. An OpenCL memory object can * be created from an OpenGL texture/buffer/render-buffer object or * the default system provided framebuffer if any only if the OpenCL * clContext has been created from a GL clContext. OpenGL contexts are * created using platform specific APIs (EGL, CGL, WGL, GLX are some * of the platform specific APIs that allow applications to create GL * contexts). The appropriate platform API (such as EGL, CGL, WGL, * GLX) will be extended to allow a CL clContext to be created from a * GL clContext. Creating an OpenCL memory object from the default * system provided framebuffer will also require an appropriate * extension to the platform API. Refer to the appropriate platform * API documentation to understand how to create a CL clContext from a * GL clContext and creating a CL memory object from the default * system provided framebuffer. * * @{ * * \addtogroup clCreateFromGLBuffer * * @{ */ /*! \brief Creates an OpenCL buffer object from an OpenGL buffer object. * * \param clContext is a valid OpenCL clContext created from an OpenGL clContext. * * \param clFlags is a bit-field that is used to specify usage information. Only * CL_MEM_READ_ONLY, CL_MEM_WRITE_ONLY and CL_MEM_READ_WRITE can be used. * * \param glBufferName is a GL buffer object name. The GL buffer * object must have a data store created though it does not need to * be initialized. The size of the data store will be used to * determine the size of the CL buffer object. * * \param pCpuMem is a pointer to the buffer data that may already be * allocated by the application. The size of the buffer that pCpuMem points * to must be >= \a size bytes. Passing in a pointer to an already allocated * buffer on the host and using it as a buffer object allows applications to * share data efficiently with kernels and the host. * * \param errcode_ret will return an appropriate error code. If errcode_ret * is NULL, no error code is returned. * * \return valid non-zero OpenCL buffer object and errcode_ret is set * to CL_SUCCESS if the buffer object is created successfully. It * returns a NULL value with one of the following error values * returned in \a errcode_ret: * - CL_INVALID_CONTEXT if \a clContext is not a valid clContext. * - CL_INVALID_VALUE if values specified in \a clFlags are not valid. * - CL_INVALID_GL_OBJECT if glBufferName is not a GL buffer object or is a * GL buffer object but does not have a data store created. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required * by the runtime. * * \version 1.0r29 */ RUNTIME_ENTRY_RET(cl_mem, clCreateFromGLBuffer, ( cl_context context, cl_mem_flags flags, GLuint bufobj, cl_int* errcode_ret)) { cl_mem clMemObj = NULL; if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("invalid parameter \"context\""); return clMemObj; } if (!(((flags & CL_MEM_READ_ONLY) == CL_MEM_READ_ONLY) || ((flags & CL_MEM_WRITE_ONLY) == CL_MEM_WRITE_ONLY) || ((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE))) { *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid parameter \"flags\""); return clMemObj; } return(amd::clCreateFromGLBufferAMD(*as_amd(context), flags, bufobj, errcode_ret)); } RUNTIME_EXIT /*! \brief creates the following: * - an OpenCL 2D image object from an OpenGL 2D texture object * or a single face of an OpenGL cubemap texture object, * - an OpenCL 2D image array object from an OpenGL 2D texture array object, * - an OpenCL 1D image object from an OpenGL 1D texture object, * - an OpenCL 1D image buffer object from an OpenGL texture buffer object, * - an OpenCL 1D image array object from an OpenGL 1D texture array object, * - an OpenCL 3D image object from an OpenGL 3D texture object. * * \param clContext is a valid OpenCL clContext created from an OpenGL clContext. * * \param clFlags is a bit-field that is used to specify usage information. * Only CL_MEM_READ_ONLY, CL_MEM_WRITE_ONLY and CL_MEM_READ_WRITE values * can be used. * * \param texture_target must be GL_TEXTURE_1D, GL_TEXTURE_1D_ARRAY, * GL_TEXTURE_BUFFER, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D, * GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, * GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, * GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, * GL_TEXTURE_CUBE_MAP_NEGATIVE_Z or GL_TEXTURE_RECTANGLE_ARB. * * \param miplevel is the mipmap level to be used. If \a texture_target * is GL_TEXTURE_BUFFER, \a miplevel must be 0. * * \param texture is a GL 1D, 2D, 3D, 1D array, 2D array, cubemap, * rectangle or buffer texture object. * The texture object must be a complete texture as per * OpenGL rules on texture completeness. The texture format and dimensions * defined by OpenGL for the specified miplevel of the texture will be * used to create the OpenCL image memory object. Only GL texture formats * that map to appropriate image channel order and data type can be used * to create the the OpenCL image memory object. * * \param errcode_ret will return an appropriate error code. If \a * errcode_ret is NULL, no error code is returned. * * \return A valid non-zero OpenCL image object and \a errcode_ret is set to * CL_SUCCESS if the image object is created successfully. It returns a NULL value * with one of the following error values returned in \a errcode_ret: * - CL_INVALID_CONTEXT if \a clContext is not a valid clContext or was not * created from a GL clContext. * - CL_INVALID_VALUE if values specified in \a clFlags are not valid. * - CL_INVALID_MIP_LEVEL if \a miplevel is not a valid mip-level for \a texture. * - CL_INVALID_GL_OBJECT if \a texture is not an appropriate GL 2D texture, * cubemap or texture rectangle. * - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR if the OpenGL texture format does not * map to an appropriate OpenCL image format. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required * by the runtime. * * \version 1.2r07 */ RUNTIME_ENTRY_RET(cl_mem, clCreateFromGLTexture, ( cl_context context, cl_mem_flags flags, GLenum texture_target, GLint miplevel, GLuint texture, cl_int* errcode_ret)) { cl_mem clMemObj = NULL; if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("invalid parameter \"context\""); return clMemObj; } if (!(((flags & CL_MEM_READ_ONLY) == CL_MEM_READ_ONLY) || ((flags & CL_MEM_WRITE_ONLY) == CL_MEM_WRITE_ONLY) || ((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE))) { *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid parameter \"flags\""); return clMemObj; } const std::vector& devices = as_amd(context)->devices(); bool supportPass = false; bool sizePass = false; std::vector::const_iterator it; for(it = devices.begin(); it != devices.end(); ++it) { if ((*it)->info().imageSupport_) { supportPass = true; } } if (!supportPass) { *not_null(errcode_ret) = CL_INVALID_OPERATION; LogWarning("there are no devices in context to support images"); return static_cast(0); } return amd::clCreateFromGLTextureAMD(*as_amd(context), flags, texture_target, miplevel, texture, errcode_ret); } RUNTIME_EXIT /*! @} * \addtogroup clCreateFromGLTexture2D * @{ */ /*! \brief Create an OpenCL 2D image object from an OpenGL 2D texture object. * * \param clContext is a valid OpenCL clContext created from an OpenGL clContext. * * \param clFlags is a bit-field that is used to specify usage information. * Only CL_MEM_READ_ONLY, CL_MEM_WRITE_ONLY and CL_MEM_READ_WRITE values * can be used. * * \param target must be GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, * GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, * GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, * GL_TEXTURE_CUBE_MAP_NEGATIVE_Z or GL_TEXTURE_RECTANGLE_ARB. * * \param miplevel is the mipmap level to be used. * * \param texture is a GL 2D texture, cubemap or texture rectangle * object name. The texture object must be a complete texture as per * OpenGL rules on texture completeness. The \a texture format and * dimensions specified using appropriate glTexImage2D call for \a * miplevel will be used to create the 2D image object. Only GL * texture formats that map to appropriate image channel order and * data type can be used to create the 2D image object. * * \param errcode_ret will return an appropriate error code. If \a * errcode_ret is NULL, no error code is returned. * * \return A valid non-zero OpenCL image object and \a errcode_ret is set to * CL_SUCCESS if the image object is created successfully. It returns a NULL value * with one of the following error values returned in \a errcode_ret: * - CL_INVALID_CONTEXT if \a clContext is not a valid clContext or was not * created from a GL clContext. * - CL_INVALID_VALUE if values specified in \a clFlags are not valid. * - CL_INVALID_MIP_LEVEL if \a miplevel is not a valid mip-level for \a texture. * - CL_INVALID_GL_OBJECT if \a texture is not an appropriate GL 2D texture, * cubemap or texture rectangle. * - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR if the OpenGL texture format does not * map to an appropriate OpenCL image format. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required * by the runtime. * * \version 1.0r29 */ RUNTIME_ENTRY_RET(cl_mem, clCreateFromGLTexture2D, ( cl_context context, cl_mem_flags flags, GLenum target, GLint miplevel, GLuint texture, cl_int* errcode_ret)) { cl_mem clMemObj = NULL; if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("invalid parameter \"context\""); return clMemObj; } if (!(((flags & CL_MEM_READ_ONLY) == CL_MEM_READ_ONLY) || ((flags & CL_MEM_WRITE_ONLY) == CL_MEM_WRITE_ONLY) || ((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE))) { *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid parameter \"flags\""); return clMemObj; } const std::vector& devices = as_amd(context)->devices(); bool supportPass = false; bool sizePass = false; std::vector::const_iterator it; for(it = devices.begin(); it != devices.end(); ++it) { if ((*it)->info().imageSupport_) { supportPass = true; } } if (!supportPass) { *not_null(errcode_ret) = CL_INVALID_OPERATION; LogWarning("there are no devices in context to support images"); return static_cast(0); } return amd::clCreateFromGLTextureAMD(*as_amd(context), flags, target, miplevel, texture, errcode_ret); } RUNTIME_EXIT /*! @} * \addtogroup clCreateFromGLTexture3D * @{ */ /*! \brief Create an OpenCL 3D image object from an OpenGL 3D texture object. * * \param clContext is a valid OpenCL clContext created from an OpenGL clContext. * * \param clFlags is a bit-field that is used to specify usage information. * Only CL_MEM_READ_ONLY, CL_MEM_WRITE_ONLY and CL_MEM_READ_WRITE values * can be used. * * \param target must be GL_TEXTURE_3D. * * \param miplevel is the mipmap level to be used. * * \param texture is a GL 3D texture object [name]. * The texture object must be a complete texture as per OpenGL rules on texture * completeness. The \a texture format and dimensions specified using appropriate * glTexImage3D call for \a miplevel will be used to create the 3D image object. * Only GL texture formats that map to appropriate image channel order and * data type can be used to create the 3D image object. * * \param errcode_ret will return an appropriate error code. If \a errcode_ret * is NULL, no error code is returned. * * \return A valid non-zero OpenCL image object and \a errcode_ret is set to * CL_SUCCESS if the image object is created successfully. It returns a NULL value * with one of the following error values returned in \a errcode_ret: * - CL_INVALID_CONTEXT if \a clContext is not a valid clContext or was not * created from a GL clContext. * - CL_INVALID_VALUE if values specified in \a clFlags are not valid. * - CL_INVALID_MIP_LEVEL if \a miplevel is not a valid mip-level for \a texture. * - CL_INVALID_GL_OBJECT if \a texture is not an GL 3D texture. * - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR if the OpenGL texture format does not * map to an appropriate OpenCL image format. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required * by the runtime. * * \version 1.0r29 */ RUNTIME_ENTRY_RET(cl_mem, clCreateFromGLTexture3D, ( cl_context context, cl_mem_flags flags, GLenum target, GLint miplevel, GLuint texture, cl_int* errcode_ret)) { cl_mem clMemObj = NULL; if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("invalid parameter \"context\""); return clMemObj; } if (!(((flags & CL_MEM_READ_ONLY) == CL_MEM_READ_ONLY) || ((flags & CL_MEM_WRITE_ONLY) == CL_MEM_WRITE_ONLY) || ((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE))) { *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid parameter \"flags\""); return clMemObj; } const std::vector& devices = as_amd(context)->devices(); bool supportPass = false; bool sizePass = false; std::vector::const_iterator it; for(it = devices.begin(); it != devices.end(); ++it) { if ((*it)->info().imageSupport_) { supportPass = true; } } if (!supportPass) { *not_null(errcode_ret) = CL_INVALID_OPERATION; LogWarning("there are no devices in context to support images"); return static_cast(0); } return amd::clCreateFromGLTextureAMD(*as_amd(context), flags, target, miplevel, texture, errcode_ret); } RUNTIME_EXIT /*! @} * \addtogroup clCreateFromGLRenderbuffer * @{ */ /*! \brief Create an OpenCL 2D image object from an OpenGL renderbuffer object. * * \param clContext is a valid OpenCL clContext created from an OpenGL clContext. * * \param clFlags is a bit-field that is used to specify usage information. * Only CL_MEM_READ_ONLY, CL_MEM_WRITE_ONLY and CL_MEM_READ_WRITE values * can be used. * * \param renderbuffer is a GL renderbuffer object name. The renderbuffer * storage must be specified before the image object can be created. Only * GL renderbuffer formats that map to appropriate image channel order and * data type can be used to create the 2D image object. * * \param errcode_ret will return an appropriate error code. If \a errcode_ret * is NULL, no error code is returned. * * \return A valid non-zero OpenCL image object and \a errcode_ret is set * to CL_SUCCESS if the image object is created successfully. It returns a * NULL value with one of the following error values returned in \a errcode_ret: * - CL_INVALID_CONTEXT if \a clContext is not a valid clContext or was not * created from a GL clContext. * - CL_INVALID_VALUE if values specified in \a clFlags are not valid. * - CL_INVALID_GL_OBJECT if \a renderbuffer is not an GL renderbuffer object. * - CL_INVALID_IMAGE_FORMAT_DESCRIPTOR if the OpenGL renderbuffer format * does not map to an appropriate OpenCL image format. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required * by the runtime. * * \version 1.0r29 */ RUNTIME_ENTRY_RET(cl_mem, clCreateFromGLRenderbuffer, ( cl_context context, cl_mem_flags flags, GLuint renderbuffer, cl_int* errcode_ret)) { cl_mem clMemObj = NULL; if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("invalid parameter \"context\""); return clMemObj; } if (!(((flags & CL_MEM_READ_ONLY) == CL_MEM_READ_ONLY) || ((flags & CL_MEM_WRITE_ONLY) == CL_MEM_WRITE_ONLY) || ((flags & CL_MEM_READ_WRITE) == CL_MEM_READ_WRITE))) { *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid parameter \"flags\""); return clMemObj; } return(amd::clCreateFromGLRenderbufferAMD(*as_amd(context), flags, renderbuffer, errcode_ret)); } RUNTIME_EXIT /*! @} * \addtogroup clGetGLObjectInfo * @{ */ /*! \brief Query GL object type from a CL memory object. * * \param memobj [is a valid cl_mem object created from a GL object]. * * \param gl_object_type returns the type of GL object attached to memobj * and can be CL_GL_OBJECT_BUFFER, CL_GL_OBJECT_TEXTURE2D, * CL_GL_OBJECT_TEXTURE_RECTANGLE, CL_GL_OBJECT_TEXTURE3D, or * CL_GL_OBJECT_RENDERBUFFER. If \a gl_object_type is NULL, it is ignored. * * \param gl_object_name returns the GL object name used to create memobj. * If \a gl_object_name is NULL, it is ignored. * * \return One of the following values is returned: * - CL_SUCCESS if the call was executed successfully. * - CL_INVALID_MEM_OBJECT if \a memobj is not a valid OpenCL memory object. * - CL_INVALID_GL_OBJECT if there is no GL object associated with \a memobj. * * \version 1.0r29 */ RUNTIME_ENTRY(cl_int, clGetGLObjectInfo, ( cl_mem memobj, cl_gl_object_type* gl_object_type, GLuint* gl_object_name)) { if (!is_valid(memobj)) { LogWarning("\"memobj\" is not a valid cl_mem object"); return CL_INVALID_MEM_OBJECT; } amd::InteropObject* interop = as_amd(memobj)->getInteropObj(); if (NULL == interop) { LogWarning("CL object \"memobj\" is not created from GL object"); return CL_INVALID_GL_OBJECT; } amd::GLObject* glObject = interop->asGLObject(); if (NULL == glObject) { LogWarning("CL object \"memobj\" is not created from GL object"); return CL_INVALID_GL_OBJECT; } cl_int result; cl_gl_object_type clGLType = glObject->getCLGLObjectType(); result = amd::clGetInfo(clGLType, sizeof(cl_gl_object_type), gl_object_type, NULL); GLuint glName = glObject->getGLName(); result |= amd::clGetInfo(glName, sizeof(GLuint), gl_object_name, NULL); return result; } RUNTIME_EXIT /*! @} * \addtogroup clGetGLTextureInfo * @{ */ /*! \brief Query additional information about the GL texture object associated * with \a memobj. * * \param memobj [is a valid cl_mem object created from a GL object]. * * \param param_name specifies what additional information about the GL * texture object associated with \a memobj to query: * - CL_GL_TEXTURE_TARGET (GLenum) to query the \a target argument specified * in clCreateGLTexture2D or clCreateGLTexture3D calls. * - CL_GL_MIPMAP_LEVEL (GLint) to query the \a miplevel argument specified * in clCreateGLTexture2D or clCreateGLTexture3D calls. * * \param param_value is a pointer to memory where the appropriate result * being queried is returned. If \a param_value is NULL, it is ignored. * * \param param_value_size is used to specify the size in bytes of memory * pointed to by \a param_value. This size must be >= size of return type as * described for \a param_name argumnet (GLenum or GLint). * \a param_value_size_ret returns the actual size in bytes of data copied to * \a param_value. If \a param_value_size_ret is NULL, it is ignored * * \return One of the following values is returned: * - CL_SUCCESS if the function is executed successfully. * - CL_INVALID_MEM_OBJECT if \a memobj is not a valid OpenCL memory object. * - CL_INVALID_GL_OBJECT if there is no GL texture object (2D or 3D texture) * associated with \a memobj. * - CL_INVALID_VALUE if \a param_name is not valid, or if size in bytes * specified by \a param_value_size is < size of return type required by * \a param_name and \a param_value is not NULL, or if \a param_value and * \a param_value_size_ret are NULL. * * \version 1.0r29 */ RUNTIME_ENTRY(cl_int, clGetGLTextureInfo, ( cl_mem memobj, cl_gl_texture_info param_name, size_t param_value_size, void* param_value, size_t* param_value_size_ret)) { if (!is_valid(memobj)) { LogWarning("\"memobj\" is not a valid cl_mem object"); return CL_INVALID_MEM_OBJECT; } amd::InteropObject* interop = as_amd(memobj)->getInteropObj(); if (NULL == interop) { LogWarning("CL object \"memobj\" is not created from GL object"); return CL_INVALID_GL_OBJECT; } amd::GLObject* glObject = interop->asGLObject(); if ((NULL == glObject) || (NULL != glObject->asBufferGL())) { LogWarning("CL object \"memobj\" is not created from GL texture"); return CL_INVALID_GL_OBJECT; } switch (param_name) { case CL_GL_TEXTURE_TARGET: { GLenum glTarget = glObject->getGLTarget(); if (glTarget == GL_TEXTURE_CUBE_MAP) { glTarget = glObject->getCubemapFace(); } return amd::clGetInfo( glTarget, param_value_size, param_value, param_value_size_ret); } case CL_GL_MIPMAP_LEVEL: { GLint mipLevel = glObject->getGLMipLevel(); return amd::clGetInfo( mipLevel, param_value_size, param_value, param_value_size_ret); } case CL_GL_NUM_SAMPLES: { GLsizei numSamples = glObject->getNumSamples(); return amd::clGetInfo( numSamples,param_value_size, param_value, param_value_size_ret); } default: LogWarning("Unknown param_name in clGetGLTextureInfoAMD"); break; } return CL_INVALID_VALUE; } RUNTIME_EXIT /*! @} * \addtogroup clEnqueueAcquireExtObjects * @{ */ /*! \brief Acquire OpenCL memory objects that have been created from external * objects (OpenGL, D3D). * * \param command_queue is a valid command-queue. * * \param num_objects is the number of memory objects to be acquired * in \a mem_objects. * * \param mem_objects is a pointer to a list of CL memory objects that refer * to a GL object (buffer/texture/renderbuffer objects or the framebuffer). * * \param event_wait_list specify [is a pointer to] events that need to * complete before this particular command can be executed. * If \a event_wait_list is NULL, then this particular command does not wait * on any event to complete. If \a event_wait_list is NULL, * \a num_events_in_wait_list must be 0. If \a event_wait_list is not NULL, * the list of events pointed to by \a event_wait_list must be valid and * \a num_events_in_wait_list must be greater than 0. The events specified in * \a event_wait_list act as synchronization points. * * \param num_events_in_wait_list specify the number of events in * \a event_wait_list. It must be 0 if \a event_wait_list is NULL. It must be * greater than 0 if \a event_wait_list is not NULL. * * \param event returns an event object that identifies this particular * command and can be used to query or queue a wait for this particular * command to complete. \a event can be NULL in which case it will not be * possible for the application to query the status of this command or queue a * wait for this command to complete. * * \return One of the following values is returned: * - CL_SUCCESS if the function is executed successfully. * - CL_SUCCESS if \a num_objects is 0 and \a mem_objects is NULL; the * function does nothing. * - CL_INVALID_VALUE if \a num_objects is zero and \a mem_objects is not a * NULL value or if \a num_objects > 0 and \a mem_objects is NULL. * - CL_INVALID_MEM_OBJECT if memory objects in \a mem_objects are not valid * OpenCL memory objects. * - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid command-queue. * - CL_INVALID_CONTEXT if clContext associated with \a command_queue was not * created from an OpenGL clContext. * - CL_INVALID_GL_OBJECT if memory objects in \a mem_objects have not been * created from a GL object(s). * - CL_INVALID_EVENT_WAIT_LIST if \a event_wait_list is NULL and * \a num_events_in_wait_list > 0, or \a event_wait_list is not NULL and * \a num_events_in_wait_list is 0, or if event objects in \a event_wait_list * are not valid events. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources * required by the OpenCL implementation on the host. * * \version 1.0r29 */ RUNTIME_ENTRY(cl_int, clEnqueueAcquireGLObjects, ( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event)) { return amd::clEnqueueAcquireExtObjectsAMD( command_queue, num_objects, mem_objects, num_events_in_wait_list, event_wait_list, event, CL_COMMAND_ACQUIRE_GL_OBJECTS); } RUNTIME_EXIT /*! @} * \addtogroup clEnqueueReleaseGLObjects * @{ */ /*! \brief Release OpenCL memory objects that have been created from OpenGL * objects. * * \param command_queue is a valid command-queue [which is associated with the * OpenCL clContext releasing the OpenGL objects]. * * \param num_objects is the number of memory objects to be released * in \a mem_objects. * * \param mem_objects is a pointer to a list of CL memory objects that refer * to a GL object (buffer/texture/renderbuffer objects or the framebuffer). * * \param event_wait_list specify [is a pointer to] events that need to * complete before this particular command can be executed. * If \a event_wait_list is NULL, then this particular command does not wait * on any event to complete. If \a event_wait_list is NULL, * \a num_events_in_wait_list must be 0. If \a event_wait_list is not NULL, * the list of events pointed to by \a event_wait_list must be valid and * \a num_events_in_wait_list must be greater than 0. The events specified in * \a event_wait_list act as synchronization points. * * \param num_events_in_wait_list specify the number of events in * \a event_wait_list. It must be 0 if \a event_wait_list is NULL. It must be * greater than 0 if \a event_wait_list is not NULL. * * \param event returns an event object that identifies this particular * command and can be used to query or queue a wait for this particular * command to complete. \a event can be NULL in which case it will not be * possible for the application to query the status of this command or queue a * wait for this command to complete. * * \return One of the following values is returned: * - CL_SUCCESS if the function is executed successfully. * - CL_SUCCESS if \a num_objects is 0 and \a mem_objects is NULL; the * function does nothing. * - CL_INVALID_VALUE if \a num_objects is zero and \a mem_objects is not a * NULL value or if \a num_objects > 0 and \a mem_objects is NULL. * - CL_INVALID_MEM_OBJECT if memory objects in \a mem_objects are not valid * OpenCL memory objects. * - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid command-queue. * - CL_INVALID_CONTEXT if clContext associated with \a command_queue was not * created from an OpenGL clContext. * - CL_INVALID_GL_OBJECT if memory objects in \a mem_objects have not been * created from a GL object(s). * - CL_INVALID_EVENT_WAIT_LIST if \a event_wait_list is NULL and * \a num_events_in_wait_list > 0, or \a event_wait_list is not NULL and * \a num_events_in_wait_list is 0, or if event objects in \a event_wait_list * are not valid events. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources * required by the OpenCL implementation on the host. * * \version 1.0r29 */ RUNTIME_ENTRY(cl_int, clEnqueueReleaseGLObjects, ( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event)) { return amd::clEnqueueReleaseExtObjectsAMD( command_queue, num_objects, mem_objects, num_events_in_wait_list, event_wait_list, event, CL_COMMAND_RELEASE_GL_OBJECTS); } RUNTIME_EXIT /*! @} * \addtogroup clCreateEventFromGLsyncKHR * @{ */ /*! \brief Creates an event object linked to an OpenGL sync object. * Completion of such an event object is equivalent to waiting for completion * of the fence command associated with the linked GL sync object. * * \param context is valid OpenCL context created from an OpenGL context * or share group, using the cl_khr_gl_sharing extension. * * \param sync is the 'name' of a sync object in the GL share group associated * with context. * * \param errcode_ret Returns an appropriate error code as described below. * If errcode_ret is NULL, no error code is returned. * * \return a valid OpenCL event object and errcode_ret is set to CL_SUCCESS * if the event object is created successfully.Otherwise, it returns a NULL * value with one of the following error values returned in errcode_ret: * - CL_INVALID_CONTEXT if context is not a valid context or was not created * from a GL context. * - CL_INVALID_GL_OBJECT if sync is not the name of a sync object in the * GL share group associated with context. * * \version 1.1 */ RUNTIME_ENTRY_RET(cl_event, clCreateEventFromGLsyncKHR, ( cl_context context, cl_GLsync clGLsync, cl_int* errcode_ret)) { // create event of fence sync type amd::ClGlEvent* clglEvent = new amd::ClGlEvent(*as_amd(context)); clglEvent->context().glenv()->glFlush_(); // initially set the status of fence as queued clglEvent->setStatus(CL_SUBMITTED); // store GLsync id of the fence in event in order to associate them together clglEvent->setData(clGLsync); amd::Event* evt = dynamic_cast(clglEvent); evt->retain(); return as_cl(evt); } RUNTIME_EXIT /*! @} * \addtogroup clGetGLContextInfoKHR * @{ */ /*! \brief This f-n is defined in CL extension cl_khr_gl_sharing and serves * the purpose of quering current device and all devices that support * CL-GL interoperability. * * \param properties points to an , which is a array of * ordered pairs terminated with zero. If an * attribute is not specified in , then its default value * (listed in table 4.attr) is used (it is said to be specified * implicitly). If is NULL or empty (points to a list * whose first value is zero), all attributes take on their default * values. * * \param param_name may accept one of the following enumerated values: * - CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR 0x2006 * - CL_DEVICES_FOR_GL_CONTEXT_KHR 0x2007. * * \param param_value_size is used to specify the size in bytes of memory * pointed to by \a param_value. This size must be >= size of return type as * described for \a param_name argumnet (GLenum or GLint). * \a param_value_size_ret returns the actual size in bytes of data copied to * \a param_value. If \a param_value_size_ret is NULL, it is ignored * * \param param_value is a pointer to memory where the appropriate result * being queried is returned. If \a param_value is NULL, it is ignored. * * \param param_value_size is used to specify the size in bytes of memory * pointed to by \a param_value. This size must be >= size of return type as * described for \a param_name argumnet (GLenum or GLint). * \a param_value_size_ret returns the actual size in bytes of data copied to * \a param_value. If \a param_value_size_ret is NULL, it is ignored * * \return one of the following values is returned: * - CL_SUCCESS if the function is executed successfully. * - CL_SUCCESS if \a num_objects is 0 and \a mem_objects is NULL; the * function does nothing. * - CL_INVALID_VALUE if \a num_objects is zero and \a mem_objects is not a * NULL value or if \a num_objects > 0 and \a mem_objects is NULL. * - CL_INVALID_MEM_OBJECT if memory objects in \a mem_objects are not valid * OpenCL memory objects. * - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid command-queue. * - CL_INVALID_CONTEXT if clContext associated with \a command_queue was not * created from an OpenGL clContext. * - CL_INVALID_GL_OBJECT if memory objects in \a mem_objects have not been * created from a GL object(s). * - CL_INVALID_EVENT_WAIT_LIST if \a event_wait_list is NULL and * \a num_events_in_wait_list > 0, or \a event_wait_list is not NULL and * \a num_events_in_wait_list is 0, or if event objects in \a event_wait_list * are not valid events. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources * required by the OpenCL implementation on the host. * - CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR if * * \version 1.0r47 */ RUNTIME_ENTRY(cl_int, clGetGLContextInfoKHR, ( const cl_context_properties *properties, cl_gl_context_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)) { cl_int errcode; cl_device_id* gpu_devices; cl_device_id* cpu_devices; cl_uint num_gpu_devices = 0; cl_uint num_cpu_devices = 0; amd::Context::Info info; static const bool VALIDATE_ONLY = true; errcode = amd::Context::checkProperties(properties, &info); if (CL_SUCCESS != errcode) { return errcode; } if (!(info.flags_ & amd::Context::GLDeviceKhr)) { // No GL context is specified return CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR; } // Get devices errcode = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 0, NULL, &num_gpu_devices); if (errcode != CL_SUCCESS && errcode != CL_DEVICE_NOT_FOUND) { return CL_INVALID_VALUE; } errcode = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_CPU, 0, NULL, &num_cpu_devices); if (errcode != CL_SUCCESS && errcode != CL_DEVICE_NOT_FOUND) { return CL_INVALID_VALUE; } if (!num_gpu_devices && !num_cpu_devices) { return CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR; } switch(param_name) { case CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR: // Return the CL device currently associated with the specified OpenGL context. if (num_gpu_devices) { gpu_devices = (cl_device_id *) alloca(num_gpu_devices * sizeof(cl_device_id)); errcode = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, num_gpu_devices, gpu_devices, NULL); if (errcode != CL_SUCCESS) { return errcode; } for (cl_uint i = 0; i < num_gpu_devices; ++i) { cl_device_id device = gpu_devices[i]; if (is_valid(device) && as_amd(device)->bindExternalDevice(info.flags_, info.hDev_, info.hCtx_, VALIDATE_ONLY)) { return amd::clGetInfo( device, param_value_size, param_value, param_value_size_ret); } } *not_null(param_value_size_ret) = 0; } else { cpu_devices = (cl_device_id *) alloca(num_cpu_devices * sizeof(cl_device_id)); errcode = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_CPU, num_cpu_devices, cpu_devices, NULL); if (errcode != CL_SUCCESS) { return errcode; } return amd::clGetInfo( cpu_devices[0], param_value_size, param_value, param_value_size_ret); } break; case CL_DEVICES_FOR_GL_CONTEXT_KHR: { //List of all CL devices that can be associated with the specified OpenGL context. cl_uint total_devices = num_gpu_devices + num_cpu_devices; size_t size = total_devices * sizeof(cl_device_id); cl_device_id* devices = (cl_device_id *) alloca(size); errcode = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_CPU, total_devices, devices, NULL); if (errcode != CL_SUCCESS) { return errcode; } std::vector compatible_devices; for (cl_uint i = 0; i < total_devices; ++i) { cl_device_id device = devices[i]; if (is_valid(device) && as_amd(device)->bindExternalDevice(info.flags_, info.hDev_, info.hCtx_, VALIDATE_ONLY)) { compatible_devices.push_back(as_amd(device)); } } size_t deviceCount = compatible_devices.size(); size_t deviceCountSize = deviceCount * sizeof(cl_device_id); if (param_value != NULL && param_value_size < deviceCountSize) { return CL_INVALID_VALUE; } *not_null(param_value_size_ret) = deviceCountSize; if (param_value != NULL) { cl_device_id* deviceList = (cl_device_id*) param_value; std::vector::const_iterator it; for (it = compatible_devices.begin(); it != compatible_devices.end(); ++it) { *deviceList++ = as_cl(*it); } } return CL_SUCCESS; } break; default: LogWarning("\"param_name\" is not valid"); return CL_INVALID_VALUE; } return CL_SUCCESS; } RUNTIME_EXIT // // // namespace amd // // namespace amd { typedef struct { GLenum glBinding; GLenum glTarget; } TargetBindings_t; /*! @} * \addtogroup CL-GL interop helper functions * @{ */ //! Function clearGLErrors() to clear all GL error bits, if any void clearGLErrors(const Context &amdContext) { GLenum glErr, glLastErr = GL_NO_ERROR; while(1) { glErr = amdContext.glenv()->glGetError_(); if (glErr == GL_NO_ERROR || glErr == glLastErr) { break; } glLastErr = glErr; LogWarning("GL error"); } } GLenum checkForGLError(const Context &amdContext) { GLenum glRetErr = GL_NO_ERROR; GLenum glErr; while(GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { glRetErr = glErr; // Just return the last GL error LogWarning("Check GL error"); } return glRetErr; } //! Function getCLFormatFromGL returns "true" if GL format //! is compatible with CL format, "false" otherwise. bool getCLFormatFromGL(const Context& amdContext, GLint gliInternalFormat, cl_image_format* pclImageFormat, int* piBytesPerPixel, cl_mem_flags flags) { bool bRetVal = false; /* Available values for "image_channel_order" ========================================== CL_R CL_A CL_INTENSITY CL_LUMINANCE CL_RG CL_RA CL_RGB CL_RGBA CL_ARGB CL_BGRA Available values for "image_channel_data_type" ============================================== CL_SNORM_INT8 CL_SNORM_INT16 CL_UNORM_INT8 CL_UNORM_INT16 CL_UNORM_SHORT_565 CL_UNORM_SHORT_555 CL_UNORM_INT_101010 CL_SIGNED_INT8 CL_SIGNED_INT16 CL_SIGNED_INT32 CL_UNSIGNED_INT8 CL_UNSIGNED_INT16 CL_UNSIGNED_INT32 CL_HALF_FLOAT CL_FLOAT */ switch(gliInternalFormat) { case GL_BGR8_ATI: case GL_BGRA8_ATI: pclImageFormat->image_channel_order = CL_BGRA; pclImageFormat->image_channel_data_type = CL_UNORM_INT8;//CL_UNSIGNED_INT8; *piBytesPerPixel = 4; bRetVal = true; break; case GL_ALPHA8: pclImageFormat->image_channel_order = CL_A; pclImageFormat->image_channel_data_type = CL_UNORM_INT8;//CL_UNSIGNED_INT8; *piBytesPerPixel = 1; bRetVal = true; break; case GL_R8: case GL_R8UI: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_R8)? CL_UNORM_INT8:CL_UNSIGNED_INT8; *piBytesPerPixel = 1; bRetVal = true; break; case GL_R8I: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_SIGNED_INT8; *piBytesPerPixel = 1; bRetVal = true; break; case GL_RG8: case GL_RG8UI: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RG8)? CL_UNORM_INT8:CL_UNSIGNED_INT8; *piBytesPerPixel = 2; bRetVal = true; break; case GL_RG8I: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_SIGNED_INT8; *piBytesPerPixel = 2; bRetVal = true; break; case GL_RGB8: case GL_RGB8UI: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RGB8)? CL_UNORM_INT8:CL_UNSIGNED_INT8; *piBytesPerPixel = 3; bRetVal = true; break; case GL_RGB8I: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_SIGNED_INT8; *piBytesPerPixel = 3; bRetVal = true; break; case GL_RGBA: case GL_RGBA8: case GL_RGBA8UI: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RGBA8UI)? CL_UNSIGNED_INT8:CL_UNORM_INT8; *piBytesPerPixel = 4; bRetVal = true; break; case GL_RGBA8I: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_SIGNED_INT8; *piBytesPerPixel = 4; bRetVal = true; break; case GL_R16: case GL_R16UI: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_R16)? CL_UNORM_INT16:CL_UNSIGNED_INT16; bRetVal = true; *piBytesPerPixel = 2; break; case GL_R16I: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_SIGNED_INT16; *piBytesPerPixel = 2; bRetVal = true; break; case GL_R16F: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_HALF_FLOAT; *piBytesPerPixel = 2; bRetVal = true; break; case GL_RG16: case GL_RG16UI: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RG16)? CL_UNORM_INT16:CL_UNSIGNED_INT16; *piBytesPerPixel = 4; bRetVal = true; break; case GL_RG16I: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_SIGNED_INT16; *piBytesPerPixel = 4; bRetVal = true; break; case GL_RG16F: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_HALF_FLOAT; *piBytesPerPixel = 4; bRetVal = true; break; case GL_RGB16: case GL_RGB16UI: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RGB16)? CL_UNORM_INT16:CL_UNSIGNED_INT16; *piBytesPerPixel = 6; bRetVal = true; break; case GL_RGB16I: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_SIGNED_INT16; *piBytesPerPixel = 6; bRetVal = true; break; case GL_RGB16F: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_HALF_FLOAT; *piBytesPerPixel = 6; bRetVal = true; break; case GL_RGBA16: case GL_RGBA16UI: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = (gliInternalFormat == GL_RGBA16)? CL_UNORM_INT16:CL_UNSIGNED_INT16; *piBytesPerPixel = 8; bRetVal = true; break; case GL_RGBA16I: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_SIGNED_INT16; *piBytesPerPixel = 8; bRetVal = true; break; case GL_RGBA16F: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_HALF_FLOAT; *piBytesPerPixel = 8; bRetVal = true; break; case GL_R32I: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_SIGNED_INT32; *piBytesPerPixel = 4; bRetVal = true; break; case GL_R32UI: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; *piBytesPerPixel = 4; bRetVal = true; break; case GL_R32F: pclImageFormat->image_channel_order = CL_R; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 4; bRetVal = true; break; case GL_RG32I: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_SIGNED_INT32; *piBytesPerPixel = 8; bRetVal = true; break; case GL_RG32UI: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; *piBytesPerPixel = 8; bRetVal = true; break; case GL_RG32F: pclImageFormat->image_channel_order = CL_RG; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 8; bRetVal = true; break; case GL_RGB32I: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_SIGNED_INT32; *piBytesPerPixel = 12; bRetVal = true; break; case GL_RGB32UI: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; *piBytesPerPixel = 12; bRetVal = true; break; case GL_RGB32F: pclImageFormat->image_channel_order = CL_RGB; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 12; bRetVal = true; break; case GL_RGBA32I: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_SIGNED_INT32; *piBytesPerPixel = 16; bRetVal = true; break; case GL_RGBA32UI: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; *piBytesPerPixel = 16; bRetVal = true; break; case GL_RGBA32F: pclImageFormat->image_channel_order = CL_RGBA; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 16; bRetVal = true; break; case GL_DEPTH_COMPONENT32F: pclImageFormat->image_channel_order = CL_DEPTH; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 4; bRetVal = true; break; case GL_DEPTH_COMPONENT16: pclImageFormat->image_channel_order = CL_DEPTH; pclImageFormat->image_channel_data_type = CL_UNORM_INT16; *piBytesPerPixel = 2; bRetVal = true; break; case GL_DEPTH24_STENCIL8: pclImageFormat->image_channel_order = CL_DEPTH_STENCIL; pclImageFormat->image_channel_data_type = CL_UNORM_INT24; *piBytesPerPixel = 4; bRetVal = true; break; case GL_DEPTH32F_STENCIL8: pclImageFormat->image_channel_order = CL_DEPTH_STENCIL; pclImageFormat->image_channel_data_type = CL_FLOAT; *piBytesPerPixel = 5; bRetVal = true; break; default: LogWarning("unsupported GL internal format"); break; } amd::Image::Format imageFormat(*pclImageFormat); if (bRetVal && !imageFormat.isSupported(amdContext, 0, flags)) { bRetVal = false; } return bRetVal; } void BufferGL::initDeviceMemory() { deviceMemories_ = reinterpret_cast( reinterpret_cast(this) + sizeof(BufferGL)); memset(deviceMemories_, 0, context_().devices().size() * sizeof(DeviceMemory)); } bool BufferGL::mapExtObjectInCQThread() { assert(!context_().glenv()->isEGL()); GLFunctions::SetIntEnv ie(context_().glenv()); if (!ie.isValid()) { return false; } GLenum glAccess = GL_READ_WRITE; // Default if (getMemFlags() & CL_MEM_READ_ONLY) { glAccess = GL_READ_ONLY; } else if (getMemFlags() & CL_MEM_WRITE_ONLY) { glAccess = GL_WRITE_ONLY; } clearGLErrors(context_()); context_().glenv()->glBindBuffer_(GL_ARRAY_BUFFER, gluiName_); void* pCpuMem = context_().glenv()->glMapBuffer_(GL_ARRAY_BUFFER, glAccess); if (checkForGLError(context_()) != GL_NO_ERROR || !pCpuMem) { LogError("cannot map GL buffer"); return false; } setHostMem(pCpuMem); return true; } bool BufferGL::unmapExtObjectInCQThread() { assert(!context_().glenv()->isEGL()); GLFunctions::SetIntEnv ie(context_().glenv()); if (!ie.isValid()) { return false; } clearGLErrors(context_()); context_().glenv()->glBindBuffer_(GL_ARRAY_BUFFER, gluiName_); if (GL_FALSE == context_().glenv()->glUnmapBuffer_(GL_ARRAY_BUFFER)) { LogError("context_().glenv()->glUnmapBuffer_ returned GL_FALSE - buffer may be corrupted"); return false; } if (checkForGLError(context_()) != GL_NO_ERROR) { LogWarning("Error unmapping GL buffer"); return false; } setHostMem(NULL); return true; } static GLenum clChannelDataTypeToGlType(cl_channel_type channel_type) { // Pick // GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, // GL_UNSIGNED_INT, GL_FLOAT, GL_2_BYTES, GL_3_BYTES, GL_4_BYTES // or GL_DOUBLE switch (channel_type) { case CL_SNORM_INT8: return GL_BYTE; case CL_SNORM_INT16: return GL_SHORT; case CL_UNORM_INT8: return GL_UNSIGNED_BYTE; case CL_UNORM_INT16: return GL_UNSIGNED_SHORT; case CL_SIGNED_INT8: return GL_BYTE; case CL_SIGNED_INT16: return GL_SHORT; case CL_SIGNED_INT32: return GL_INT; case CL_UNSIGNED_INT8: return GL_UNSIGNED_BYTE; case CL_UNSIGNED_INT16: return GL_UNSIGNED_SHORT; case CL_UNSIGNED_INT32: return GL_UNSIGNED_INT; case CL_FLOAT: return GL_FLOAT; case CL_HALF_FLOAT: case CL_UNORM_SHORT_565: case CL_UNORM_SHORT_555: case CL_UNORM_INT_101010: default: guarantee(false && "Unexpected CL type."); return 0; } } static GLenum glInternalFormatToGlFormat(GLenum internalFormat) { switch (internalFormat) { // Base internal formats case GL_RGBA: case GL_BGRA: return internalFormat; // Sized internal formats case GL_RGBA8: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA32F: return GL_RGBA; case GL_RGBA8I: case GL_RGBA8UI: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA32I: case GL_RGBA32UI: return GL_RGBA_INTEGER; default: guarantee(false && "Unexpected GL internal format."); return 0; } } void ImageGL::initDeviceMemory() { deviceMemories_ = reinterpret_cast( reinterpret_cast(this) + sizeof(ImageGL)); memset(deviceMemories_, 0, context_().devices().size() * sizeof(DeviceMemory)); } bool ImageGL::mapExtObjectInCQThread() { assert(!context_().glenv()->isEGL()); GLFunctions::SetIntEnv ie(context_().glenv()); if (!ie.isValid()) { return false; } GLenum glAccess = GL_READ_WRITE; // Default if (getMemFlags() & CL_MEM_READ_ONLY) { glAccess = GL_READ_ONLY; } else if (getMemFlags() & CL_MEM_WRITE_ONLY) { glAccess = GL_WRITE_ONLY; } clearGLErrors(context_()); context_().glenv()->glBindTexture_(getGLTarget(), gluiName_); size_t mem_size = getSize(); char* pCpuMem = new char[mem_size]; if (pCpuMem == NULL) { LogError("Cannot alloc host memory for ImageGL"); return false; } context_().glenv()->glGetTexImage_( getGLTarget(), gliMipLevel_, glInternalFormatToGlFormat(glInternalFormat_), clChannelDataTypeToGlType(getImageFormat().image_channel_data_type), pCpuMem); if (checkForGLError(context_()) != GL_NO_ERROR) { LogError("cannot map GL texture"); free(pCpuMem); return false; } setHostMem(pCpuMem); return true; } bool ImageGL::unmapExtObjectInCQThread() { assert(!context_().glenv()->isEGL()); GLFunctions::SetIntEnv ie(context_().glenv()); if (!ie.isValid()) { return false; } bool status = true; clearGLErrors(context_()); context_().glenv()->glBindTexture_(getGLTarget(), gluiName_); char* pCpuMem = (char *)getHostMem(); if (checkForGLError(context_()) != GL_NO_ERROR) { LogError("Cannot map GL texture"); status = false; goto cleanup; } context_().glenv()->glTexImage2D_( getGLTarget(), // target gliMipLevel_, // miplevel glInternalFormat_, // internalFormat or bytes per pixel gliWidth_, // width gliHeight_, // height 0, // border // format glInternalFormatToGlFormat(glInternalFormat_), // type clChannelDataTypeToGlType(getImageFormat().image_channel_data_type), pCpuMem); // data if (checkForGLError(context_()) != GL_NO_ERROR) { LogError("Cannot update GL texture"); status = false; goto cleanup; } cleanup: delete [] pCpuMem; setHostMem(NULL); return status; } //******************************************************************* // // Internal implementation of CL API functions // //******************************************************************* // // clCreateFromGLBufferAMD // cl_mem clCreateFromGLBufferAMD( Context& amdContext, cl_mem_flags flags, GLuint bufobj, cl_int* errcode_ret) { BufferGL* pBufferGL = NULL; GLenum glErr; GLenum glTarget = GL_ARRAY_BUFFER; GLint gliSize = 0; GLint gliMapped = 0; // Verify context init'ed for interop if (!amdContext.glenv() || !amdContext.glenv()->isAssociated()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return (cl_mem) 0; } // Add this scope to bound the scoped lock { GLFunctions::SetIntEnv ie(amdContext.glenv()); if (!ie.isValid()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return as_cl(0); } // Verify GL buffer object clearGLErrors(amdContext); if ((GL_FALSE == amdContext.glenv()->glIsBuffer_(bufobj)) || (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_()))) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("\"bufobj\" is not a GL buffer object"); return (cl_mem) 0; } // It seems that CL spec is not concerned with GL_BUFFER_USAGE, so skip it // Check if size is available - data store is created amdContext.glenv()->glBindBuffer_(glTarget, bufobj); clearGLErrors(amdContext); amdContext.glenv()->glGetBufferParameteriv_(glTarget, GL_BUFFER_SIZE, &gliSize); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("cannot get the GL buffer size"); return (cl_mem) 0; } if (gliSize == 0) { //@todo - check why sometime the size is zero *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("the GL buffer's data store is not created"); return (cl_mem) 0; } // Mapping will be done at acquire time (sync point) } // Release scoped lock // Now create BufferGL object pBufferGL = new(amdContext) BufferGL(amdContext, flags, gliSize, 0, bufobj); if (!pBufferGL) { *not_null(errcode_ret) = CL_OUT_OF_HOST_MEMORY; LogWarning("cannot create object of class BufferGL"); return (cl_mem) 0; } if (!pBufferGL->create()) { *not_null(errcode_ret) = CL_MEM_OBJECT_ALLOCATION_FAILURE; pBufferGL->release(); return (cl_mem) 0; } *not_null(errcode_ret) = CL_SUCCESS; // Create interop object if (pBufferGL->getInteropObj() == NULL) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("cannot create object of class BufferGL"); return (cl_mem)0; } // Fixme: If more than one device is present in the context, we choose the first device. // We should come up with a more elegant solution to handle this. assert(amdContext.devices().size() == 1); std::vector::const_iterator itr = amdContext.devices().begin(); amd::Device& dev = *(*itr); if (dev.type() != CL_DEVICE_TYPE_CPU){ device::Memory* mem = pBufferGL->getDeviceMemory(dev); if (NULL == mem) { LogPrintfError("Can't allocate memory size - 0x%08X bytes!", pBufferGL->getSize()); *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; } mem->processGLResource(device::Memory::GLDecompressResource); } return as_cl(pBufferGL); } cl_mem clCreateFromGLTextureAMD( Context& amdContext, cl_mem_flags clFlags, GLenum target, GLint miplevel, GLuint texture, int* errcode_ret) { ImageGL* pImageGL = NULL; GLenum glErr; GLenum glTarget = 0; GLenum glInternalFormat; cl_image_format clImageFormat; uint dim = 1; cl_mem_object_type clType; cl_gl_object_type clGLType; GLsizei numSamples = 1; // Verify context init'ed for interop if (!amdContext.glenv() || !amdContext.glenv()->isAssociated()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return static_cast(0); } GLint gliTexWidth = 1; GLint gliTexHeight = 1; GLint gliTexDepth = 1; // Add this scope to bound the scoped lock { GLFunctions::SetIntEnv ie(amdContext.glenv()); if (!ie.isValid()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return as_cl(0); } // Verify GL texture object clearGLErrors(amdContext); if ((GL_FALSE == amdContext.glenv()->glIsTexture_(texture)) || (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_()))) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("\"texture\" is not a GL texture object"); return static_cast(0); } bool image = true; // Check target value validity switch(target) { case GL_TEXTURE_BUFFER: glTarget = GL_TEXTURE_BUFFER; dim = 1; clType = CL_MEM_OBJECT_IMAGE1D_BUFFER; clGLType = CL_GL_OBJECT_TEXTURE_BUFFER; image = false; break; case GL_TEXTURE_1D: glTarget = GL_TEXTURE_1D; dim = 1; clType = CL_MEM_OBJECT_IMAGE1D; clGLType = CL_GL_OBJECT_TEXTURE1D; break; case GL_TEXTURE_CUBE_MAP_POSITIVE_X: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: glTarget = GL_TEXTURE_CUBE_MAP; dim = 2; clType = CL_MEM_OBJECT_IMAGE2D; clGLType = CL_GL_OBJECT_TEXTURE2D; break; case GL_TEXTURE_1D_ARRAY: glTarget = GL_TEXTURE_1D_ARRAY; dim = 2; clType = CL_MEM_OBJECT_IMAGE1D_ARRAY; clGLType = CL_GL_OBJECT_TEXTURE1D_ARRAY; break; case GL_TEXTURE_2D: glTarget = GL_TEXTURE_2D; dim = 2; clType = CL_MEM_OBJECT_IMAGE2D; clGLType = CL_GL_OBJECT_TEXTURE2D; break; case GL_TEXTURE_2D_MULTISAMPLE: glTarget = GL_TEXTURE_2D_MULTISAMPLE; dim = 2; clType = CL_MEM_OBJECT_IMAGE2D; clGLType = CL_GL_OBJECT_TEXTURE2D; break; case GL_TEXTURE_RECTANGLE_ARB: glTarget = GL_TEXTURE_RECTANGLE_ARB; dim = 2; clType = CL_MEM_OBJECT_IMAGE2D; clGLType = CL_GL_OBJECT_TEXTURE2D; break; case GL_TEXTURE_2D_ARRAY: glTarget = GL_TEXTURE_2D_ARRAY; dim = 3; clType = CL_MEM_OBJECT_IMAGE2D_ARRAY; clGLType = CL_GL_OBJECT_TEXTURE2D_ARRAY; break; case GL_TEXTURE_3D: glTarget = GL_TEXTURE_3D; dim = 3; clType = CL_MEM_OBJECT_IMAGE3D; clGLType = CL_GL_OBJECT_TEXTURE3D; break; default: // wrong value *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid \"target\" value"); return static_cast(0); break; } amdContext.glenv()->glBindTexture_(glTarget, texture); // Check if size is available - data store is created if (image) { // Check mipmap level for "texture" name GLint gliTexBaseLevel; GLint gliTexMaxLevel; clearGLErrors(amdContext); amdContext.glenv()->glGetTexParameteriv_(glTarget, GL_TEXTURE_BASE_LEVEL, &gliTexBaseLevel); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_MIP_LEVEL; LogWarning("Cannot get base mipmap level of a GL \"texture\" object"); return static_cast(0); } clearGLErrors(amdContext); amdContext.glenv()->glGetTexParameteriv_(glTarget, GL_TEXTURE_MAX_LEVEL, &gliTexMaxLevel); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_MIP_LEVEL; LogWarning("Cannot get max mipmap level of a GL \"texture\" object"); return static_cast(0); } if ((gliTexBaseLevel > miplevel) || (miplevel > gliTexMaxLevel)) { *not_null(errcode_ret) = CL_INVALID_MIP_LEVEL; LogWarning("\"miplevel\" is not a valid mipmap level of the GL \"texture\" object"); return static_cast(0); } // Get GL texture format and check if it's compatible with CL format clearGLErrors(amdContext); amdContext.glenv()->glGetTexLevelParameteriv_(target, miplevel, GL_TEXTURE_INTERNAL_FORMAT, (GLint*) &glInternalFormat); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("Cannot get internal format of \"miplevel\" of GL \"texture\" object"); return static_cast(0); } amdContext.glenv()->glGetTexLevelParameteriv_(target, miplevel, GL_TEXTURE_SAMPLES, (GLint*) &numSamples); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("Cannot get numbers of samples of GL \"texture\" object"); return static_cast(0); } if (numSamples > 1) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("MSAA \"texture\" object is not suppoerted for the device"); return static_cast(0); } // Now get CL format from GL format and bytes per pixel int iBytesPerPixel = 0; if (!getCLFormatFromGL(amdContext, glInternalFormat, &clImageFormat, &iBytesPerPixel, clFlags)) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("\"texture\" format does not map to an appropriate CL image format"); return static_cast(0); } switch (dim) { case 3: clearGLErrors(amdContext); amdContext.glenv()->glGetTexLevelParameteriv_(target, miplevel, GL_TEXTURE_DEPTH, &gliTexDepth); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("Cannot get the depth of \"miplevel\" of GL \"texure\""); return static_cast(0); } // Fall trough to process other dimensions... case 2: clearGLErrors(amdContext); amdContext.glenv()->glGetTexLevelParameteriv_(target, miplevel, GL_TEXTURE_HEIGHT, &gliTexHeight); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("Cannot get the height of \"miplevel\" of GL \"texure\""); return static_cast(0); } // Fall trough to process other dimensions... case 1: clearGLErrors(amdContext); amdContext.glenv()->glGetTexLevelParameteriv_(target, miplevel, GL_TEXTURE_WIDTH, &gliTexWidth); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("Cannot get the width of \"miplevel\" of GL \"texure\""); return static_cast(0); } break; default: *not_null(errcode_ret) = CL_INVALID_VALUE; LogWarning("invalid \"target\" value"); return static_cast(0); } } else { GLint size; // In case target is GL_TEXTURE_BUFFER amdContext.glenv()->glBindBuffer_(glTarget, texture); // Get GL texture format and check if it's compatible with CL format clearGLErrors(amdContext); amdContext.glenv()->glGetIntegerv_(GL_TEXTURE_BUFFER_FORMAT, reinterpret_cast(&glInternalFormat)); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("Cannot get internal format of \"miplevel\" of GL \"texture\" object"); return static_cast(0); } // Now get CL format from GL format and bytes per pixel int iBytesPerPixel = 0; if (!getCLFormatFromGL(amdContext, glInternalFormat, &clImageFormat, &iBytesPerPixel, clFlags)) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("\"texture\" format does not map to an appropriate CL image format"); return static_cast(0); } clearGLErrors(amdContext); amdContext.glenv()->glGetBufferParameteriv_(glTarget, GL_BUFFER_SIZE, &size); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("Cannot get internal format of \"miplevel\" of GL \"texture\" object"); return static_cast(0); } gliTexWidth = size / iBytesPerPixel; } size_t imageSize = (clType == CL_MEM_OBJECT_IMAGE1D_ARRAY) ? static_cast(gliTexHeight) : static_cast(gliTexDepth); if (!amd::Image::validateDimensions( amdContext.devices(), clType, static_cast(gliTexWidth), static_cast(gliTexHeight), static_cast(gliTexDepth), imageSize)) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("The GL \"texture\" data store is not created or out of supported dimensions"); return static_cast(0); } // PBO and mapping will be done at "acquire" time (sync point) } // Release scoped lock target = (glTarget == GL_TEXTURE_CUBE_MAP) ? target : 0; pImageGL = new(amdContext) ImageGL(amdContext, clType, clFlags, clImageFormat, static_cast(gliTexWidth), static_cast(gliTexHeight), static_cast(gliTexDepth), glTarget, texture, miplevel, glInternalFormat, clGLType,numSamples, target); if (!pImageGL) { *not_null(errcode_ret) = CL_OUT_OF_HOST_MEMORY; LogWarning("Cannot create class ImageGL - out of memory?"); return static_cast(0); } if (!pImageGL->create()) { *not_null(errcode_ret) = CL_MEM_OBJECT_ALLOCATION_FAILURE; pImageGL->release(); return static_cast(0); } *not_null(errcode_ret) = CL_SUCCESS; return as_cl(pImageGL); } // // clCreateFromGLRenderbufferDAMD // cl_mem clCreateFromGLRenderbufferAMD( Context& amdContext, cl_mem_flags clFlags, GLuint renderbuffer, int* errcode_ret) { ImageGL* pImageGL = NULL; GLenum glErr; GLenum glTarget = GL_RENDERBUFFER; GLenum glInternalFormat; cl_image_format clImageFormat; // Verify context init'ed for interop if (!amdContext.glenv() || !amdContext.glenv()->isAssociated()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return (cl_mem) 0; } GLint gliRbWidth; GLint gliRbHeight; // Add this scope to bound the scoped lock { GLFunctions::SetIntEnv ie(amdContext.glenv()); if (!ie.isValid()) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; LogWarning("\"amdContext\" is not created from GL context or share list"); return as_cl(0); } // Verify GL renderbuffer object clearGLErrors(amdContext); if ((GL_FALSE == amdContext.glenv()->glIsRenderbufferEXT_(renderbuffer)) || (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_()))) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("\"renderbuffer\" is not a GL texture object"); return (cl_mem) 0; } amdContext.glenv()->glBindRenderbuffer_(glTarget, renderbuffer); // Get GL RB format and check if it's compatible with CL format clearGLErrors(amdContext); amdContext.glenv()->glGetRenderbufferParameterivEXT_(glTarget, GL_RENDERBUFFER_INTERNAL_FORMAT, (GLint*) &glInternalFormat); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("Cannot get internal format of GL \"renderbuffer\" object"); return (cl_mem) 0; } // Now get CL format from GL format and bytes per pixel int iBytesPerPixel = 0; if (!getCLFormatFromGL(amdContext, glInternalFormat, &clImageFormat, &iBytesPerPixel, clFlags)) { *not_null(errcode_ret) = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; LogWarning("\"renderbuffer\" format does not map to an appropriate CL image format"); return (cl_mem) 0; } // Check if size is available - data store is created clearGLErrors(amdContext); amdContext.glenv()->glGetRenderbufferParameterivEXT_(glTarget, GL_RENDERBUFFER_WIDTH, &gliRbWidth); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("Cannot get the width of GL \"renderbuffer\""); return (cl_mem) 0; } if (gliRbWidth == 0) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("The GL \"renderbuffer\" data store is not created"); return (cl_mem) 0; } clearGLErrors(amdContext); amdContext.glenv()->glGetRenderbufferParameterivEXT_(glTarget, GL_RENDERBUFFER_HEIGHT, &gliRbHeight); if (GL_NO_ERROR != (glErr = amdContext.glenv()->glGetError_())) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("Cannot get the height of GL \"renderbuffer\""); return (cl_mem) 0; } if (gliRbHeight == 0) { *not_null(errcode_ret) = CL_INVALID_GL_OBJECT; LogWarning("The GL \"renderbuffer\" data store is not created"); return (cl_mem) 0; } // PBO and mapping will be done at "acquire" time (sync point) } // Release scoped lock pImageGL = new(amdContext) ImageGL(amdContext, CL_MEM_OBJECT_IMAGE2D, clFlags, clImageFormat, (size_t) gliRbWidth, (size_t) gliRbHeight, 1, glTarget, renderbuffer, 0, glInternalFormat, CL_GL_OBJECT_RENDERBUFFER, 0); if (!pImageGL) { *not_null(errcode_ret) = CL_OUT_OF_HOST_MEMORY; LogWarning("Cannot create class ImageGL from renderbuffer - out of memory?"); return (cl_mem) 0; } if (!pImageGL->create()) { *not_null(errcode_ret) = CL_MEM_OBJECT_ALLOCATION_FAILURE; pImageGL->release(); return (cl_mem) 0; } *not_null(errcode_ret) = CL_SUCCESS; return as_cl(pImageGL); } // // clEnqueueAcquireExtObjectsAMD // static cl_int clSetInteropObjects(cl_uint num_objects, const cl_mem* mem_objects, std::vector& interopObjects) { if ((num_objects == 0 && mem_objects != NULL) || (num_objects != 0 && mem_objects == NULL)) { return CL_INVALID_VALUE; } while (num_objects-- > 0) { cl_mem obj = *mem_objects++; if (!is_valid(obj)) { return CL_INVALID_MEM_OBJECT; } amd::Memory* mem = as_amd(obj); if (mem->getInteropObj() == NULL) { return CL_INVALID_GL_OBJECT; } interopObjects.push_back(mem); } return CL_SUCCESS; } cl_int clEnqueueAcquireExtObjectsAMD(cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event, cl_command_type cmd_type) { if (!is_valid(command_queue)) { return CL_INVALID_COMMAND_QUEUE; } amd::HostQueue* queue = as_amd(command_queue)->asHostQueue(); if (NULL == queue) { return CL_INVALID_COMMAND_QUEUE; } amd::HostQueue& hostQueue = *queue; if (cmd_type == CL_COMMAND_ACQUIRE_GL_OBJECTS) { // Verify context init'ed for interop if (!hostQueue.context().glenv() || !hostQueue.context().glenv()->isAssociated()) { LogWarning("\"amdContext\" is not created from GL context or share list"); return CL_INVALID_CONTEXT; } } std::vector memObjects; cl_int err = clSetInteropObjects(num_objects, mem_objects, memObjects); if (err != CL_SUCCESS){ return err; } amd::Command::EventWaitList eventWaitList; err = amd::clSetEventWaitList(eventWaitList, hostQueue.context(), num_events_in_wait_list, event_wait_list); if (err != CL_SUCCESS){ return err; } #ifdef _WIN32 if ((hostQueue.context().info().flags_ & amd::Context::InteropUserSync) == 0) { //! Make sure D3D10 queues are flushed and all commands are finished //! before CL side would access interop objects if (cmd_type == CL_COMMAND_ACQUIRE_D3D10_OBJECTS_KHR) { SyncD3D10Objects(memObjects); } //! Make sure D3D11 queues are flushed and all commands are finished //! before CL side would access interop objects if (cmd_type == CL_COMMAND_ACQUIRE_D3D11_OBJECTS_KHR) { SyncD3D11Objects(memObjects); } //! Make sure D3D9 queues are flushed and all commands are finished //! before CL side would access interop objects if (cmd_type == CL_COMMAND_ACQUIRE_DX9_MEDIA_SURFACES_KHR) { SyncD3D9Objects(memObjects); } } #endif //_WIN32 //! Now create command and enqueue amd::AcquireExtObjectsCommand* command = new amd::AcquireExtObjectsCommand( hostQueue, eventWaitList, num_objects, memObjects, cmd_type); if (command == NULL) { return CL_OUT_OF_HOST_MEMORY; } // Make sure we have memory for the command execution if (!command->validateMemory()) { delete command; return CL_MEM_OBJECT_ALLOCATION_FAILURE; } command->enqueue(); *not_null(event) = as_cl(&command->event()); if (event == NULL) { command->release(); } return CL_SUCCESS; } // // clEnqueueReleaseExtObjectsAMD // cl_int clEnqueueReleaseExtObjectsAMD(cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event, cl_command_type cmd_type) { if (!is_valid(command_queue)) { return CL_INVALID_COMMAND_QUEUE; } amd::HostQueue* queue = as_amd(command_queue)->asHostQueue(); if (NULL == queue) { return CL_INVALID_COMMAND_QUEUE; } amd::HostQueue& hostQueue = *queue; std::vector memObjects; cl_int err = clSetInteropObjects(num_objects, mem_objects, memObjects); if (err != CL_SUCCESS){ return err; } amd::Command::EventWaitList eventWaitList; err = amd::clSetEventWaitList(eventWaitList, hostQueue.context(), num_events_in_wait_list, event_wait_list); if (err != CL_SUCCESS){ return err; } //! Now create command and enqueue amd::ReleaseExtObjectsCommand* command = new amd::ReleaseExtObjectsCommand( hostQueue, eventWaitList, num_objects, memObjects, cmd_type); if (command == NULL) { return CL_OUT_OF_HOST_MEMORY; } // Make sure we have memory for the command execution if (!command->validateMemory()) { delete command; return CL_MEM_OBJECT_ALLOCATION_FAILURE; } command->enqueue(); #ifdef _WIN32 if ((hostQueue.context().info().flags_ & amd::Context::InteropUserSync) == 0) { //! Make sure CL command queue is flushed and all commands are finished //! before D3D10 side would access interop resources if (cmd_type == CL_COMMAND_RELEASE_DX9_MEDIA_SURFACES_KHR || cmd_type == CL_COMMAND_RELEASE_D3D10_OBJECTS_KHR || cmd_type == CL_COMMAND_RELEASE_D3D11_OBJECTS_KHR) { command->awaitCompletion(); } } #endif //_WIN32 *not_null(event) = as_cl(&command->event()); if (event == NULL) { command->release(); } return CL_SUCCESS; } // Placed here as opposed to command.cpp, as glext.h and cl_gl_amd.hpp will have // to be included because of the GL calls bool ClGlEvent::waitForFence() { GLenum ret; // get fence id associated with fence event GLsync gs = reinterpret_cast (command().data()); if (!gs) return false; // Try to use DC and GLRC of current thread, if it doesn't exist // create a new GL context on this thread, which is shared with the original context #ifdef _WIN32 HDC tempDC_ = wglGetCurrentDC(); HGLRC tempGLRC_ = wglGetCurrentContext(); // Set DC and GLRC if (tempDC_ && tempGLRC_) { ret = context().glenv()->glClientWaitSync_(gs, GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); if (!(ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED)) return false; } else { tempDC_ = context().glenv()->getDC(); tempGLRC_ = context().glenv()->getIntGLRC(); if (!context().glenv()->init(reinterpret_cast(tempDC_), reinterpret_cast(tempGLRC_))) return false; // Make the newly created GL context current to this thread context().glenv()->setIntEnv(); // If fence has not yet executed, wait till it finishes ret = context().glenv()->glClientWaitSync_(gs, GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); if (!(ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED)) return false; // Since we're done making GL calls, restore whatever context was previously current to this thread context().glenv()->restoreEnv(); } #else // Lnx Display* tempDpy_ = context().glenv()->glXGetCurrentDisplay_(); GLXDrawable tempDrawable_ = context().glenv()->glXGetCurrentDrawable_(); GLXContext tempCtx_ = context().glenv()->glXGetCurrentContext_(); // Set internal Display and GLXContext if (tempDpy_ && tempCtx_) { ret = context().glenv()->glClientWaitSync_(gs, GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); if (!(ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED)) return false; } else { if (!context().glenv()->init(reinterpret_cast(context().glenv()->getIntDpy()), reinterpret_cast(context().glenv()->getIntCtx()))) return false; // Make the newly created GL context current to this thread context().glenv()->setIntEnv(); // If fence has not yet executed, wait till it finishes ret = context().glenv()->glClientWaitSync_(gs, GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); if (!(ret == GL_ALREADY_SIGNALED || ret == GL_CONDITION_SATISFIED)) return false; // Since we're done making GL calls, restore whatever context was previously current to this thread context().glenv()->restoreEnv(); } #endif // If we reach this point, fence should have completed setStatus(CL_COMPLETE); return true; } // // GLFunctions implementation // #ifdef _WIN32 #define CONVERT_CHAR_GLUBYTE #else //!_WIN32 #define CONVERT_CHAR_GLUBYTE (GLubyte*) #endif //!_WIN32 #define GLPREFIX(rtype, fcn, dclargs) \ if (!(fcn##_ = (PFN_##fcn) GETPROCADDRESS( \ libHandle_, #fcn))) { \ if (!(fcn##_ = (PFN_##fcn) GetProcAddress_( \ reinterpret_cast(#fcn)))) ++missed_; \ } GLFunctions::SetIntEnv::SetIntEnv(GLFunctions* env) : env_(env) { env_->getLock().lock(); // Set environment (DC and GLRC) isValid_ = env_->setIntEnv(); } GLFunctions::SetIntEnv::~SetIntEnv() { // Restore environment (CL DC and CL GLRC) env_->restoreEnv(); env_->getLock().unlock(); } GLFunctions::GLFunctions(HMODULE h, bool isEGL) : libHandle_(h), missed_(0), eglDisplay_(EGL_NO_DISPLAY), eglOriginalContext_(EGL_NO_CONTEXT), eglInternalContext_(EGL_NO_CONTEXT), eglTempContext_(EGL_NO_CONTEXT), isEGL_(isEGL), #ifdef _WIN32 hOrigGLRC_(0), hDC_(0), hIntGLRC_(0) #else //!_WIN32 Dpy_(0), Drawable_(0), origCtx_(0), intDpy_(0), intDrawable_(0), intCtx_(0), XOpenDisplay_(NULL), XCloseDisplay_(NULL), glXGetCurrentDrawable_(NULL), glXGetCurrentDisplay_(NULL), glXGetCurrentContext_(NULL), glXChooseVisual_(NULL), glXCreateContext_(NULL), glXDestroyContext_(NULL), glXMakeCurrent_(NULL) #endif //!_WIN32 { #define VERIFY_POINTER(p) if (NULL == p) {missed_++;} if (isEGL_) { GetProcAddress_ = (PFN_xxxGetProcAddress) GETPROCADDRESS(h, "eglGetProcAddress"); } else { GetProcAddress_ = (PFN_xxxGetProcAddress) GETPROCADDRESS(h, API_GETPROCADDR); } #ifndef _WIN32 // Initialize pointers to X11/GLX functions // We can not link with these functions on compile time since we need to support // console mode. In console mode X server and X server components may be absent. // Hence linking with X11 or libGL will fail module image loading in console mode.-tzachi cohen if (!isEGL_) { glXGetCurrentDrawable_ = (PFNglXGetCurrentDrawable)GETPROCADDRESS(h,"glXGetCurrentDrawable"); VERIFY_POINTER(glXGetCurrentDrawable_) glXGetCurrentDisplay_ = (PFNglXGetCurrentDisplay)GETPROCADDRESS(h,"glXGetCurrentDisplay"); VERIFY_POINTER(glXGetCurrentDisplay_) glXGetCurrentContext_ = (PFNglXGetCurrentContext) GETPROCADDRESS(h,"glXGetCurrentContext"); VERIFY_POINTER(glXGetCurrentContext_) glXChooseVisual_ = (PFNglXChooseVisual)GETPROCADDRESS(h,"glXChooseVisual"); VERIFY_POINTER(glXChooseVisual_) glXCreateContext_ = (PFNglXCreateContext)GETPROCADDRESS(h,"glXCreateContext"); VERIFY_POINTER(glXCreateContext_) glXDestroyContext_ = (PFNglXDestroyContext) GETPROCADDRESS(h,"glXDestroyContext"); VERIFY_POINTER(glXDestroyContext_) glXMakeCurrent_ = (PFNglXMakeCurrent) GETPROCADDRESS(h,"glXMakeCurrent"); VERIFY_POINTER(glXMakeCurrent_) HMODULE hXModule = (HMODULE) Os::loadLibrary("libX11.so.6"); if (NULL != hXModule) { XOpenDisplay_ = (PFNXOpenDisplay)GETPROCADDRESS(hXModule,"XOpenDisplay"); VERIFY_POINTER(XOpenDisplay_) XCloseDisplay_= (PFNXCloseDisplay)GETPROCADDRESS(hXModule,"XCloseDisplay"); VERIFY_POINTER(XCloseDisplay_) } else { missed_ += 2; } } // Initialize pointers to GL functions #include "gl_functions.hpp" #else if (!isEGL_) { wglCreateContext_ = (PFN_wglCreateContext)GETPROCADDRESS(h,"wglCreateContext"); VERIFY_POINTER(wglCreateContext_) wglGetCurrentContext_ = (PFN_wglGetCurrentContext)GETPROCADDRESS(h,"wglGetCurrentContext"); VERIFY_POINTER(wglGetCurrentContext_) wglGetCurrentDC_ = (PFN_wglGetCurrentDC)GETPROCADDRESS(h,"wglGetCurrentDC"); VERIFY_POINTER(wglGetCurrentDC_) wglDeleteContext_ = (PFN_wglDeleteContext)GETPROCADDRESS(h,"wglDeleteContext"); VERIFY_POINTER(wglDeleteContext_) wglMakeCurrent_ = (PFN_wglMakeCurrent)GETPROCADDRESS(h,"wglMakeCurrent"); VERIFY_POINTER(wglMakeCurrent_) wglShareLists_ = (PFN_wglShareLists)GETPROCADDRESS(h,"wglShareLists"); VERIFY_POINTER(wglShareLists_) } #endif } GLFunctions::~GLFunctions() { #ifdef _WIN32 if (hIntGLRC_) { if (!wglDeleteContext_(hIntGLRC_)) { DWORD dwErr = GetLastError(); LogWarning("Cannot delete GLRC"); } } #else //!_WIN32 if (intDpy_) { if (intCtx_) { glXDestroyContext_(intDpy_, intCtx_); intCtx_ = NULL; } XCloseDisplay_(intDpy_); intDpy_ = NULL; } #endif //!_WIN32 } bool GLFunctions::init(intptr_t hdc, intptr_t hglrc) { if (isEGL_) { eglDisplay_ = (EGLDisplay)hdc; eglOriginalContext_ = (EGLContext)hglrc; return true; } #ifdef _WIN32 DWORD err; if (missed_) { return false; } if (!hdc) { hDC_ = wglGetCurrentDC_(); } else { hDC_ = (HDC) hdc; } hOrigGLRC_ = (HGLRC) hglrc; if (!(hIntGLRC_ = wglCreateContext_(hDC_))) { err = GetLastError(); return false; } if (!wglShareLists_(hOrigGLRC_, hIntGLRC_)) { err = GetLastError(); return false; } bool makeCurrentNull = false; if (wglGetCurrentContext_() == NULL) { wglMakeCurrent_(hDC_, hIntGLRC_); makeCurrentNull = true; } // Initialize pointers to GL functions #include "gl_functions.hpp" if (makeCurrentNull) { wglMakeCurrent_(NULL, NULL); } if (missed_ == 0) { return true; } #else //!_WIN32 if (!missed_) { if (!hdc) { Dpy_ = glXGetCurrentDisplay_(); } else { Dpy_ = (Display*) hdc; } Drawable_ = glXGetCurrentDrawable_(); origCtx_ = (GLXContext) hglrc; int attribList[] = { GLX_RGBA, None}; if (!(intDpy_ = XOpenDisplay_(DisplayString(Dpy_)))) { #if defined(ATI_ARCH_X86) asm("int $3"); #endif } intDrawable_ = DefaultRootWindow(intDpy_); XVisualInfo* vis; int defaultScreen = DefaultScreen(intDpy_); if (!(vis = glXChooseVisual_(intDpy_, defaultScreen , attribList))) { return false; } if (!(intCtx_ = glXCreateContext_(intDpy_, vis, origCtx_, true))) { return false; } return true; } #endif //!_WIN32 return false; } bool GLFunctions::setIntEnv() { if (isEGL_) { return true; } #ifdef _WIN32 // Save current DC and GLRC tempDC_ = wglGetCurrentDC_(); tempGLRC_ = wglGetCurrentContext_(); // Set internal DC and GLRC if (tempDC_ != getDC() || tempGLRC_ != getIntGLRC()) { if (!wglMakeCurrent_(getDC(), getIntGLRC())) { DWORD err = GetLastError(); LogWarning("cannot set internal GL environment"); return false; } } #else //!_WIN32 tempDpy_ = glXGetCurrentDisplay_(); tempDrawable_ = glXGetCurrentDrawable_(); tempCtx_ = glXGetCurrentContext_(); // Set internal Display and GLXContext if (tempDpy_ != getDpy() || tempCtx_ != getIntCtx()) { if (!glXMakeCurrent_( getIntDpy(), getIntDrawable(), getIntCtx())) { LogWarning("cannot set internal GL environment"); return false; } } #endif //!_WIN32 return true; } bool GLFunctions::restoreEnv() { if (isEGL_) { // eglMakeCurrent( ); return true; } #ifdef _WIN32 // Restore original DC and GLRC if (!wglMakeCurrent_(tempDC_, tempGLRC_)) { DWORD err = GetLastError(); LogWarning("cannot restore original GL environment"); return false; } #else //!_WIN32 // Restore Display and GLXContext if (tempDpy_) { if (!glXMakeCurrent_(tempDpy_, tempDrawable_, tempCtx_)) { LogWarning("cannot restore original GL environment"); return false; } } else { // Just release internal context if (!glXMakeCurrent_(getIntDpy(), None, NULL)) { LogWarning("cannot reelase internal GL environment"); return false; } } #endif //!_WIN32 return true; } } //namespace amd