Skip to content

[SYCL][Bindless][UR][E2E] Add image memory and handle support queries #17865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6491544
Add image memory and handle support queries
przemektmalon Mar 13, 2025
4581989
Merge branch 'sycl' into przemek/image-support-queries
przemektmalon Apr 7, 2025
e7a57b0
Fix include
przemektmalon Apr 7, 2025
b054575
Pass image_descriptor by ref
przemektmalon Apr 7, 2025
1824593
Fix return value for skipping read_2D test
przemektmalon Apr 7, 2025
e0fc137
Use ur_bool_t
przemektmalon Apr 8, 2025
630ebb4
Merge branch 'sycl' into przemek/image-support-queries
przemektmalon Apr 8, 2025
65cb5b4
Update use of ur_bool_t
przemektmalon Apr 8, 2025
0606014
Combine UR memory allocation queries. Use more UR helper functions.
przemektmalon Apr 8, 2025
8bbe186
Fix changelog function name
przemektmalon Apr 8, 2025
c26503c
Update func enum values in registry.yml
przemektmalon Apr 8, 2025
2bd03cc
Update unused, and maybe unused, function parameters
przemektmalon Apr 8, 2025
8334791
Update maybe unused function parameters
przemektmalon Apr 8, 2025
b66f94f
Update maybe unused function parameters
przemektmalon Apr 8, 2025
cf22079
Fix DX12 test LIT invocation
przemektmalon Apr 8, 2025
c6a54b3
Merge branch 'sycl' into przemek/image-support-queries
przemektmalon Apr 23, 2025
2a9a4c6
Fix DX12 test
przemektmalon Apr 23, 2025
38035cc
Format
przemektmalon Apr 23, 2025
baaf4c4
Merge branch 'sycl' into przemek/image-support-queries
przemektmalon Apr 24, 2025
e7449a9
Change query name
przemektmalon Apr 29, 2025
e5970c4
Merge branch 'sycl' into przemek/image-support-queries
przemektmalon Apr 29, 2025
c29abd9
Update symbols
przemektmalon Apr 29, 2025
15dba5a
Format
przemektmalon Apr 29, 2025
a1b15b1
Remove commented code. Revert unintentional format
przemektmalon May 2, 2025
a7f57c8
Fix format
przemektmalon May 2, 2025
a63ced3
EOF newline
przemektmalon May 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,78 @@ type of the returned `image_descriptor` will be `image_type::standard`.

Only array image types support more than one array layer.

==== Querying image support

Not all devices support all combinations of image channel type, the number of
channels, the type of backing memory, and dimensionality. We provide functions
to query device support for the allocation and creation of images for a given
`image_descriptor` and the type of backing memory.

===== Querying image memory support

Before allocating memory for an image, the user may first query whether their
desired image backing memory type is supported by the device.

The following query returns a vector of supported `image_memory_handle_type`s
based on the properties of a given `image_descriptor`.

The `image_memory_handle_type::usm_pointer` relates to USM allocations, while
the `image_memory_handle_type::opaque_handle` relates to memory allocations of
the `image_mem_handle` type.

If the returned vector is empty, this indicates that the device does not support
allocating or creating images for the specified `image_descriptor`.

```cpp
namespace sycl::ext::oneapi::experimental {

enum class image_memory_handle_type : /* unspecified */ {
usm_pointer,
opaque_handle
};

std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::device &syclDevice,
const sycl::context &syclContext);

std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::queue &syclQueue);
}
```

===== Querying image handle support

In order to query what types of image handles are supported for a combination
of a given `image_descriptor` and `image_memory_handle_type`, the user should
use the `is_image_handle_supported` query.

The template parameter passed to this query should be either
`unsampled_image_handle` or `sampled_image_handle`.

The boolean value returned from the query indicates whether the device supports
creating the given image handle type (sampled or unsampled) given the specified
`image_descriptor` and `image_memory_handle_type`.

```cpp
namespace sycl::ext::oneapi::experimental {

template <typename ImageHandleType>
bool
is_image_handle_supported(const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::device &syclDevice,
const sycl::context &syclContext);

template <typename ImageHandleType>
bool
is_image_handle_supported(const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::queue &syclQueue);
}
```

=== Allocating image memory

The process of creating an image is two-fold:
Expand Down
83 changes: 83 additions & 0 deletions sycl/include/sycl/ext/oneapi/bindless_images.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,89 @@ __SYCL_EXPORT unsigned int
get_image_num_channels(const image_mem_handle memHandle,
const sycl::queue &syclQueue);

/**
* @brief Returns a vector of image-backing memory types supported by the
* device for a given `image_descriptor`. If the returned vector is
* empty, it indicates that the device does not support allocating or
* creating images with the properties described in the
* `image_descriptor`.
*
* @param imageDescriptor Properties of the image we want to query support
* for.
* @param syclDevice The device in which we created our image memory handle
* @param syclContext The context in which we created our image memory handle
* @return List of supported image-backing memory types
*/
__SYCL_EXPORT std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::device &syclDevice,
const sycl::context &syclContext);

/**
* @brief Returns a vector of image-backing memory types supported by the
* device for a given `image_descriptor`. If the returned vector is
* empty, it indicates that the device does not support allocating or
* creating images with the properties described in the
* `image_descriptor`.
*
* @param imageDescriptor Properties of the image we want to query support
* for.
* @param syclQueue The device/context association for which we want to query
* image memory support.
* @return List of supported image-backing memory types
*/
__SYCL_EXPORT std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::queue &syclQueue);

/**
* @brief Returns `true` if the device supports creation of images of the
* ImageHandleType, given the combination of `image_descriptor` and
* `image_memory_handle_type`.
*
* @tparam ImageHandleType Either `sampled_image_handle` or
* `unsampled_image_handle`.
* @param imageDescriptor Properties of the image we want to query support
* for.
* @param imageMemoryHandleType Image memory handle type we want to query
* support for.
* @param syclDevice The device in which we want to query image handle
* support
* @param syclContext The context in which we want to query image handle
* support
* @return Boolean indicating support for image creation with the specified
* parameter.
*/

template <typename ImageHandleType>
__SYCL_EXPORT bool
is_image_handle_supported(const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::device &syclDevice,
const sycl::context &syclContext);

/**
* @brief Returns `true` if the device supports creation of images of the
* ImageHandleType, given the combination of `image_descriptor` and
* `image_memory_handle_type`.
*
* @tparam ImageHandleType Either `sampled_image_handle` or
* `unsampled_image_handle`
* @param imageDescriptor Properties of the image we want to query support
* for.
* @param imageMemoryHandleType Image memory handle type we want to query
* support for.
* @param syclQueue The device/context association for which we want to query
* image handle support.
* @return Boolean indicating support for image creation with the specified
* parameter.
*/
template <typename ImageHandleType>
__SYCL_EXPORT bool
is_image_handle_supported(const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::queue &syclQueue);

namespace detail {

// is sycl::vec
Expand Down
6 changes: 6 additions & 0 deletions sycl/include/sycl/ext/oneapi/bindless_images_memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ enum image_copy_flags : unsigned int {
DtoD = 2,
};

// The types of handles to image-backing memory
enum class image_memory_handle_type : unsigned int {
usm_pointer = 0,
opaque_handle = 1,
};

} // namespace ext::oneapi::experimental
} // namespace _V1
} // namespace sycl
Expand Down
130 changes: 130 additions & 0 deletions sycl/source/detail/bindless_images.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,136 @@ get_image_num_channels(const image_mem_handle memHandle,
syclQueue.get_context());
}

__SYCL_EXPORT std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::device &syclDevice,
const sycl::context &syclContext) {
std::shared_ptr<sycl::detail::device_impl> DevImpl =
sycl::detail::getSyclObjImpl(syclDevice);
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
sycl::detail::getSyclObjImpl(syclContext);
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();

ur_image_desc_t urDesc;
ur_image_format_t urFormat;
populate_ur_structs(imageDescriptor, urDesc, urFormat);

ur_bool_t supportsPointerAllocation{0};
Adapter->call<sycl::errc::runtime,
sycl::detail::UrApiKind::
urBindlessImagesGetImageMemoryHandleTypeSupportExp>(
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER,
&supportsPointerAllocation);

ur_bool_t supportsOpaqueAllocation{0};
Adapter->call<sycl::errc::runtime,
sycl::detail::UrApiKind::
urBindlessImagesGetImageMemoryHandleTypeSupportExp>(
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE,
&supportsOpaqueAllocation);

std::vector<image_memory_handle_type> supportedMemHandleTypes;

if (supportsPointerAllocation) {
supportedMemHandleTypes.push_back(image_memory_handle_type::usm_pointer);
}

if (supportsOpaqueAllocation) {
supportedMemHandleTypes.push_back(image_memory_handle_type::opaque_handle);
}

return supportedMemHandleTypes;
}

__SYCL_EXPORT std::vector<image_memory_handle_type>
get_image_memory_support(const image_descriptor &imageDescriptor,
const sycl::queue &syclQueue) {
return get_image_memory_support(imageDescriptor, syclQueue.get_device(),
syclQueue.get_context());
}

template <>
__SYCL_EXPORT bool is_image_handle_supported<unsampled_image_handle>(
const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::device &syclDevice, const sycl::context &syclContext) {
std::shared_ptr<sycl::detail::device_impl> DevImpl =
sycl::detail::getSyclObjImpl(syclDevice);
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
sycl::detail::getSyclObjImpl(syclContext);
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();

ur_image_desc_t urDesc;
ur_image_format_t urFormat;
populate_ur_structs(imageDescriptor, urDesc, urFormat);

const ur_exp_image_mem_type_t memHandleType =
(imageMemoryHandleType == image_memory_handle_type::opaque_handle)
? ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE
: ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER;

ur_bool_t supportsUnsampledHandle{0};
Adapter->call<sycl::errc::runtime,
sycl::detail::UrApiKind::
urBindlessImagesGetImageUnsampledHandleSupportExp>(
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
memHandleType, &supportsUnsampledHandle);

return supportsUnsampledHandle;
}

template <>
__SYCL_EXPORT bool is_image_handle_supported<unsampled_image_handle>(
const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::queue &syclQueue) {
return is_image_handle_supported<unsampled_image_handle>(
imageDescriptor, imageMemoryHandleType, syclQueue.get_device(),
syclQueue.get_context());
}

template <>
__SYCL_EXPORT bool is_image_handle_supported<sampled_image_handle>(
const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::device &syclDevice, const sycl::context &syclContext) {
std::shared_ptr<sycl::detail::device_impl> DevImpl =
sycl::detail::getSyclObjImpl(syclDevice);
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
sycl::detail::getSyclObjImpl(syclContext);
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();

ur_image_desc_t urDesc;
ur_image_format_t urFormat;
populate_ur_structs(imageDescriptor, urDesc, urFormat);

const ur_exp_image_mem_type_t memHandleType =
(imageMemoryHandleType == image_memory_handle_type::opaque_handle)
? ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE
: ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER;

ur_bool_t supportsSampledHandle{0};
Adapter->call<
sycl::errc::runtime,
sycl::detail::UrApiKind::urBindlessImagesGetImageSampledHandleSupportExp>(
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
memHandleType, &supportsSampledHandle);

return supportsSampledHandle;
}

template <>
__SYCL_EXPORT bool is_image_handle_supported<sampled_image_handle>(
const image_descriptor &imageDescriptor,
image_memory_handle_type imageMemoryHandleType,
const sycl::queue &syclQueue) {
return is_image_handle_supported<sampled_image_handle>(
imageDescriptor, imageMemoryHandleType, syclQueue.get_device(),
syclQueue.get_context());
}

} // namespace ext::oneapi::experimental
} // namespace _V1
} // namespace sycl
27 changes: 16 additions & 11 deletions sycl/test-e2e/bindless_images/3_channel_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <iostream>
#include <sycl/detail/core.hpp>

#include "helpers/common.hpp"
#include <sycl/ext/oneapi/bindless_images.hpp>

// Uncomment to print additional test information
Expand Down Expand Up @@ -45,14 +46,26 @@ int main() {
syclexp::image_descriptor desc({width}, 3,
sycl::image_channel_type::unsigned_int16);

// Verify ability to allocate the above image descriptor
if (!bindless_helpers::memoryAllocationSupported(
desc, syclexp::image_memory_handle_type::opaque_handle, q)) {
// We cannot allocate the opaque `image_mem` below
// Skip the test
if (ctxt.get_backend() == sycl::backend::ext_oneapi_cuda) {
std::cout << "CUDA doesn't support 3-channel formats. Skipping test.\n";
} else {
std::cout << "Memory allocation unsupported. Skipping test.\n";
}
return 0;
}

syclexp::image_mem imgMem(desc, dev, ctxt);

q.ext_oneapi_copy(dataIn.data(), imgMem.get_handle(), desc);
q.wait_and_throw();

// Some backends don't support 3-channel formats
// We still try to create the image,
// but we expect it to fail with UR_RESULT_ERROR_UNSUPPORTED_IMAGE_FORMAT
// Backends which do not support 3-channel formats will have been skipped
// with the check above.
syclexp::unsampled_image_handle imgHandle =
sycl::ext::oneapi::experimental::create_image(imgMem, desc, dev, ctxt);

Expand All @@ -77,14 +90,6 @@ int main() {

} catch (const sycl::exception &ex) {
const std::string_view errMsg(ex.what());
if (ctxt.get_backend() == sycl::backend::ext_oneapi_cuda) {
if (errMsg.find("UR_RESULT_ERROR_UNSUPPORTED_IMAGE_FORMAT") !=
std::string::npos) {
std::cout << "CUDA doesn't support 3-channel formats, test passed."
<< std::endl;
return 0;
}
}
std::cerr << "Unexpected SYCL exception: " << errMsg << "\n";
return 1;
} catch (...) {
Expand Down
Loading