Skip to content

Commit ca7b9bb

Browse files
authored
[Bindless][SYCL] read/write images with only acceptable coordinate types (#11341)
Assert the restrictions on reading/writing coordinate types and mipmap anisotropic viewing gradient types
1 parent 3d86438 commit ca7b9bb

File tree

2 files changed

+69
-25
lines changed

2 files changed

+69
-25
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_bindless_images.asciidoc

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ with error code `sycl::errc::invalid`, and relay an error message back to the
945945
user through `sycl::exception::what()`, describing which of the scenarios
946946
listed above caused the failure.
947947

948-
=== Reading and writing inside the kernel
948+
=== Reading and writing inside the kernel [[reading_writing_inside_kernel]]
949949

950950
```cpp
951951
namespace sycl::ext::oneapi::experimental {
@@ -994,16 +994,16 @@ trivially copyable.
994994

995995
Sampled images cannot be written to using `write_image`.
996996

997-
For unsampled images, coordinates are specified by `int`, `sycl::vec<int, 2>`,
998-
and `sycl::vec<int, 4>` for 1D, 2D, and 3D images respectively.
997+
For reading and writing of unsampled images, coordinates are specified by `int`,
998+
`sycl::vec<int, 2>`, and `sycl::vec<int, 4>` for 1D, 2D, and 3D images,
999+
respectively.
9991000

10001001
Sampled image reads take `float`, `sycl::vec<float, 2>`, and
1001-
`sycl::vec<float, 4>` coordinate types for 1D, 2D, and 3D images respectively.
1002+
`sycl::vec<float, 4>` coordinate types for 1D, 2D, and 3D images, respectively.
10021003

1003-
In the case of 3D reads or writes, the fourth element in the coordinate vector
1004-
is ignored.
1005-
1006-
Note that coordinates for 3D images take a vector of size 4, not 3.
1004+
Note that in the case of 3D reads or writes, coordinates for 3D images take a
1005+
vector of size 4, not 3, as the fourth element in the coordinate vector is
1006+
ignored.
10071007

10081008
Note also that all images must be used in either read-only or write-only fashion
10091009
within a single kernel invocation; read/write images are not supported.
@@ -1124,6 +1124,10 @@ DataT read_image(const sampled_image_handle &ImageHandle,
11241124
const CoordT &Dx, const CoordT &Dy);
11251125
```
11261126

1127+
Reading a mipmap follows the same restrictions on what coordinate types may be
1128+
used as laid out in <<reading_writing_inside_kernel>>, and the viewing gradients
1129+
are bound to the same type as used for the coordinates.
1130+
11271131
== Interoperability
11281132

11291133
=== Querying interoperability support
@@ -1976,4 +1980,5 @@ These features still need to be handled:
19761980
(`unorm_short_555`, `unorm_short_565`, `unorm_int_101010`)
19771981
|4.4|2023-09-12| - Added overload with `sycl::queue` to standalone functions
19781982
|4.5|2023-09-14| - Update wording for allocating images + fix typo
1983+
|4.6|2023-09-19| - Clarify restrictions on reading/writing coordinate types
19791984
|======================

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

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -633,15 +633,26 @@ get_image_num_channels(const image_mem_handle memHandle,
633633
const sycl::queue &syclQueue);
634634

635635
namespace detail {
636+
637+
// is sycl::vec
638+
template <typename T> struct is_vec {
639+
static constexpr bool value = false;
640+
};
641+
template <typename T, int N> struct is_vec<sycl::vec<T, N>> {
642+
static constexpr bool value = true;
643+
};
644+
template <typename T> inline constexpr bool is_vec_v = is_vec<T>::value;
645+
636646
// Get the number of coordinates
637647
template <typename CoordT> constexpr size_t coord_size() {
638-
if constexpr (std::is_scalar<CoordT>::value) {
648+
if constexpr (std::is_scalar_v<CoordT>) {
639649
return 1;
640650
} else {
641651
return CoordT::size();
642652
}
643653
}
644654

655+
#if defined(__NVPTX__)
645656
// bit_cast Color to a type the NVPTX backend is known to accept
646657
template <typename DataT> constexpr auto convert_color_nvptx(DataT Color) {
647658
constexpr size_t dataSize = sizeof(DataT);
@@ -662,15 +673,39 @@ template <typename DataT> constexpr auto convert_color_nvptx(DataT Color) {
662673
return sycl::bit_cast<sycl::vec<uint32_t, 4>>(Color);
663674
}
664675
}
676+
#endif
677+
678+
// assert coords or elements of coords is of an integer type
679+
template <typename CoordT> constexpr void assert_unsampled_coords() {
680+
if constexpr (std::is_scalar_v<CoordT>) {
681+
static_assert(std::is_same_v<CoordT, int>,
682+
"Expected integer coordinate data type");
683+
} else {
684+
static_assert(is_vec_v<CoordT>, "Expected sycl::vec coordinates");
685+
static_assert(std::is_same_v<typename CoordT::element_type, int>,
686+
"Expected integer coordinates data type");
687+
}
688+
}
665689

690+
// assert coords or elements of coords is of a float type
691+
template <typename CoordT> constexpr void assert_sampled_coords() {
692+
if constexpr (std::is_scalar_v<CoordT>) {
693+
static_assert(std::is_same_v<CoordT, float>,
694+
"Expected float coordinate data type");
695+
} else {
696+
static_assert(is_vec_v<CoordT>, "Expected sycl::vec coordinates");
697+
static_assert(std::is_same_v<typename CoordT::element_type, float>,
698+
"Expected float coordinates data type");
699+
}
700+
}
666701
} // namespace detail
667702

668703
/**
669704
* @brief Read an unsampled image using its handle
670705
*
671706
* @tparam DataT The return type
672707
* @tparam CoordT The input coordinate type. e.g. int, int2, or int4 for
673-
* 1D, 2D, and 3D respectively
708+
* 1D, 2D, and 3D, respectively
674709
* @param imageHandle The image handle
675710
* @param coords The coordinates at which to fetch image data
676711
* @return Image data
@@ -684,10 +719,11 @@ template <typename DataT> constexpr auto convert_color_nvptx(DataT Color) {
684719
template <typename DataT, typename CoordT>
685720
DataT read_image(const unsampled_image_handle &imageHandle [[maybe_unused]],
686721
const CoordT &coords [[maybe_unused]]) {
722+
detail::assert_unsampled_coords<CoordT>();
687723
constexpr size_t coordSize = detail::coord_size<CoordT>();
688724
static_assert(coordSize == 1 || coordSize == 2 || coordSize == 4,
689725
"Expected input coordinate to be have 1, 2, or 4 components "
690-
"for 1D, 2D and 3D images respectively.");
726+
"for 1D, 2D and 3D images, respectively.");
691727

692728
#ifdef __SYCL_DEVICE_ONLY__
693729
#if defined(__NVPTX__)
@@ -705,7 +741,7 @@ DataT read_image(const unsampled_image_handle &imageHandle [[maybe_unused]],
705741
*
706742
* @tparam DataT The return type
707743
* @tparam CoordT The input coordinate type. e.g. float, float2, or float4 for
708-
* 1D, 2D, and 3D respectively
744+
* 1D, 2D, and 3D, respectively
709745
* @param imageHandle The image handle
710746
* @param coords The coordinates at which to fetch image data
711747
* @return Sampled image data
@@ -719,10 +755,11 @@ DataT read_image(const unsampled_image_handle &imageHandle [[maybe_unused]],
719755
template <typename DataT, typename CoordT>
720756
DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
721757
const CoordT &coords [[maybe_unused]]) {
758+
detail::assert_sampled_coords<CoordT>();
722759
constexpr size_t coordSize = detail::coord_size<CoordT>();
723760
static_assert(coordSize == 1 || coordSize == 2 || coordSize == 4,
724761
"Expected input coordinate to be have 1, 2, or 4 components "
725-
"for 1D, 2D and 3D images respectively.");
762+
"for 1D, 2D and 3D images, respectively.");
726763

727764
#ifdef __SYCL_DEVICE_ONLY__
728765
#if defined(__NVPTX__)
@@ -740,7 +777,7 @@ DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
740777
*
741778
* @tparam DataT The return type
742779
* @tparam CoordT The input coordinate type. e.g. float, float2, or float4 for
743-
* 1D, 2D, and 3D respectively
780+
* 1D, 2D, and 3D, respectively
744781
* @param imageHandle The mipmap image handle
745782
* @param coords The coordinates at which to fetch mipmap image data
746783
* @param level The mipmap level at which to sample
@@ -750,10 +787,11 @@ template <typename DataT, typename CoordT>
750787
DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
751788
const CoordT &coords [[maybe_unused]],
752789
const float level [[maybe_unused]]) {
790+
detail::assert_sampled_coords<CoordT>();
753791
constexpr size_t coordSize = detail::coord_size<CoordT>();
754792
static_assert(coordSize == 1 || coordSize == 2 || coordSize == 4,
755793
"Expected input coordinate to be have 1, 2, or 4 components "
756-
"for 1D, 2D and 3D images respectively.");
794+
"for 1D, 2D and 3D images, respectively.");
757795

758796
#ifdef __SYCL_DEVICE_ONLY__
759797
#if defined(__NVPTX__)
@@ -771,7 +809,7 @@ DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
771809
*
772810
* @tparam DataT The return type
773811
* @tparam CoordT The input coordinate type. e.g. float, float2, or float4 for
774-
* 1D, 2D, and 3D respectively
812+
* 1D, 2D, and 3D, respectively
775813
* @param imageHandle The mipmap image handle
776814
* @param coords The coordinates at which to fetch mipmap image data
777815
* @param dX Screen space gradient in the x dimension
@@ -783,11 +821,11 @@ DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
783821
const CoordT &coords [[maybe_unused]],
784822
const CoordT &dX [[maybe_unused]],
785823
const CoordT &dY [[maybe_unused]]) {
824+
detail::assert_sampled_coords<CoordT>();
786825
constexpr size_t coordSize = detail::coord_size<CoordT>();
787826
static_assert(coordSize == 1 || coordSize == 2 || coordSize == 4,
788-
"Expected input coordinate and gradient to have 1, 2, or 4 "
789-
"components "
790-
"for 1D, 2D and 3D images respectively.");
827+
"Expected input coordinates and gradients to have 1, 2, or 4 "
828+
"components for 1D, 2D, and 3D images, respectively.");
791829

792830
#ifdef __SYCL_DEVICE_ONLY__
793831
#if defined(__NVPTX__)
@@ -805,23 +843,24 @@ DataT read_image(const sampled_image_handle &imageHandle [[maybe_unused]],
805843
*
806844
* @tparam DataT The data type to write
807845
* @tparam CoordT The input coordinate type. e.g. int, int2, or int4 for
808-
* 1D, 2D, and 3D respectively
846+
* 1D, 2D, and 3D, respectively
809847
* @param imageHandle The image handle
810848
* @param coords The coordinates at which to write image data
811849
*/
812850
template <typename DataT, typename CoordT>
813851
void write_image(const unsampled_image_handle &imageHandle [[maybe_unused]],
814-
const CoordT &Coords [[maybe_unused]],
815-
const DataT &Color [[maybe_unused]]) {
852+
const CoordT &coords [[maybe_unused]],
853+
const DataT &color [[maybe_unused]]) {
854+
detail::assert_unsampled_coords<CoordT>();
816855
constexpr size_t coordSize = detail::coord_size<CoordT>();
817856
static_assert(coordSize == 1 || coordSize == 2 || coordSize == 4,
818857
"Expected input coordinate to be have 1, 2, or 4 components "
819-
"for 1D, 2D and 3D images respectively.");
858+
"for 1D, 2D and 3D images, respectively.");
820859

821860
#ifdef __SYCL_DEVICE_ONLY__
822861
#if defined(__NVPTX__)
823-
__invoke__ImageWrite((uint64_t)imageHandle.raw_handle, Coords,
824-
detail::convert_color_nvptx(Color));
862+
__invoke__ImageWrite((uint64_t)imageHandle.raw_handle, coords,
863+
detail::convert_color_nvptx(color));
825864
#else
826865
// TODO: add SPIRV part for unsampled image write
827866
#endif

0 commit comments

Comments
 (0)