// // Copyright (c) 2022 The Khronos Group Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include #include "opencl_vulkan_wrapper.hpp" #include "vulkan_wrapper.hpp" #include "harness/errorHelpers.h" #include "harness/deviceInfo.h" #include #include #include #define ASSERT(x) assert((x)) #define GB(x) ((unsigned long long)(x) << 30) pfnclCreateSemaphoreWithPropertiesKHR clCreateSemaphoreWithPropertiesKHRptr; pfnclEnqueueWaitSemaphoresKHR clEnqueueWaitSemaphoresKHRptr; pfnclEnqueueSignalSemaphoresKHR clEnqueueSignalSemaphoresKHRptr; pfnclEnqueueAcquireExternalMemObjectsKHR clEnqueueAcquireExternalMemObjectsKHRptr; pfnclEnqueueReleaseExternalMemObjectsKHR clEnqueueReleaseExternalMemObjectsKHRptr; pfnclReleaseSemaphoreKHR clReleaseSemaphoreKHRptr; pfnclGetSemaphoreHandleForTypeKHR clGetSemaphoreHandleForTypeKHRptr; pfnclReImportSemaphoreSyncFdKHR clReImportSemaphoreSyncFdKHRptr; void init_cl_vk_ext(cl_platform_id opencl_platform, cl_uint num_devices, cl_device_id *deviceIds) { clEnqueueWaitSemaphoresKHRptr = (pfnclEnqueueWaitSemaphoresKHR)clGetExtensionFunctionAddressForPlatform( opencl_platform, "clEnqueueWaitSemaphoresKHR"); if (NULL == clEnqueueWaitSemaphoresKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clEnqueueWaitSemaphoresKHRptr!"); } clEnqueueSignalSemaphoresKHRptr = (pfnclEnqueueSignalSemaphoresKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clEnqueueSignalSemaphoresKHR"); if (NULL == clEnqueueSignalSemaphoresKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clEnqueueSignalSemaphoresKHRptr!"); } clReleaseSemaphoreKHRptr = (pfnclReleaseSemaphoreKHR)clGetExtensionFunctionAddressForPlatform( opencl_platform, "clReleaseSemaphoreKHR"); if (NULL == clReleaseSemaphoreKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clReleaseSemaphoreKHRptr!"); } clCreateSemaphoreWithPropertiesKHRptr = (pfnclCreateSemaphoreWithPropertiesKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clCreateSemaphoreWithPropertiesKHR"); if (NULL == clCreateSemaphoreWithPropertiesKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clCreateSemaphoreWithPropertiesKHRptr!"); } clGetSemaphoreHandleForTypeKHRptr = (pfnclGetSemaphoreHandleForTypeKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clGetSemaphoreHandleForTypeKHR"); if (NULL == clGetSemaphoreHandleForTypeKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clGetSemaphoreHandleForTypeKHRptr!"); } // Required only if cl_khr_external_semaphore_sync_fd is reported clReImportSemaphoreSyncFdKHRptr = (pfnclReImportSemaphoreSyncFdKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clReImportSemaphoreSyncFdKHR"); for (cl_uint i = 0; i < num_devices; i++) { if (is_extension_available(deviceIds[i], "cl_khr_external_semaphore_sync_fd") && (NULL == clReImportSemaphoreSyncFdKHRptr)) { throw std::runtime_error("Failed to get the function pointer of " "clReImportSemaphoreSyncFdKHR!"); } } clEnqueueAcquireExternalMemObjectsKHRptr = (pfnclEnqueueAcquireExternalMemObjectsKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clEnqueueAcquireExternalMemObjectsKHR"); if (nullptr == clEnqueueAcquireExternalMemObjectsKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clEnqueueAcquireExternalMemObjectsKHR!"); } clEnqueueReleaseExternalMemObjectsKHRptr = (pfnclEnqueueReleaseExternalMemObjectsKHR) clGetExtensionFunctionAddressForPlatform( opencl_platform, "clEnqueueReleaseExternalMemObjectsKHR"); if (nullptr == clEnqueueReleaseExternalMemObjectsKHRptr) { throw std::runtime_error("Failed to get the function pointer of " "clEnqueueReleaseExternalMemObjectsKHR!"); } } cl_int setMaxImageDimensions(cl_device_id deviceID, size_t &max_width, size_t &max_height) { cl_int result = CL_SUCCESS; cl_ulong val; size_t paramSize; result = clGetDeviceInfo(deviceID, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &val, ¶mSize); if (result != CL_SUCCESS) { return result; } if (val < GB(4)) { max_width = 256; max_height = 256; } else if (val < GB(8)) { max_width = 512; max_height = 256; } else { max_width = 1024; max_height = 512; } return result; } cl_int getCLFormatFromVkFormat(VkFormat vkFormat, cl_image_format *clImageFormat) { cl_int result = CL_SUCCESS; switch (vkFormat) { case VK_FORMAT_R8G8B8A8_UNORM: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNORM_INT8; break; case VK_FORMAT_B8G8R8A8_UNORM: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_UNORM_INT8; break; case VK_FORMAT_R16G16B16A16_UNORM: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNORM_INT16; break; case VK_FORMAT_R8G8B8A8_SINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_SIGNED_INT8; break; case VK_FORMAT_R16G16B16A16_SINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_SIGNED_INT16; break; case VK_FORMAT_R32G32B32A32_SINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_SIGNED_INT32; break; case VK_FORMAT_R8G8B8A8_UINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT8; break; case VK_FORMAT_R16G16B16A16_UINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT16; break; case VK_FORMAT_R32G32B32A32_UINT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; break; case VK_FORMAT_R16G16B16A16_SFLOAT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_HALF_FLOAT; break; case VK_FORMAT_R32G32B32A32_SFLOAT: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_FLOAT; break; case VK_FORMAT_R8_SNORM: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_SNORM_INT8; break; case VK_FORMAT_R16_SNORM: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_SNORM_INT16; break; case VK_FORMAT_R8_UNORM: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_UNORM_INT8; break; case VK_FORMAT_R16_UNORM: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_UNORM_INT16; break; case VK_FORMAT_R8_SINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_SIGNED_INT8; break; case VK_FORMAT_R16_SINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_SIGNED_INT16; break; case VK_FORMAT_R32_SINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_SIGNED_INT32; break; case VK_FORMAT_R8_UINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT8; break; case VK_FORMAT_R16_UINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT16; break; case VK_FORMAT_R32_UINT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; break; case VK_FORMAT_R16_SFLOAT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_HALF_FLOAT; break; case VK_FORMAT_R32_SFLOAT: clImageFormat->image_channel_order = CL_R; clImageFormat->image_channel_data_type = CL_FLOAT; break; case VK_FORMAT_R8G8_SNORM: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_SNORM_INT8; break; case VK_FORMAT_R16G16_SNORM: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_SNORM_INT16; break; case VK_FORMAT_R8G8_UNORM: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_UNORM_INT8; break; case VK_FORMAT_R16G16_UNORM: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_UNORM_INT16; break; case VK_FORMAT_R8G8_SINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_SIGNED_INT8; break; case VK_FORMAT_R16G16_SINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_SIGNED_INT16; break; case VK_FORMAT_R32G32_SINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_SIGNED_INT32; break; case VK_FORMAT_R8G8_UINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT8; break; case VK_FORMAT_R16G16_UINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT16; break; case VK_FORMAT_R32G32_UINT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT32; break; case VK_FORMAT_R16G16_SFLOAT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_HALF_FLOAT; break; case VK_FORMAT_R32G32_SFLOAT: clImageFormat->image_channel_order = CL_RG; clImageFormat->image_channel_data_type = CL_FLOAT; break; case VK_FORMAT_R5G6B5_UNORM_PACK16: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNORM_SHORT_565; break; case VK_FORMAT_R5G5B5A1_UNORM_PACK16: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_UNORM_SHORT_555; break; case VK_FORMAT_R8G8B8A8_SNORM: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_SNORM_INT8; break; case VK_FORMAT_R16G16B16A16_SNORM: clImageFormat->image_channel_order = CL_RGBA; clImageFormat->image_channel_data_type = CL_SNORM_INT16; break; case VK_FORMAT_B8G8R8A8_SNORM: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_SNORM_INT8; break; case VK_FORMAT_B5G6R5_UNORM_PACK16: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_UNORM_SHORT_565; break; case VK_FORMAT_B5G5R5A1_UNORM_PACK16: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_UNORM_SHORT_555; break; case VK_FORMAT_B8G8R8A8_SINT: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_SIGNED_INT8; break; case VK_FORMAT_B8G8R8A8_UINT: clImageFormat->image_channel_order = CL_BGRA; clImageFormat->image_channel_data_type = CL_UNSIGNED_INT8; break; case VK_FORMAT_A8B8G8R8_SNORM_PACK32: result = CL_INVALID_VALUE; break; case VK_FORMAT_A8B8G8R8_UNORM_PACK32: result = CL_INVALID_VALUE; break; case VK_FORMAT_A8B8G8R8_SINT_PACK32: result = CL_INVALID_VALUE; break; case VK_FORMAT_A8B8G8R8_UINT_PACK32: result = CL_INVALID_VALUE; break; default: log_error("Unsupported format\n"); ASSERT(0); break; } return result; } cl_mem_object_type getImageTypeFromVk(VkImageType imageType) { cl_mem_object_type cl_image_type = CL_INVALID_VALUE; switch (imageType) { case VK_IMAGE_TYPE_1D: cl_image_type = CL_MEM_OBJECT_IMAGE1D; break; case VK_IMAGE_TYPE_2D: cl_image_type = CL_MEM_OBJECT_IMAGE2D; break; case VK_IMAGE_TYPE_3D: cl_image_type = CL_MEM_OBJECT_IMAGE3D; break; default: break; } return cl_image_type; } size_t GetElementNBytes(const cl_image_format *format) { size_t result; switch (format->image_channel_order) { case CL_R: case CL_A: case CL_INTENSITY: case CL_LUMINANCE: case CL_DEPTH: result = 1; break; case CL_RG: case CL_RA: result = 2; break; case CL_RGB: result = 3; break; case CL_RGBA: case CL_ARGB: case CL_BGRA: case CL_sRGBA: result = 4; break; default: result = 0; break; } switch (format->image_channel_data_type) { case CL_SNORM_INT8: case CL_UNORM_INT8: case CL_SIGNED_INT8: case CL_UNSIGNED_INT8: // result *= 1; break; case CL_SNORM_INT16: case CL_UNORM_INT16: case CL_SIGNED_INT16: case CL_UNSIGNED_INT16: case CL_HALF_FLOAT: result *= 2; break; case CL_SIGNED_INT32: case CL_UNSIGNED_INT32: case CL_FLOAT: result *= 4; break; case CL_UNORM_SHORT_565: case CL_UNORM_SHORT_555: if (result == 3) { result = 2; } else { result = 0; } break; case CL_UNORM_INT_101010: if (result == 3) { result = 4; } else { result = 0; } break; default: result = 0; break; } return result; } cl_int getImageDimensions(const VkImageCreateInfo *VulkanImageCreateInfo, cl_image_format *img_fmt, cl_image_desc *img_desc, VkExtent3D max_ext, const VkSubresourceLayout *layout) { test_assert_error( VulkanImageCreateInfo != nullptr, "getImageDimensions: invalid VulkanImageCreateInfo pointer!"); test_assert_error(img_fmt != nullptr, "getImageDimensions: invalid img_fmt pointer!"); test_assert_error(img_desc != nullptr, "getImageDimensions: invalid img_desc pointer!"); img_desc->image_depth = VulkanImageCreateInfo->extent.depth; img_desc->image_width = VulkanImageCreateInfo->extent.width; img_desc->image_height = VulkanImageCreateInfo->extent.height; if (layout != nullptr) { size_t element_size = GetElementNBytes(img_fmt); size_t row_pitch = element_size * VulkanImageCreateInfo->extent.width; row_pitch = row_pitch < layout->rowPitch ? layout->rowPitch : row_pitch; img_desc->image_row_pitch = row_pitch; size_t slice_pitch = row_pitch * VulkanImageCreateInfo->extent.height; slice_pitch = slice_pitch < layout->depthPitch ? layout->depthPitch : slice_pitch; img_desc->image_slice_pitch = slice_pitch; } switch (img_desc->image_type) { case CL_MEM_OBJECT_IMAGE3D: test_assert_error(img_desc->image_depth >= 1 && img_desc->image_depth <= max_ext.depth, "getImageDimensions: invalid image depth!"); case CL_MEM_OBJECT_IMAGE2D: test_assert_error(img_desc->image_height >= 1 && img_desc->image_height <= max_ext.height, "getImageDimensions: invalid image height!"); case CL_MEM_OBJECT_IMAGE1D: test_assert_error(img_desc->image_width >= 1 && img_desc->image_width <= max_ext.width, "getImageDimensions: invalid image width!"); } return CL_SUCCESS; } cl_int getCLImageInfoFromVkImageInfo(const cl_device_id device, const VkImageCreateInfo *VulkanImageCreateInfo, cl_image_format *img_fmt, cl_image_desc *img_desc, const VkSubresourceLayout *layout) { cl_int error = CL_SUCCESS; cl_image_format clImgFormat = { 0 }; error = getCLFormatFromVkFormat(VulkanImageCreateInfo->format, &clImgFormat); if (CL_SUCCESS != error) { return error; } memcpy(img_fmt, &clImgFormat, sizeof(cl_image_format)); img_desc->image_type = getImageTypeFromVk(VulkanImageCreateInfo->imageType); if (CL_INVALID_VALUE == static_cast(img_desc->image_type)) { return CL_INVALID_VALUE; } VkExtent3D max_ext = { 0, 0, 0 }; size_t width = 0, height = 0, depth = 0; if (img_desc->image_type == CL_MEM_OBJECT_IMAGE3D) { error = clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_WIDTH, sizeof(width), &width, NULL); test_error(error, "Unable to get CL_DEVICE_IMAGE3D_MAX_WIDTH"); error = clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_HEIGHT, sizeof(height), &height, NULL); test_error(error, "Unable to get CL_DEVICE_IMAGE3D_MAX_HEIGHT"); error = clGetDeviceInfo(device, CL_DEVICE_IMAGE3D_MAX_DEPTH, sizeof(depth), &depth, NULL); test_error(error, "Unable to get CL_DEVICE_IMAGE3D_MAX_DEPTH"); } else { error = clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(width), &width, NULL); test_error(error, "Unable to get CL_DEVICE_IMAGE2D_MAX_WIDTH"); error = clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(height), &height, NULL); test_error(error, "Unable to get CL_DEVICE_IMAGE2D_MAX_HEIGHT"); } max_ext.depth = depth; max_ext.height = height; max_ext.width = width; // If image_row_pitch is zero and the image is created from an external // memory handle, then the image row pitch is implementation-defined img_desc->image_row_pitch = 0; // If image_slice_pitch is zero and the image is created from an external // memory handle, then the image slice pitch is implementation-defined img_desc->image_slice_pitch = 0; error = getImageDimensions(VulkanImageCreateInfo, img_fmt, img_desc, max_ext, layout); if (CL_SUCCESS != error) { throw std::runtime_error("getImageDimensions failed!!!"); } img_desc->image_depth = static_cast(VulkanImageCreateInfo->extent.depth); img_desc->image_array_size = 0; img_desc->num_mip_levels = 0; img_desc->num_samples = 0; img_desc->buffer = NULL; return error; } cl_int check_external_memory_handle_type( cl_device_id deviceID, cl_external_memory_handle_type_khr requiredHandleType) { unsigned int i; cl_external_memory_handle_type_khr *handle_type; size_t handle_type_size = 0; cl_int errNum = CL_SUCCESS; errNum = clGetDeviceInfo(deviceID, CL_DEVICE_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR, 0, NULL, &handle_type_size); test_error(errNum, "clGetDeviceInfo failed"); handle_type = (cl_external_memory_handle_type_khr *)malloc(handle_type_size); errNum = clGetDeviceInfo(deviceID, CL_DEVICE_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR, handle_type_size, handle_type, NULL); test_error( errNum, "Unable to query CL_DEVICE_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR \n"); for (i = 0; i < handle_type_size; i++) { if (requiredHandleType == handle_type[i]) { return CL_SUCCESS; } } log_error("cl_khr_external_memory extension is missing support for %d\n", requiredHandleType); return CL_INVALID_VALUE; } void check_external_semaphore_handle_type( cl_device_id device, cl_external_semaphore_handle_type_khr requiredHandleType, cl_device_info queryParamName) { unsigned int i; cl_external_semaphore_handle_type_khr *handle_type; size_t handle_type_size = 0; cl_int errNum = CL_SUCCESS; errNum = clGetDeviceInfo(device, queryParamName, 0, NULL, &handle_type_size); ASSERT_SUCCESS(errNum, "clGetDeviceInfo"); if (handle_type_size == 0) { log_error("Device does not support %s semaphore\n", queryParamName == CL_DEVICE_SEMAPHORE_IMPORT_HANDLE_TYPES_KHR ? "importing" : "exporting"); throw std::runtime_error(""); } handle_type = (cl_external_semaphore_handle_type_khr *)malloc(handle_type_size); errNum = clGetDeviceInfo(device, queryParamName, handle_type_size, handle_type, NULL); ASSERT_SUCCESS(errNum, "clGetDeviceInfo"); bool found = false; for (i = 0; i < handle_type_size; i++) { if (requiredHandleType == handle_type[i]) { found = true; break; } } if (!found) { log_error("cl_khr_external_semaphore extension is missing support for " "handle type %d\n", requiredHandleType); throw std::runtime_error(""); } } clExternalMemory::clExternalMemory() {} clExternalMemory::clExternalMemory(const clExternalMemory &externalMemory) : m_externalMemory(externalMemory.m_externalMemory) {} clExternalMemory::clExternalMemory( const VulkanDeviceMemory *deviceMemory, VulkanExternalMemoryHandleType externalMemoryHandleType, uint64_t size, cl_context context, cl_device_id deviceId) { int err = 0; m_externalMemory = NULL; cl_device_id devList[] = { deviceId, NULL }; std::vector extMemProperties; #ifdef _WIN32 if (!is_extension_available(devList[0], "cl_khr_external_memory_win32")) { throw std::runtime_error( "Device does not support cl_khr_external_memory_win32 extension\n"); } #else if (!is_extension_available(devList[0], "cl_khr_external_memory_opaque_fd")) { throw std::runtime_error( "Device does not support cl_khr_external_memory_opaque_fd " "extension \n"); } #endif switch (externalMemoryHandleType) { case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD: #ifdef _WIN32 log_info("Opaque file descriptors are not supported on Windows\n"); ASSERT(0); #else fd = (int)deviceMemory->getHandle(externalMemoryHandleType); err = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR); extMemProperties.push_back( (cl_mem_properties)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR); extMemProperties.push_back((cl_mem_properties)fd); #endif break; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT: #ifndef _WIN32 log_info("Opaque NT handles are only supported on Windows\n"); ASSERT(0); #else handle = deviceMemory->getHandle(externalMemoryHandleType); err = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR); extMemProperties.push_back( (cl_mem_properties)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR); extMemProperties.push_back((cl_mem_properties)handle); #endif break; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT_NAME: { #ifndef _WIN32 log_info("Opaque NT handles are only supported on Windows\n"); ASSERT(0); #else const std::wstring &name = deviceMemory->getName(); if (name.size()) { err = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_NAME_KHR); extMemProperties.push_back( (cl_mem_properties) CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_NAME_KHR); extMemProperties.push_back((cl_mem_properties)name.c_str()); } else { throw std::runtime_error("Unsupported operation: import via " "name but no name provided\n"); } #endif } break; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT: #ifndef _WIN32 log_info("Opaque D3DKMT handles are only supported on Windows\n"); ASSERT(0); #else handle = deviceMemory->getHandle(externalMemoryHandleType); err = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR); extMemProperties.push_back( (cl_mem_properties) CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR); extMemProperties.push_back((cl_mem_properties)handle); #endif break; default: log_error("Unsupported external memory handle type\n"); ASSERT(0); break; } if (CL_SUCCESS != err) { throw std::runtime_error("Unsupported external memory type\n"); } extMemProperties.push_back( (cl_mem_properties)CL_MEM_DEVICE_HANDLE_LIST_KHR); extMemProperties.push_back((cl_mem_properties)devList[0]); extMemProperties.push_back( (cl_mem_properties)CL_MEM_DEVICE_HANDLE_LIST_END_KHR); extMemProperties.push_back(0); m_externalMemory = clCreateBufferWithProperties( context, extMemProperties.data(), 1, size, NULL, &err); if (CL_SUCCESS != err) { log_error("clCreateBufferWithProperties failed with %d\n", err); throw std::runtime_error("clCreateBufferWithProperties failed\n"); } } clExternalMemoryImage::clExternalMemoryImage( const VulkanDeviceMemory &deviceMemory, VulkanExternalMemoryHandleType externalMemoryHandleType, cl_context context, size_t totalImageMemSize, size_t imageWidth, size_t imageHeight, size_t totalSize, const VulkanImage2D &image2D, cl_device_id deviceId) { cl_int errcode_ret = 0; std::vector extMemProperties1; cl_device_id devList[] = { deviceId, NULL }; auto vulkanImageTiling = vkClExternalMemoryHandleTilingAssumption( deviceId, externalMemoryHandleType, &errcode_ret); if (CL_SUCCESS != errcode_ret) { throw std::runtime_error("Failed to query OpenCL tiling mode"); } if (vulkanImageTiling == std::nullopt) { throw std::runtime_error( "Could not find image tiling supported by both Vulkan and OpenCL"); } #ifdef _WIN32 if (!is_extension_available(devList[0], "cl_khr_external_memory_win32")) { throw std::runtime_error("Device does not support " "cl_khr_external_memory_win32 extension \n"); } #elif !defined(__APPLE__) if (!is_extension_available(devList[0], "cl_khr_external_memory_opaque_fd")) { throw std::runtime_error( "Device does not support cl_khr_external_memory_opaque_fd " "extension\n"); } #endif switch (externalMemoryHandleType) { #ifdef _WIN32 case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT: handle = deviceMemory.getHandle(externalMemoryHandleType); errcode_ret = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR); extMemProperties1.push_back( (cl_mem_properties)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR); extMemProperties1.push_back((cl_mem_properties)handle); break; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT_NAME: { const std::wstring &name = deviceMemory.getName(); if (name.size()) { errcode_ret = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_NAME_KHR); extMemProperties1.push_back( (cl_mem_properties) CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_NAME_KHR); extMemProperties1.push_back((cl_mem_properties)name.c_str()); } else { throw std::runtime_error("Unsupported operation: import via " "name but no name provided\n"); } } break; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT: handle = deviceMemory.getHandle(externalMemoryHandleType); errcode_ret = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR); extMemProperties1.push_back( (cl_mem_properties) CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR); extMemProperties1.push_back((cl_mem_properties)handle); break; #elif !defined(__APPLE__) case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD: fd = (int)deviceMemory.getHandle(externalMemoryHandleType); errcode_ret = check_external_memory_handle_type( devList[0], CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR); extMemProperties1.push_back( (cl_mem_properties)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR); extMemProperties1.push_back((cl_mem_properties)fd); break; #endif default: log_error("Unsupported external memory handle type\n"); ASSERT(0); break; } if (CL_SUCCESS != errcode_ret) { throw std::runtime_error("Unsupported external memory type\n"); } // Set cl_image_desc size_t clImageFormatSize; cl_image_desc image_desc; memset(&image_desc, 0x0, sizeof(cl_image_desc)); img_format = { 0 }; const VkImageCreateInfo VulkanImageCreateInfo = image2D.getVkImageCreateInfo(); errcode_ret = getCLImageInfoFromVkImageInfo( deviceId, &VulkanImageCreateInfo, &img_format, &image_desc); if (CL_SUCCESS != errcode_ret) { throw std::runtime_error("getCLImageInfoFromVkImageInfo failed\n"); } // If OpenCL will assume linear, query the Vulkan image's row pitch, // otherwise it may not match OpenCL's assumption of the row pitch. if (vulkanImageTiling == VULKAN_IMAGE_TILING_LINEAR) { VkSubresourceLayout subresourceLayout = image2D.getSubresourceLayout(); image_desc.image_row_pitch = subresourceLayout.rowPitch; image_desc.image_slice_pitch = subresourceLayout.depthPitch; } extMemProperties1.push_back( (cl_mem_properties)CL_MEM_DEVICE_HANDLE_LIST_KHR); extMemProperties1.push_back((cl_mem_properties)devList[0]); extMemProperties1.push_back( (cl_mem_properties)CL_MEM_DEVICE_HANDLE_LIST_END_KHR); extMemProperties1.push_back(0); m_externalMemory = clCreateImageWithProperties( context, extMemProperties1.data(), CL_MEM_READ_WRITE, &img_format, &image_desc, NULL, &errcode_ret); if (CL_SUCCESS != errcode_ret) { throw std::runtime_error("clCreateImageWithProperties failed\n"); } } cl_mem clExternalMemory::getExternalMemoryBuffer() { return m_externalMemory; } cl_mem clExternalMemoryImage::getExternalMemoryImage() { return m_externalMemory; } clExternalMemoryImage::~clExternalMemoryImage() { clReleaseMemObject(m_externalMemory); } clExternalMemory::~clExternalMemory() { clReleaseMemObject(m_externalMemory); } clExternalMemoryImage::clExternalMemoryImage() {} ////////////////////////////////////////// // clExternalSemaphore implementation // ////////////////////////////////////////// clExternalSemaphore::~clExternalSemaphore() = default; clExternalImportableSemaphore::clExternalImportableSemaphore( const VulkanSemaphore &semaphore, cl_context context, VulkanExternalSemaphoreHandleType externalSemaphoreHandleType, cl_device_id deviceId) : m_deviceSemaphore(semaphore) { cl_int err = 0; cl_device_id devList[] = { deviceId, NULL }; cl_external_semaphore_handle_type_khr clSemaphoreHandleType = getCLSemaphoreTypeFromVulkanType(externalSemaphoreHandleType); check_external_semaphore_handle_type( deviceId, clSemaphoreHandleType, CL_DEVICE_SEMAPHORE_IMPORT_HANDLE_TYPES_KHR); m_externalHandleType = externalSemaphoreHandleType; m_externalSemaphore = nullptr; m_device = deviceId; m_context = context; std::vector sema_props{ (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_BINARY_KHR, }; switch (externalSemaphoreHandleType) { case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD: #ifdef _WIN32 log_info("Opaque file descriptors are not supported on Windows\n"); ASSERT(0); #else fd = (int)semaphore.getHandle(externalSemaphoreHandleType); sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_HANDLE_OPAQUE_FD_KHR); sema_props.push_back((cl_semaphore_properties_khr)fd); #endif break; case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_NT: #ifndef _WIN32 log_info("Opaque NT handles are only supported on Windows\n"); ASSERT(0); #else handle = semaphore.getHandle(externalSemaphoreHandleType); sema_props.push_back((cl_semaphore_properties_khr) CL_SEMAPHORE_HANDLE_OPAQUE_WIN32_KHR); sema_props.push_back((cl_semaphore_properties_khr)handle); #endif break; case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_NT_NAME: { #ifndef _WIN32 log_info("Opaque NT handles are only supported on Windows\n"); ASSERT(0); #else const std::wstring &name = semaphore.getName(); if (name.size()) { sema_props.push_back( (cl_semaphore_properties_khr) CL_SEMAPHORE_HANDLE_OPAQUE_WIN32_NAME_KHR); sema_props.push_back((cl_semaphore_properties_khr)name.c_str()); } else { throw std::runtime_error("Unsupported operation: import via " "name but no name provided\n"); } #endif } break; case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT: #ifndef _WIN32 log_info("Opaque D3DKMT handles are only supported on Windows\n"); ASSERT(0); #else handle = semaphore.getHandle(externalSemaphoreHandleType); sema_props.push_back((cl_semaphore_properties_khr) CL_SEMAPHORE_HANDLE_OPAQUE_WIN32_KMT_KHR); sema_props.push_back((cl_semaphore_properties_khr)handle); #endif break; case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD: sema_props.push_back(static_cast( CL_SEMAPHORE_HANDLE_SYNC_FD_KHR)); sema_props.push_back(static_cast(-1)); break; default: log_error("Unsupported external memory handle type\n"); ASSERT(0); break; } if (CL_SUCCESS != err) { throw std::runtime_error( "Unsupported external sempahore handle type\n "); } sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_DEVICE_HANDLE_LIST_KHR); sema_props.push_back((cl_semaphore_properties_khr)devList[0]); sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_DEVICE_HANDLE_LIST_END_KHR); sema_props.push_back(0); m_externalSemaphore = clCreateSemaphoreWithPropertiesKHRptr(context, sema_props.data(), &err); if (CL_SUCCESS != err) { log_error("clCreateSemaphoreWithPropertiesKHRptr failed with %d\n", err); throw std::runtime_error( "clCreateSemaphoreWithPropertiesKHRptr failed! "); } } clExternalImportableSemaphore::~clExternalImportableSemaphore() { cl_int err = clReleaseSemaphoreKHRptr(m_externalSemaphore); if (err != CL_SUCCESS) { log_error("clReleaseSemaphoreKHR failed with %d\n", err); } } int clExternalImportableSemaphore::wait(cl_command_queue cmd_queue) { int err = CL_SUCCESS; if (m_externalHandleType == VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD) { cl_int err = 0; fd = (int)m_deviceSemaphore.getHandle(m_externalHandleType); err = clReImportSemaphoreSyncFdKHRptr(m_externalSemaphore, nullptr, fd); if (err != CL_SUCCESS) { return err; } } err = clEnqueueWaitSemaphoresKHRptr(cmd_queue, 1, &m_externalSemaphore, NULL, 0, NULL, NULL); return err; } int clExternalImportableSemaphore::signal(cl_command_queue cmd_queue) { return clEnqueueSignalSemaphoresKHRptr(cmd_queue, 1, &m_externalSemaphore, NULL, 0, NULL, NULL); } cl_semaphore_khr &clExternalImportableSemaphore::getCLSemaphore() { return m_externalSemaphore; } clExternalExportableSemaphore::clExternalExportableSemaphore( const VulkanSemaphore &semaphore, cl_context context, VulkanExternalSemaphoreHandleType externalSemaphoreHandleType, cl_device_id deviceId) : m_deviceSemaphore(semaphore) { cl_int err = 0; cl_device_id devList[] = { deviceId, NULL }; cl_external_semaphore_handle_type_khr clSemaphoreHandleType = getCLSemaphoreTypeFromVulkanType(externalSemaphoreHandleType); check_external_semaphore_handle_type( deviceId, clSemaphoreHandleType, CL_DEVICE_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR); m_externalHandleType = externalSemaphoreHandleType; m_externalSemaphore = nullptr; m_device = deviceId; m_context = context; std::vector sema_props{ (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_BINARY_KHR, }; sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR); sema_props.push_back((cl_semaphore_properties_khr)clSemaphoreHandleType); sema_props.push_back((cl_semaphore_properties_khr) CL_SEMAPHORE_EXPORT_HANDLE_TYPES_LIST_END_KHR); sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_DEVICE_HANDLE_LIST_KHR); sema_props.push_back((cl_semaphore_properties_khr)devList[0]); sema_props.push_back( (cl_semaphore_properties_khr)CL_SEMAPHORE_DEVICE_HANDLE_LIST_END_KHR); sema_props.push_back(0); m_externalSemaphore = clCreateSemaphoreWithPropertiesKHRptr(context, sema_props.data(), &err); if (CL_SUCCESS != err) { log_error("clCreateSemaphoreWithPropertiesKHRptr failed with %d\n", err); throw std::runtime_error( "clCreateSemaphoreWithPropertiesKHRptr failed! "); } switch (m_externalHandleType) { case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD: { err = clGetSemaphoreHandleForTypeKHRptr( m_externalSemaphore, m_device, clSemaphoreHandleType, sizeof(int), &fd, nullptr); if (err != CL_SUCCESS) { throw std::runtime_error("Failed to export OpenCL semaphore\n"); } VkImportSemaphoreFdInfoKHR vkImportSemaphoreFdInfoKHR = {}; vkImportSemaphoreFdInfoKHR.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; vkImportSemaphoreFdInfoKHR.semaphore = m_deviceSemaphore; vkImportSemaphoreFdInfoKHR.fd = fd; vkImportSemaphoreFdInfoKHR.pNext = nullptr; vkImportSemaphoreFdInfoKHR.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; vkImportSemaphoreFdInfoKHR.flags = 0; if (vkImportSemaphoreFdKHR(m_deviceSemaphore.getDevice(), &vkImportSemaphoreFdInfoKHR) != VK_SUCCESS) { throw std::runtime_error( "Failed to import semaphore in Vulkan\n"); } break; } case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT: case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_NT: { err = clGetSemaphoreHandleForTypeKHRptr( m_externalSemaphore, m_device, clSemaphoreHandleType, sizeof(void *), (void *)&handle, nullptr); if (err != CL_SUCCESS) { throw std::runtime_error("Failed to export OpenCL semaphore\n"); } #ifdef _WIN32 VkImportSemaphoreWin32HandleInfoKHR vkImportSemaphoreWin32HandleInfoKHR = {}; vkImportSemaphoreWin32HandleInfoKHR.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR; vkImportSemaphoreWin32HandleInfoKHR.pNext = nullptr; vkImportSemaphoreWin32HandleInfoKHR.semaphore = m_deviceSemaphore; vkImportSemaphoreWin32HandleInfoKHR.flags = 0; vkImportSemaphoreWin32HandleInfoKHR.handleType = (VkExternalSemaphoreHandleTypeFlagBits)m_externalHandleType; vkImportSemaphoreWin32HandleInfoKHR.handle = (HANDLE)handle; vkImportSemaphoreWin32HandleInfoKHR.name = nullptr; if (vkImportSemaphoreWin32HandleKHR( m_deviceSemaphore.getDevice(), &vkImportSemaphoreWin32HandleInfoKHR) != VK_SUCCESS) { throw std::runtime_error( "Failed to import semaphore in Vulkan\n"); } #else log_error( "Opaque D3DKMT and NT handles are only supported on Windows\n"); ASSERT(0); #endif break; } case VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD: // Do nothing, imported after each signal from OpenCL break; default: log_error("Unsupported external semaphore handle type\n"); ASSERT(0); break; } } clExternalExportableSemaphore::~clExternalExportableSemaphore() { cl_int err = clReleaseSemaphoreKHRptr(m_externalSemaphore); if (err != CL_SUCCESS) { log_error("clReleaseSemaphoreKHR failed with %d\n", err); } } int clExternalExportableSemaphore::signal(cl_command_queue cmd_queue) { int err = clEnqueueSignalSemaphoresKHRptr( cmd_queue, 1, &m_externalSemaphore, NULL, 0, NULL, nullptr); if (err != CL_SUCCESS) { return err; } if (m_externalHandleType == VULKAN_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD) { err = clGetSemaphoreHandleForTypeKHRptr(m_externalSemaphore, m_device, CL_SEMAPHORE_HANDLE_SYNC_FD_KHR, sizeof(int), &fd, nullptr); if (err != CL_SUCCESS) { log_error("Failed to export fd from semaphore\n"); return err; } VkImportSemaphoreFdInfoKHR import = {}; import.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; import.semaphore = m_deviceSemaphore; import.fd = fd; import.pNext = nullptr; import.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR; import.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; VkResult res = vkImportSemaphoreFdKHR(m_deviceSemaphore.getDevice(), &import); ASSERT(res == VK_SUCCESS); if (res != VK_SUCCESS) { err = CL_INVALID_OPERATION; } } return err; } int clExternalExportableSemaphore::wait(cl_command_queue command_queue) { return clEnqueueWaitSemaphoresKHRptr(command_queue, 1, &m_externalSemaphore, NULL, 0, NULL, nullptr); } cl_semaphore_khr &clExternalExportableSemaphore::getCLSemaphore() { return m_externalSemaphore; } cl_external_memory_handle_type_khr vkToOpenCLExternalMemoryHandleType( VulkanExternalMemoryHandleType vkExternalMemoryHandleType) { switch (vkExternalMemoryHandleType) { default: case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_NONE: log_error("Unexpected external memory handle type\n"); return 0; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD: return CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT: return CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT: case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT_KMT: return CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR; case VULKAN_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_NT_NAME: return CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_NAME_KHR; } return 0; } std::optional vkClExternalMemoryHandleTilingAssumption( cl_device_id deviceId, VulkanExternalMemoryHandleType vkExternalMemoryHandleType, int *error_ret) { size_t size = 0; assert(error_ret != nullptr); // errcode_ret is not optional, it must be checked *error_ret = clGetDeviceInfo( deviceId, CL_DEVICE_EXTERNAL_MEMORY_IMPORT_ASSUME_LINEAR_IMAGES_HANDLE_TYPES_KHR, 0, nullptr, &size); if (*error_ret != CL_SUCCESS) { return std::nullopt; } if (size == 0) { return std::nullopt; } std::vector assume_linear_types( size / sizeof(cl_external_memory_handle_type_khr)); *error_ret = clGetDeviceInfo( deviceId, CL_DEVICE_EXTERNAL_MEMORY_IMPORT_ASSUME_LINEAR_IMAGES_HANDLE_TYPES_KHR, size, assume_linear_types.data(), nullptr); if (*error_ret != CL_SUCCESS) { return std::nullopt; } if (std::find( assume_linear_types.begin(), assume_linear_types.end(), vkToOpenCLExternalMemoryHandleType(vkExternalMemoryHandleType)) != assume_linear_types.end()) { return VULKAN_IMAGE_TILING_LINEAR; } return std::nullopt; }