Bindless Image Experimental Extension
Contents
Bindless Image Experimental Extension¶
API¶
Enumerations
Structures
Functions
Bindless Images¶
A bindless image is defined as one which provides access to the underlying data via image reference handles. At the application level, this allows the user to implement programs where the number of images is not known at compile-time, and store all handles to images irrespective of varying formats and layouts in some container such as a dynamic array.
Currently, in Level Zero, zeImageCreate performs the image memory allocation and image handle generation. This function only allows for the allocation of image memory in an implementation-specific layout.
- In this extension, we propose the following additions:
Provide a new image descriptor and flags for Bindless images.
Support for creation of images on linearly allocated memory backed by USM.
Extension API to create an image handle from pitched memory
A “Bindless image” can be created by passing ze_image_bindless_exp_desc_t to pNext member of ze_image_desc_t and set the flags value as ZE_IMAGE_BINDLESS_EXP_FLAG_BINDLESS
This extension is complimentary to and may be used in conjunction with the ZE_extension_image_view extension
Programming example with Bindless images¶
// Assumed image data on host std::vector<float> imageDataHost; // 2D image dimensions size_t imageWidth = 1024; size_t imageHeight = 1024; // Single-precision float image format with one channel ze_image_format_t imageFormat = { ZE_IMAGE_FORMAT_LAYOUT_32, ZE_IMAGE_FORMAT_TYPE_FLOAT, ZE_IMAGE_FORMAT_SWIZZLE_R, ZE_IMAGE_FORMAT_SWIZZLE_X, ZE_IMAGE_FORMAT_SWIZZLE_R, ZE_IMAGE_FORMAT_SWIZZLE_X } // Create an image descriptor for bindless image ze_image_desc_t imageDesc = { ZE_STRUCTURE_TYPE_IMAGE_DESC, nullptr, 0, ZE_IMAGE_TYPE_2D, imageFormat, 128, 128, 0, 0, 0 }; ze_image_bindless_exp_desc_t bindlessImageDesc = {ZE_STRUCTURE_TYPE_BINDLESS_IMAGE_EXP_DESC}; bindlessImageDesc.flags = ZE_IMAGE_BINDLESS_EXP_FLAG_BINDLESS; imageDesc.pNext = &bindlessImageDesc; // A bindless image is valid on both host and device and can be passed into kernels // When passing ZE_IMAGE_BINDLESS_EXP_FLAG_BINDLESS to zeImageCreate, only the backing memory is allocated for Image ze_image_handle_t hImage; zeImageCreate(hContext, hDevice, &imageDesc, &hImage); //Copy To new bindless image memory zeCommandListAppendImageCopyFromMemory(hCommandlist, hImage, imageDataHost.data(), nullptr, nullptr, 0, nullptr); // Launch kernel and perform appropriate synchronizations // Copy back zeCommandListAppendImageCopyToMemory(hCommandlist, imageDataHost.data(), hImage, nullptr, nullptr, 0, nullptr); // Further image views can be created from the existing memory allocated using bindless flags ze_image_handle_t hImageView; zeImageViewCreateExt(hContext, hDevice, &imageDesc, hImage, &hImageView); // New image view can be separately used by users and destroyed // ... // Once all operations are complete we need destroy bindless image handle(s) zeImageDestroy(hImageView); zeImageDestroy(hImage);
Programming example with pitched memory usage¶
// Retrieve pitched alloc properties specific to device ze_device_image_properties_t deviceImageProperties = {}; ze_device_pitched_alloc_exp_properties_t pitchedAllocProperties = {}; pitchedAllocProperties.stype = ZE_STRUCTURE_TYPE_PITCHED_ALLOC_DEVICE_EXP_PROPERTIES deviceImageProperties.pNext = &pitchedAllocProperties; zeDeviceGetImageProperties(hDevice, &deviceImageProperties); // Assumed image data on host std::vector<float> imageDataHost; // 2D image dimensions size_t imageWidth = 1024; size_t imageHeight = 1024; //Pitched memory in linear layout size_t rowPitch; unsigned int elementSize = 128; zeMemGetPitchFor2dImage(hContext, hDevice, imageWidth, imageHeight, elementSize, &rowPitch); size_t allocSize = rowPitch * imageHeight; ze_device_mem_alloc_desc_t allocDesc = {ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC}; zeMemAllocDevice(hContext, &allocDesc, allocSize, allocSize, hDevice, &pitchedPtr); // Declare the copy region for copying ze_copy_region_t copyRegion = {0, 0, 0, imageWidth * sizeof(float), imageHeight, 0}; // Copy from host to device zeCommandListAppendMemoryCopyRegion(hCommandList, pitchedPtr, ©Region, rowPitch, 0, imageDataHost.data(), ©Region, imageWidth * sizeof(float), 0, nullptr, 0, nullptr); // Single-precision float image format with one channel ze_image_format_t imageFormat = { ZE_IMAGE_FORMAT_LAYOUT_32, ZE_IMAGE_FORMAT_TYPE_FLOAT, ZE_IMAGE_FORMAT_SWIZZLE_R, ZE_IMAGE_FORMAT_SWIZZLE_X, ZE_IMAGE_FORMAT_SWIZZLE_R, ZE_IMAGE_FORMAT_SWIZZLE_X } // Create an image descriptor for bindless image ze_image_desc_t imageDesc = { ZE_STRUCTURE_TYPE_IMAGE_DESC, nullptr, 0, ZE_IMAGE_TYPE_2D, imageFormat, 128, 128, 0, 0, 0 }; ze_image_pitched_exp_desc_t pitchedImageDesc = {ZE_STRUCTURE_TYPE_PITCHED_IMAGE_EXP_DESC}; pitchedImageDesc.ptr = pitchedPtr; imageDesc.pNext = &pitchedImageDesc; // A image created out of pitched memory is valid on both host and device and can be passed into kernels ze_image_handle_t hImage; zeImageCreate(hContext, hDevice, &imageDesc, &hImage); // Launch kernel and perform appropriate synchronizations // ... // Copy from device to host zeCommandListAppendMemoryCopyRegion(hCommandList, imageDataHost.data(), ©Region, imageWidth * sizeof(float), 0, pitchedPtr, ©Region, rowPitch, 0, nullptr, 0, nullptr); // Once all operations on the image are complete we need destroy image handle and free memory zeImageDestroy(hImage); zeMemFree(hContext, pitchedPtr);