Skip to content

Commit 5309b94

Browse files
committed
[SYCL][Bindless] Allow 3-channel image formats
This patch doesn't implement support for 3-channel formats, it just allows it in the spec and implementation so that a backend could implement it later (e.g. Level Zero). * Removed check from `image_descriptor::verify` * Updated spec to allow 3-channel formats * Very simple test, just verifies that CUDA backend still fails * Missing some PTX intrinsics, use `float4` instead of `float4` when compiling for CUDA. The more important part is checking that the CUDA backend throws an exception when trying to create image.
1 parent 283073a commit 5309b94

File tree

3 files changed

+118
-8
lines changed

3 files changed

+118
-8
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_bindless_images.asciidoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,9 @@ descriptor against the limitations outlined below. If the given descriptor is
239239
deemed invalid, then a `sycl::exception` will be thrown with error code
240240
`sycl::errc::invalid`.
241241

242-
For all image types, the value of `num_channels` must be `1`, `2`, or `4`.
242+
The value of `num_channels` supported by all image types and backends
243+
is `1`, `2`, or `4`.
244+
Some backends also support `num_channels` to be `3`.
243245

244246
For the `standard` image type, the value of `num_levels` and `array_size` must
245247
both be `1`.
@@ -2884,4 +2886,5 @@ These features still need to be handled:
28842886
handles and the imported `interop_xxx_handle`.
28852887
|5.17|2024-07-30| - Add support for mapping external memory to linear USM using
28862888
`map_external_linear_memory`.
2889+
|5.18|2024-08-27| - Allow 3-channel image formats on some backends.
28872890
|======================

sycl/include/sycl/ext/oneapi/bindless_images_descriptor.hpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ namespace ext::oneapi::experimental {
2323

2424
namespace detail {
2525

26-
inline image_channel_order
26+
constexpr image_channel_order
2727
get_image_default_channel_order(unsigned int num_channels) {
2828
switch (num_channels) {
2929
case 1:
3030
return image_channel_order::r;
3131
case 2:
3232
return image_channel_order::rg;
33+
case 3:
34+
return image_channel_order::rgb;
3335
case 4:
3436
return image_channel_order::rgba;
3537
default:
@@ -120,13 +122,9 @@ struct image_descriptor {
120122
}
121123

122124
void verify() const {
123-
124-
if (this->num_channels != 1 && this->num_channels != 2 &&
125-
this->num_channels != 4) {
126-
// Images can only have 1, 2, or 4 channels.
125+
if ((this->num_channels < 1) || (this->num_channels > 4)) {
127126
throw sycl::exception(sycl::errc::invalid,
128-
"Images must have only 1, 2, or 4 channels! Use a "
129-
"valid number of channels instead.");
127+
"Images must have 1, 2, 3, or 4 channels.");
130128
}
131129

132130
switch (this->type) {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// REQUIRES: cuda
2+
3+
// RUN: %{build} -o %t.out
4+
// RUN: %t.out
5+
6+
#include <iostream>
7+
#include <sycl/detail/core.hpp>
8+
9+
#include <sycl/ext/oneapi/bindless_images.hpp>
10+
11+
// Uncomment to print additional test information
12+
// #define VERBOSE_PRINT
13+
14+
class image_kernel;
15+
16+
namespace syclexp = sycl::ext::oneapi::experimental;
17+
18+
int main() {
19+
sycl::device dev;
20+
sycl::queue q(dev);
21+
auto ctxt = q.get_context();
22+
23+
constexpr size_t width = 512;
24+
std::vector<float> out(width);
25+
std::vector<float> expected(width);
26+
std::vector<sycl::float3> dataIn(width);
27+
float exp = 512;
28+
for (int i = 0; i < width; i++) {
29+
expected[i] = exp;
30+
dataIn[i] = sycl::float3(exp, width, i);
31+
}
32+
33+
try {
34+
// Main point of this test is to check creating an image
35+
// with a 3-channel format
36+
syclexp::image_descriptor desc({width}, 3, sycl::image_channel_type::fp32);
37+
38+
syclexp::image_mem imgMem(desc, dev, ctxt);
39+
40+
q.ext_oneapi_copy(dataIn.data(), imgMem.get_handle(), desc);
41+
q.wait_and_throw();
42+
43+
// Some backends don't support 3-channel formats
44+
// We still try to create the image,
45+
// but we expect it to fail with UR_RESULT_ERROR_UNSUPPORTED_IMAGE_FORMAT
46+
syclexp::unsampled_image_handle imgHandle =
47+
sycl::ext::oneapi::experimental::create_image(imgMem, desc, dev, ctxt);
48+
49+
sycl::buffer<float> buf(out.data(), width);
50+
51+
q.submit([&](sycl::handler &cgh) {
52+
sycl::accessor outAcc{buf};
53+
54+
cgh.parallel_for<image_kernel>(width, [=](sycl::id<1> id) {
55+
#if defined(__SYCL_DEVICE_ONLY__) && defined(__NVPTX__)
56+
// This shouldn't be hit anyway since CUDA doesn't support
57+
// 3-channel formats, but we need to ensure the kernel can compile
58+
using pixel_t = sycl::float4;
59+
#else
60+
using pixel_t = sycl::float3;
61+
#endif
62+
auto pixel = syclexp::fetch_image<pixel_t>(imgHandle, int(id[0]));
63+
outAcc[id] = pixel[0];
64+
});
65+
});
66+
q.wait_and_throw();
67+
68+
} catch (const sycl::exception &ex) {
69+
const std::string_view errMsg(ex.what());
70+
if (ctxt.get_backend() == sycl::backend::ext_oneapi_cuda) {
71+
if (errMsg.find("UR_RESULT_ERROR_UNSUPPORTED_IMAGE_FORMAT") !=
72+
std::string::npos) {
73+
std::cout << "CUDA doesn't support 3-channel formats, test passed."
74+
<< std::endl;
75+
return 0;
76+
}
77+
}
78+
std::cerr << "Unexpected SYCL exception: " << errMsg << "\n";
79+
return 1;
80+
} catch (...) {
81+
std::cerr << "Unknown exception caught!\n";
82+
return 2;
83+
}
84+
85+
bool validated = true;
86+
for (int i = 0; i < width; i++) {
87+
bool mismatch = false;
88+
if (out[i] != expected[i]) {
89+
mismatch = true;
90+
validated = false;
91+
}
92+
93+
if (mismatch) {
94+
#ifdef VERBOSE_PRINT
95+
std::cout << "Result mismatch! Expected: " << expected[i]
96+
<< ", Actual: " << out[i] << std::endl;
97+
#else
98+
break;
99+
#endif
100+
}
101+
}
102+
if (validated) {
103+
std::cout << "Test passed!" << std::endl;
104+
return 0;
105+
}
106+
107+
std::cout << "Test failed!" << std::endl;
108+
return 3;
109+
}

0 commit comments

Comments
 (0)