Skip to content

Commit 0128832

Browse files
[SYCL][Bindless][UR][E2E] Add image memory and handle support queries (#17865)
This patch introduces two new SYCL queries for Bindless Images. `get_image_memory_support` returns a vector of supported image backing memory types for a device given an `image_descriptor. `is_image_handle_supported` returns a boolean indicating whether the device supports creation of either a `sampled_image_handle` or `unsampled_image_handle`, given an `image_descriptor` and image backing memory type. Some tests are updated to use these queries to filter out unsupported image properties, e.g. allocating `unorm` channel types on the LevelZero backend. Additionally some fixes are made to the UR HIP device queries that pertain to supported image properties. The HIP queries for `UR_DEVICE_INFO_IMAGE<N>D_MAX_<WIDTH/HEIGHT/DEPTH>` have been amended to remove redundant calls to `hipDeviceGetAttribute` (as this was being called twice unnecessarily, with the same query parameter). The HIP queries for `UR_DEVICE_INFO_MAX_IMAGE_LINEAR_<WIDTH/HEIGHT/PITCH>_EXP` have been amended to return plausible values for the HIP backend (instead of returning `1` for each of these).
1 parent a4488a7 commit 0128832

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3032
-102
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_bindless_images.asciidoc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,78 @@ type of the returned `image_descriptor` will be `image_type::standard`.
259259

260260
Only array image types support more than one array layer.
261261

262+
==== Querying image support
263+
264+
Not all devices support all combinations of image channel type, the number of
265+
channels, the type of backing memory, and dimensionality. We provide functions
266+
to query device support for the allocation and creation of images for a given
267+
`image_descriptor` and the type of backing memory.
268+
269+
===== Querying image memory support
270+
271+
Before allocating memory for an image, the user may first query whether their
272+
desired image backing memory type is supported by the device.
273+
274+
The following query returns a vector of supported `image_memory_handle_type`s
275+
based on the properties of a given `image_descriptor`.
276+
277+
The `image_memory_handle_type::usm_pointer` relates to USM allocations, while
278+
the `image_memory_handle_type::opaque_handle` relates to memory allocations of
279+
the `image_mem_handle` type.
280+
281+
If the returned vector is empty, this indicates that the device does not support
282+
allocating or creating images for the specified `image_descriptor`.
283+
284+
```cpp
285+
namespace sycl::ext::oneapi::experimental {
286+
287+
enum class image_memory_handle_type : /* unspecified */ {
288+
usm_pointer,
289+
opaque_handle
290+
};
291+
292+
std::vector<image_memory_handle_type>
293+
get_image_memory_support(const image_descriptor &imageDescriptor,
294+
const sycl::device &syclDevice,
295+
const sycl::context &syclContext);
296+
297+
std::vector<image_memory_handle_type>
298+
get_image_memory_support(const image_descriptor &imageDescriptor,
299+
const sycl::queue &syclQueue);
300+
}
301+
```
302+
303+
===== Querying image handle support
304+
305+
In order to query what types of image handles are supported for a combination
306+
of a given `image_descriptor` and `image_memory_handle_type`, the user should
307+
use the `is_image_handle_supported` query.
308+
309+
The template parameter passed to this query should be either
310+
`unsampled_image_handle` or `sampled_image_handle`.
311+
312+
The boolean value returned from the query indicates whether the device supports
313+
creating the given image handle type (sampled or unsampled) given the specified
314+
`image_descriptor` and `image_memory_handle_type`.
315+
316+
```cpp
317+
namespace sycl::ext::oneapi::experimental {
318+
319+
template <typename ImageHandleType>
320+
bool
321+
is_image_handle_supported(const image_descriptor &imageDescriptor,
322+
image_memory_handle_type imageMemoryHandleType,
323+
const sycl::device &syclDevice,
324+
const sycl::context &syclContext);
325+
326+
template <typename ImageHandleType>
327+
bool
328+
is_image_handle_supported(const image_descriptor &imageDescriptor,
329+
image_memory_handle_type imageMemoryHandleType,
330+
const sycl::queue &syclQueue);
331+
}
332+
```
333+
262334
=== Allocating image memory
263335

264336
The process of creating an image is two-fold:

sycl/include/sycl/ext/oneapi/bindless_images.hpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,89 @@ __SYCL_EXPORT unsigned int
593593
get_image_num_channels(const image_mem_handle memHandle,
594594
const sycl::queue &syclQueue);
595595

596+
/**
597+
* @brief Returns a vector of image-backing memory types supported by the
598+
* device for a given `image_descriptor`. If the returned vector is
599+
* empty, it indicates that the device does not support allocating or
600+
* creating images with the properties described in the
601+
* `image_descriptor`.
602+
*
603+
* @param imageDescriptor Properties of the image we want to query support
604+
* for.
605+
* @param syclDevice The device in which we created our image memory handle
606+
* @param syclContext The context in which we created our image memory handle
607+
* @return List of supported image-backing memory types
608+
*/
609+
__SYCL_EXPORT std::vector<image_memory_handle_type>
610+
get_image_memory_support(const image_descriptor &imageDescriptor,
611+
const sycl::device &syclDevice,
612+
const sycl::context &syclContext);
613+
614+
/**
615+
* @brief Returns a vector of image-backing memory types supported by the
616+
* device for a given `image_descriptor`. If the returned vector is
617+
* empty, it indicates that the device does not support allocating or
618+
* creating images with the properties described in the
619+
* `image_descriptor`.
620+
*
621+
* @param imageDescriptor Properties of the image we want to query support
622+
* for.
623+
* @param syclQueue The device/context association for which we want to query
624+
* image memory support.
625+
* @return List of supported image-backing memory types
626+
*/
627+
__SYCL_EXPORT std::vector<image_memory_handle_type>
628+
get_image_memory_support(const image_descriptor &imageDescriptor,
629+
const sycl::queue &syclQueue);
630+
631+
/**
632+
* @brief Returns `true` if the device supports creation of images of the
633+
* ImageHandleType, given the combination of `image_descriptor` and
634+
* `image_memory_handle_type`.
635+
*
636+
* @tparam ImageHandleType Either `sampled_image_handle` or
637+
* `unsampled_image_handle`.
638+
* @param imageDescriptor Properties of the image we want to query support
639+
* for.
640+
* @param imageMemoryHandleType Image memory handle type we want to query
641+
* support for.
642+
* @param syclDevice The device in which we want to query image handle
643+
* support
644+
* @param syclContext The context in which we want to query image handle
645+
* support
646+
* @return Boolean indicating support for image creation with the specified
647+
* parameter.
648+
*/
649+
650+
template <typename ImageHandleType>
651+
__SYCL_EXPORT bool
652+
is_image_handle_supported(const image_descriptor &imageDescriptor,
653+
image_memory_handle_type imageMemoryHandleType,
654+
const sycl::device &syclDevice,
655+
const sycl::context &syclContext);
656+
657+
/**
658+
* @brief Returns `true` if the device supports creation of images of the
659+
* ImageHandleType, given the combination of `image_descriptor` and
660+
* `image_memory_handle_type`.
661+
*
662+
* @tparam ImageHandleType Either `sampled_image_handle` or
663+
* `unsampled_image_handle`
664+
* @param imageDescriptor Properties of the image we want to query support
665+
* for.
666+
* @param imageMemoryHandleType Image memory handle type we want to query
667+
* support for.
668+
* @param syclQueue The device/context association for which we want to query
669+
* image handle support.
670+
* @return Boolean indicating support for image creation with the specified
671+
* parameter.
672+
*/
673+
template <typename ImageHandleType>
674+
__SYCL_EXPORT bool
675+
is_image_handle_supported(const image_descriptor &imageDescriptor,
676+
image_memory_handle_type imageMemoryHandleType,
677+
const sycl::queue &syclQueue);
678+
596679
namespace detail {
597680

598681
// is sycl::vec

sycl/include/sycl/ext/oneapi/bindless_images_memory.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ enum image_copy_flags : unsigned int {
107107
DtoD = 2,
108108
};
109109

110+
// The types of handles to image-backing memory
111+
enum class image_memory_handle_type : unsigned int {
112+
usm_pointer = 0,
113+
opaque_handle = 1,
114+
};
115+
110116
} // namespace ext::oneapi::experimental
111117
} // namespace _V1
112118
} // namespace sycl

sycl/source/detail/bindless_images.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,136 @@ get_image_num_channels(const image_mem_handle memHandle,
793793
syclQueue.get_context());
794794
}
795795

796+
__SYCL_EXPORT std::vector<image_memory_handle_type>
797+
get_image_memory_support(const image_descriptor &imageDescriptor,
798+
const sycl::device &syclDevice,
799+
const sycl::context &syclContext) {
800+
std::shared_ptr<sycl::detail::device_impl> DevImpl =
801+
sycl::detail::getSyclObjImpl(syclDevice);
802+
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
803+
sycl::detail::getSyclObjImpl(syclContext);
804+
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();
805+
806+
ur_image_desc_t urDesc;
807+
ur_image_format_t urFormat;
808+
populate_ur_structs(imageDescriptor, urDesc, urFormat);
809+
810+
ur_bool_t supportsPointerAllocation{0};
811+
Adapter->call<sycl::errc::runtime,
812+
sycl::detail::UrApiKind::
813+
urBindlessImagesGetImageMemoryHandleTypeSupportExp>(
814+
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
815+
ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER,
816+
&supportsPointerAllocation);
817+
818+
ur_bool_t supportsOpaqueAllocation{0};
819+
Adapter->call<sycl::errc::runtime,
820+
sycl::detail::UrApiKind::
821+
urBindlessImagesGetImageMemoryHandleTypeSupportExp>(
822+
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
823+
ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE,
824+
&supportsOpaqueAllocation);
825+
826+
std::vector<image_memory_handle_type> supportedMemHandleTypes;
827+
828+
if (supportsPointerAllocation) {
829+
supportedMemHandleTypes.push_back(image_memory_handle_type::usm_pointer);
830+
}
831+
832+
if (supportsOpaqueAllocation) {
833+
supportedMemHandleTypes.push_back(image_memory_handle_type::opaque_handle);
834+
}
835+
836+
return supportedMemHandleTypes;
837+
}
838+
839+
__SYCL_EXPORT std::vector<image_memory_handle_type>
840+
get_image_memory_support(const image_descriptor &imageDescriptor,
841+
const sycl::queue &syclQueue) {
842+
return get_image_memory_support(imageDescriptor, syclQueue.get_device(),
843+
syclQueue.get_context());
844+
}
845+
846+
template <>
847+
__SYCL_EXPORT bool is_image_handle_supported<unsampled_image_handle>(
848+
const image_descriptor &imageDescriptor,
849+
image_memory_handle_type imageMemoryHandleType,
850+
const sycl::device &syclDevice, const sycl::context &syclContext) {
851+
std::shared_ptr<sycl::detail::device_impl> DevImpl =
852+
sycl::detail::getSyclObjImpl(syclDevice);
853+
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
854+
sycl::detail::getSyclObjImpl(syclContext);
855+
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();
856+
857+
ur_image_desc_t urDesc;
858+
ur_image_format_t urFormat;
859+
populate_ur_structs(imageDescriptor, urDesc, urFormat);
860+
861+
const ur_exp_image_mem_type_t memHandleType =
862+
(imageMemoryHandleType == image_memory_handle_type::opaque_handle)
863+
? ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE
864+
: ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER;
865+
866+
ur_bool_t supportsUnsampledHandle{0};
867+
Adapter->call<sycl::errc::runtime,
868+
sycl::detail::UrApiKind::
869+
urBindlessImagesGetImageUnsampledHandleSupportExp>(
870+
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
871+
memHandleType, &supportsUnsampledHandle);
872+
873+
return supportsUnsampledHandle;
874+
}
875+
876+
template <>
877+
__SYCL_EXPORT bool is_image_handle_supported<unsampled_image_handle>(
878+
const image_descriptor &imageDescriptor,
879+
image_memory_handle_type imageMemoryHandleType,
880+
const sycl::queue &syclQueue) {
881+
return is_image_handle_supported<unsampled_image_handle>(
882+
imageDescriptor, imageMemoryHandleType, syclQueue.get_device(),
883+
syclQueue.get_context());
884+
}
885+
886+
template <>
887+
__SYCL_EXPORT bool is_image_handle_supported<sampled_image_handle>(
888+
const image_descriptor &imageDescriptor,
889+
image_memory_handle_type imageMemoryHandleType,
890+
const sycl::device &syclDevice, const sycl::context &syclContext) {
891+
std::shared_ptr<sycl::detail::device_impl> DevImpl =
892+
sycl::detail::getSyclObjImpl(syclDevice);
893+
std::shared_ptr<sycl::detail::context_impl> CtxImpl =
894+
sycl::detail::getSyclObjImpl(syclContext);
895+
const sycl::detail::AdapterPtr &Adapter = CtxImpl->getAdapter();
896+
897+
ur_image_desc_t urDesc;
898+
ur_image_format_t urFormat;
899+
populate_ur_structs(imageDescriptor, urDesc, urFormat);
900+
901+
const ur_exp_image_mem_type_t memHandleType =
902+
(imageMemoryHandleType == image_memory_handle_type::opaque_handle)
903+
? ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_OPAQUE_HANDLE
904+
: ur_exp_image_mem_type_t::UR_EXP_IMAGE_MEM_TYPE_USM_POINTER;
905+
906+
ur_bool_t supportsSampledHandle{0};
907+
Adapter->call<
908+
sycl::errc::runtime,
909+
sycl::detail::UrApiKind::urBindlessImagesGetImageSampledHandleSupportExp>(
910+
CtxImpl->getHandleRef(), DevImpl->getHandleRef(), &urDesc, &urFormat,
911+
memHandleType, &supportsSampledHandle);
912+
913+
return supportsSampledHandle;
914+
}
915+
916+
template <>
917+
__SYCL_EXPORT bool is_image_handle_supported<sampled_image_handle>(
918+
const image_descriptor &imageDescriptor,
919+
image_memory_handle_type imageMemoryHandleType,
920+
const sycl::queue &syclQueue) {
921+
return is_image_handle_supported<sampled_image_handle>(
922+
imageDescriptor, imageMemoryHandleType, syclQueue.get_device(),
923+
syclQueue.get_context());
924+
}
925+
796926
} // namespace ext::oneapi::experimental
797927
} // namespace _V1
798928
} // namespace sycl

sycl/test-e2e/bindless_images/3_channel_format.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <iostream>
1414
#include <sycl/detail/core.hpp>
1515

16+
#include "helpers/common.hpp"
1617
#include <sycl/ext/oneapi/bindless_images.hpp>
1718

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

49+
// Verify ability to allocate the above image descriptor
50+
if (!bindless_helpers::memoryAllocationSupported(
51+
desc, syclexp::image_memory_handle_type::opaque_handle, q)) {
52+
// We cannot allocate the opaque `image_mem` below
53+
// Skip the test
54+
if (ctxt.get_backend() == sycl::backend::ext_oneapi_cuda) {
55+
std::cout << "CUDA doesn't support 3-channel formats. Skipping test.\n";
56+
} else {
57+
std::cout << "Memory allocation unsupported. Skipping test.\n";
58+
}
59+
return 0;
60+
}
61+
4862
syclexp::image_mem imgMem(desc, dev, ctxt);
4963

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

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

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

7891
} catch (const sycl::exception &ex) {
7992
const std::string_view errMsg(ex.what());
80-
if (ctxt.get_backend() == sycl::backend::ext_oneapi_cuda) {
81-
if (errMsg.find("UR_RESULT_ERROR_UNSUPPORTED_IMAGE_FORMAT") !=
82-
std::string::npos) {
83-
std::cout << "CUDA doesn't support 3-channel formats, test passed."
84-
<< std::endl;
85-
return 0;
86-
}
87-
}
8893
std::cerr << "Unexpected SYCL exception: " << errMsg << "\n";
8994
return 1;
9095
} catch (...) {

0 commit comments

Comments
 (0)