Skip to content

Commit 39dce88

Browse files
committed
MSL: Add support for sampler Y'CbCr conversion.
This change introduces functions and in one case, a class, to support the `VK_KHR_sampler_ycbcr_conversion` extension. Except in the case of GBGR8 and BGRG8 formats, for which Metal natively supports implicit chroma reconstruction, we're on our own here. We have to do everything ourselves. Much of the complexity comes from the need to support multiple planes, which must now be passed to functions that use the corresponding combined image-samplers. The rest is from the actual Y'CbCr conversion itself, which requires additional post-processing of the sample retrieved from the image. Passing sampled images to a function was a particular problem. To support this, I've added a new class which is emitted to MSL shaders that pass sampled images with Y'CbCr conversions attached around. It can handle sampled images with or without Y'CbCr conversion. This is an awful abomination that should not exist, but I'm worried that there's some shader out there which does this. This support requires Metal 2.0 to work properly, because it uses default-constructed texture objects, which were only added in MSL 2. I'm not even going to get into arrays of combined image-samplers--that's a whole other can of worms. They are deliberately unsupported in this change. I've taken the liberty of refactoring the support for texture swizzling while I'm at it. It's now treated as a post-processing step similar to Y'CbCr conversion. I'd like to think this is cleaner than having everything in `to_function_name()`/`to_function_args()`. It still looks really hairy, though. I did, however, get rid of the explicit type arguments to `spvGatherSwizzle()`/`spvGatherCompareSwizzle()`. Update the C API. In addition to supporting this new functionality, add some compiler options that I added in previous changes, but for which I neglected to update the C API.
1 parent 9b845a4 commit 39dce88

26 files changed

Lines changed: 2025 additions & 789 deletions

CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ if (SPIRV_CROSS_STATIC)
287287
endif()
288288

289289
set(spirv-cross-abi-major 0)
290-
set(spirv-cross-abi-minor 16)
290+
set(spirv-cross-abi-minor 17)
291291
set(spirv-cross-abi-patch 0)
292292

293293
if (SPIRV_CROSS_SHARED)
@@ -461,6 +461,10 @@ if (SPIRV_CROSS_CLI)
461461
target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c)
462462
set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
463463

464+
add_executable(spirv-cross-msl-ycbcr-conversion-test tests-other/msl_ycbcr_conversion_test.cpp)
465+
target_link_libraries(spirv-cross-msl-ycbcr-conversion-test spirv-cross-c)
466+
set_target_properties(spirv-cross-msl-ycbcr-conversion-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
467+
464468
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
465469
target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
466470
endif()
@@ -475,6 +479,10 @@ if (SPIRV_CROSS_CLI)
475479
COMMAND $<TARGET_FILE:spirv-cross-msl-constexpr-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv)
476480
add_test(NAME spirv-cross-msl-resource-binding-test
477481
COMMAND $<TARGET_FILE:spirv-cross-msl-resource-binding-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv)
482+
add_test(NAME spirv-cross-msl-ycbcr-conversion-test
483+
COMMAND $<TARGET_FILE:spirv-cross-msl-ycbcr-conversion-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test.spv)
484+
add_test(NAME spirv-cross-msl-ycbcr-conversion-test-2
485+
COMMAND $<TARGET_FILE:spirv-cross-msl-ycbcr-conversion-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test_2.spv)
478486
add_test(NAME spirv-cross-test
479487
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
480488
${spirv-cross-externals}

reference/opt/shaders-msl/comp/force-recompile-hooks.swizzle.comp

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,6 @@
55

66
using namespace metal;
77

8-
enum class spvSwizzle : uint
9-
{
10-
none = 0,
11-
zero,
12-
one,
13-
red,
14-
green,
15-
blue,
16-
alpha
17-
};
18-
198
template<typename T> struct spvRemoveReference { typedef T type; };
209
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
2110
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
@@ -28,6 +17,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
2817
return static_cast<thread T&&>(x);
2918
}
3019

20+
enum class spvSwizzle : uint
21+
{
22+
none = 0,
23+
zero,
24+
one,
25+
red,
26+
green,
27+
blue,
28+
alpha
29+
};
30+
3131
template<typename T>
3232
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
3333
{
@@ -65,66 +65,6 @@ inline T spvTextureSwizzle(T x, uint s)
6565
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
6666
}
6767

68-
// Wrapper function that swizzles texture gathers.
69-
template<typename T, typename Tex, typename... Ts>
70-
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
71-
{
72-
if (sw)
73-
{
74-
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
75-
{
76-
case spvSwizzle::none:
77-
break;
78-
case spvSwizzle::zero:
79-
return vec<T, 4>(0, 0, 0, 0);
80-
case spvSwizzle::one:
81-
return vec<T, 4>(1, 1, 1, 1);
82-
case spvSwizzle::red:
83-
return t.gather(s, spvForward<Ts>(params)..., component::x);
84-
case spvSwizzle::green:
85-
return t.gather(s, spvForward<Ts>(params)..., component::y);
86-
case spvSwizzle::blue:
87-
return t.gather(s, spvForward<Ts>(params)..., component::z);
88-
case spvSwizzle::alpha:
89-
return t.gather(s, spvForward<Ts>(params)..., component::w);
90-
}
91-
}
92-
switch (c)
93-
{
94-
case component::x:
95-
return t.gather(s, spvForward<Ts>(params)..., component::x);
96-
case component::y:
97-
return t.gather(s, spvForward<Ts>(params)..., component::y);
98-
case component::z:
99-
return t.gather(s, spvForward<Ts>(params)..., component::z);
100-
case component::w:
101-
return t.gather(s, spvForward<Ts>(params)..., component::w);
102-
}
103-
}
104-
105-
// Wrapper function that swizzles depth texture gathers.
106-
template<typename T, typename Tex, typename... Ts>
107-
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
108-
{
109-
if (sw)
110-
{
111-
switch (spvSwizzle(sw & 0xFF))
112-
{
113-
case spvSwizzle::none:
114-
case spvSwizzle::red:
115-
break;
116-
case spvSwizzle::zero:
117-
case spvSwizzle::green:
118-
case spvSwizzle::blue:
119-
case spvSwizzle::alpha:
120-
return vec<T, 4>(0, 0, 0, 0);
121-
case spvSwizzle::one:
122-
return vec<T, 4>(1, 1, 1, 1);
123-
}
124-
}
125-
return t.gather_compare(s, spvForward<Ts>(params)...);
126-
}
127-
12868
kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> foo [[texture(0)]], texture2d<float, access::write> bar [[texture(1)]], sampler fooSmplr [[sampler(0)]])
12969
{
13070
constant uint& fooSwzl = spvSwizzleConstants[0];

reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,6 @@ struct main0_in
2222
float2 vUV [[user(locn0)]];
2323
};
2424

25-
enum class spvSwizzle : uint
26-
{
27-
none = 0,
28-
zero,
29-
one,
30-
red,
31-
green,
32-
blue,
33-
alpha
34-
};
35-
3625
template<typename T> struct spvRemoveReference { typedef T type; };
3726
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
3827
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
@@ -45,6 +34,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
4534
return static_cast<thread T&&>(x);
4635
}
4736

37+
enum class spvSwizzle : uint
38+
{
39+
none = 0,
40+
zero,
41+
one,
42+
red,
43+
green,
44+
blue,
45+
alpha
46+
};
47+
4848
template<typename T>
4949
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
5050
{
@@ -82,66 +82,6 @@ inline T spvTextureSwizzle(T x, uint s)
8282
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
8383
}
8484

85-
// Wrapper function that swizzles texture gathers.
86-
template<typename T, typename Tex, typename... Ts>
87-
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
88-
{
89-
if (sw)
90-
{
91-
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
92-
{
93-
case spvSwizzle::none:
94-
break;
95-
case spvSwizzle::zero:
96-
return vec<T, 4>(0, 0, 0, 0);
97-
case spvSwizzle::one:
98-
return vec<T, 4>(1, 1, 1, 1);
99-
case spvSwizzle::red:
100-
return t.gather(s, spvForward<Ts>(params)..., component::x);
101-
case spvSwizzle::green:
102-
return t.gather(s, spvForward<Ts>(params)..., component::y);
103-
case spvSwizzle::blue:
104-
return t.gather(s, spvForward<Ts>(params)..., component::z);
105-
case spvSwizzle::alpha:
106-
return t.gather(s, spvForward<Ts>(params)..., component::w);
107-
}
108-
}
109-
switch (c)
110-
{
111-
case component::x:
112-
return t.gather(s, spvForward<Ts>(params)..., component::x);
113-
case component::y:
114-
return t.gather(s, spvForward<Ts>(params)..., component::y);
115-
case component::z:
116-
return t.gather(s, spvForward<Ts>(params)..., component::z);
117-
case component::w:
118-
return t.gather(s, spvForward<Ts>(params)..., component::w);
119-
}
120-
}
121-
122-
// Wrapper function that swizzles depth texture gathers.
123-
template<typename T, typename Tex, typename... Ts>
124-
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
125-
{
126-
if (sw)
127-
{
128-
switch (spvSwizzle(sw & 0xFF))
129-
{
130-
case spvSwizzle::none:
131-
case spvSwizzle::red:
132-
break;
133-
case spvSwizzle::zero:
134-
case spvSwizzle::green:
135-
case spvSwizzle::blue:
136-
case spvSwizzle::alpha:
137-
return vec<T, 4>(0, 0, 0, 0);
138-
case spvSwizzle::one:
139-
return vec<T, 4>(1, 1, 1, 1);
140-
}
141-
}
142-
return t.gather_compare(s, spvForward<Ts>(params)...);
143-
}
144-
14585
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> uSampler1 [[texture(0)]], sampler uSampler1Smplr [[sampler(0)]])
14686
{
14787
main0_out out = {};

reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,6 @@ struct main0_in
1515
float2 vUV [[user(locn0)]];
1616
};
1717

18-
enum class spvSwizzle : uint
19-
{
20-
none = 0,
21-
zero,
22-
one,
23-
red,
24-
green,
25-
blue,
26-
alpha
27-
};
28-
2918
template<typename T> struct spvRemoveReference { typedef T type; };
3019
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
3120
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
@@ -38,6 +27,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
3827
return static_cast<thread T&&>(x);
3928
}
4029

30+
enum class spvSwizzle : uint
31+
{
32+
none = 0,
33+
zero,
34+
one,
35+
red,
36+
green,
37+
blue,
38+
alpha
39+
};
40+
4141
template<typename T>
4242
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
4343
{
@@ -75,66 +75,6 @@ inline T spvTextureSwizzle(T x, uint s)
7575
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
7676
}
7777

78-
// Wrapper function that swizzles texture gathers.
79-
template<typename T, typename Tex, typename... Ts>
80-
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
81-
{
82-
if (sw)
83-
{
84-
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
85-
{
86-
case spvSwizzle::none:
87-
break;
88-
case spvSwizzle::zero:
89-
return vec<T, 4>(0, 0, 0, 0);
90-
case spvSwizzle::one:
91-
return vec<T, 4>(1, 1, 1, 1);
92-
case spvSwizzle::red:
93-
return t.gather(s, spvForward<Ts>(params)..., component::x);
94-
case spvSwizzle::green:
95-
return t.gather(s, spvForward<Ts>(params)..., component::y);
96-
case spvSwizzle::blue:
97-
return t.gather(s, spvForward<Ts>(params)..., component::z);
98-
case spvSwizzle::alpha:
99-
return t.gather(s, spvForward<Ts>(params)..., component::w);
100-
}
101-
}
102-
switch (c)
103-
{
104-
case component::x:
105-
return t.gather(s, spvForward<Ts>(params)..., component::x);
106-
case component::y:
107-
return t.gather(s, spvForward<Ts>(params)..., component::y);
108-
case component::z:
109-
return t.gather(s, spvForward<Ts>(params)..., component::z);
110-
case component::w:
111-
return t.gather(s, spvForward<Ts>(params)..., component::w);
112-
}
113-
}
114-
115-
// Wrapper function that swizzles depth texture gathers.
116-
template<typename T, typename Tex, typename... Ts>
117-
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
118-
{
119-
if (sw)
120-
{
121-
switch (spvSwizzle(sw & 0xFF))
122-
{
123-
case spvSwizzle::none:
124-
case spvSwizzle::red:
125-
break;
126-
case spvSwizzle::zero:
127-
case spvSwizzle::green:
128-
case spvSwizzle::blue:
129-
case spvSwizzle::alpha:
130-
return vec<T, 4>(0, 0, 0, 0);
131-
case spvSwizzle::one:
132-
return vec<T, 4>(1, 1, 1, 1);
133-
}
134-
}
135-
return t.gather_compare(s, spvForward<Ts>(params)...);
136-
}
137-
13878
fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvSwizzleConstants [[buffer(30)]], array<texture2d<float>, 4> uSampler [[texture(0)]], array<sampler, 4> uSamplerSmplr [[sampler(0)]])
13979
{
14080
main0_out out = {};

0 commit comments

Comments
 (0)