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.
This commit is contained in:
parent
9b845a4788
commit
39dce88d3b
@ -287,7 +287,7 @@ if (SPIRV_CROSS_STATIC)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(spirv-cross-abi-major 0)
|
set(spirv-cross-abi-major 0)
|
||||||
set(spirv-cross-abi-minor 16)
|
set(spirv-cross-abi-minor 17)
|
||||||
set(spirv-cross-abi-patch 0)
|
set(spirv-cross-abi-patch 0)
|
||||||
|
|
||||||
if (SPIRV_CROSS_SHARED)
|
if (SPIRV_CROSS_SHARED)
|
||||||
@ -461,6 +461,10 @@ if (SPIRV_CROSS_CLI)
|
|||||||
target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c)
|
target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c)
|
||||||
set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
|
set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
|
||||||
|
|
||||||
|
add_executable(spirv-cross-msl-ycbcr-conversion-test tests-other/msl_ycbcr_conversion_test.cpp)
|
||||||
|
target_link_libraries(spirv-cross-msl-ycbcr-conversion-test spirv-cross-c)
|
||||||
|
set_target_properties(spirv-cross-msl-ycbcr-conversion-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
|
||||||
|
|
||||||
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
|
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
|
||||||
target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
|
target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
|
||||||
endif()
|
endif()
|
||||||
@ -475,6 +479,10 @@ if (SPIRV_CROSS_CLI)
|
|||||||
COMMAND $<TARGET_FILE:spirv-cross-msl-constexpr-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv)
|
COMMAND $<TARGET_FILE:spirv-cross-msl-constexpr-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv)
|
||||||
add_test(NAME spirv-cross-msl-resource-binding-test
|
add_test(NAME spirv-cross-msl-resource-binding-test
|
||||||
COMMAND $<TARGET_FILE:spirv-cross-msl-resource-binding-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv)
|
COMMAND $<TARGET_FILE:spirv-cross-msl-resource-binding-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv)
|
||||||
|
add_test(NAME spirv-cross-msl-ycbcr-conversion-test
|
||||||
|
COMMAND $<TARGET_FILE:spirv-cross-msl-ycbcr-conversion-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test.spv)
|
||||||
|
add_test(NAME spirv-cross-msl-ycbcr-conversion-test-2
|
||||||
|
COMMAND $<TARGET_FILE:spirv-cross-msl-ycbcr-conversion-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test_2.spv)
|
||||||
add_test(NAME spirv-cross-test
|
add_test(NAME spirv-cross-test
|
||||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
|
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
|
||||||
${spirv-cross-externals}
|
${spirv-cross-externals}
|
||||||
|
@ -5,17 +5,6 @@
|
|||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -65,66 +65,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> foo [[texture(0)]], texture2d<float, access::write> bar [[texture(1)]], sampler fooSmplr [[sampler(0)]])
|
kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> foo [[texture(0)]], texture2d<float, access::write> bar [[texture(1)]], sampler fooSmplr [[sampler(0)]])
|
||||||
{
|
{
|
||||||
constant uint& fooSwzl = spvSwizzleConstants[0];
|
constant uint& fooSwzl = spvSwizzleConstants[0];
|
||||||
|
@ -22,17 +22,6 @@ struct main0_in
|
|||||||
float2 vUV [[user(locn0)]];
|
float2 vUV [[user(locn0)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -82,66 +82,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]])
|
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)]])
|
||||||
{
|
{
|
||||||
main0_out out = {};
|
main0_out out = {};
|
||||||
|
@ -15,17 +15,6 @@ struct main0_in
|
|||||||
float2 vUV [[user(locn0)]];
|
float2 vUV [[user(locn0)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -75,66 +75,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]])
|
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)]])
|
||||||
{
|
{
|
||||||
main0_out out = {};
|
main0_out out = {};
|
||||||
|
@ -11,17 +11,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -34,6 +23,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -72,8 +72,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -109,8 +109,8 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
// Wrapper function that swizzles depth texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
inline vec<T, 4> spvGatherCompareSwizzle(const thread Tex<T>& t, sampler s, uint sw, Ts... params)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -177,13 +177,13 @@ fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d
|
|||||||
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
||||||
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
||||||
c = texBuffer.read(spvTexelBufferCoord(0));
|
c = texBuffer.read(spvTexelBufferCoord(0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d<float>, float2, int2>(tex2dSamp, tex2d, float2(0.0), int2(0), component::x, tex2dSwzl);
|
c = spvGatherSwizzle(tex2d, tex2dSamp, tex2dSwzl, component::x, float2(0.0), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube<float>, float3>(texCubeSamp, texCube, float3(0.0), component::y, texCubeSwzl);
|
c = spvGatherSwizzle(texCube, texCubeSamp, texCubeSwzl, component::y, float3(0.0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d_array<float>, float2, uint, int2>(tex2dArraySamp, tex2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, tex2dArraySwzl);
|
c = spvGatherSwizzle(tex2dArray, tex2dArraySamp, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube_array<float>, float3, uint>(texCubeArraySamp, texCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, texCubeArraySwzl);
|
c = spvGatherSwizzle(texCubeArray, texCubeArraySamp, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)));
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d<float>, float2, float>(depth2dSamp, depth2d, float2(0.0), 1.0, depth2dSwzl);
|
c = spvGatherCompareSwizzle(depth2d, depth2dSamp, depth2dSwzl, float2(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube<float>, float3, float>(depthCubeSamp, depthCube, float3(0.0), 1.0, depthCubeSwzl);
|
c = spvGatherCompareSwizzle(depthCube, depthCubeSamp, depthCubeSwzl, float3(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d_array<float>, float2, uint, float>(depth2dArraySamp, depth2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0, depth2dArraySwzl);
|
c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySamp, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube_array<float>, float3, uint, float>(depthCubeArraySamp, depthCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0, depthCubeArraySwzl);
|
c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySamp, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -34,6 +23,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -72,8 +72,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -108,29 +108,6 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d<int> tex1d [[texture(0)]], texture2d<int> tex2d [[texture(1)]], texture3d<int> tex3d [[texture(2)]], texturecube<int> texCube [[texture(3)]], texture2d_array<int> tex2dArray [[texture(4)]], texturecube_array<int> texCubeArray [[texture(5)]], texture2d<int> texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]])
|
fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d<int> tex1d [[texture(0)]], texture2d<int> tex2d [[texture(1)]], texture3d<int> tex3d [[texture(2)]], texturecube<int> texCube [[texture(3)]], texture2d_array<int> tex2dArray [[texture(4)]], texturecube_array<int> texCubeArray [[texture(5)]], texture2d<int> texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]])
|
||||||
{
|
{
|
||||||
constant uint& tex1dSwzl = spvSwizzleConstants[0];
|
constant uint& tex1dSwzl = spvSwizzleConstants[0];
|
||||||
@ -162,9 +139,9 @@ fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d
|
|||||||
c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl));
|
c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl));
|
||||||
c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl));
|
c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl));
|
||||||
c = float4(texBuffer.read(spvTexelBufferCoord(0)));
|
c = float4(texBuffer.read(spvTexelBufferCoord(0)));
|
||||||
c = float4(spvGatherSwizzle<int, metal::texture2d<int>, float2, int2>(tex2dSmplr, tex2d, float2(0.0), int2(0), component::x, tex2dSwzl));
|
c = float4(spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0)));
|
||||||
c = float4(spvGatherSwizzle<int, metal::texturecube<int>, float3>(texCubeSmplr, texCube, float3(0.0), component::y, texCubeSwzl));
|
c = float4(spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0)));
|
||||||
c = float4(spvGatherSwizzle<int, metal::texture2d_array<int>, float2, uint, int2>(tex2dArraySmplr, tex2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, tex2dArraySwzl));
|
c = float4(spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)));
|
||||||
c = float4(spvGatherSwizzle<int, metal::texturecube_array<int>, float3, uint>(texCubeArraySmplr, texCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, texCubeArraySwzl));
|
c = float4(spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -34,6 +23,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -72,8 +72,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -109,8 +109,8 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
// Wrapper function that swizzles depth texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
inline vec<T, 4> spvGatherCompareSwizzle(const thread Tex<T>& t, sampler s, uint sw, Ts... params)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -167,14 +167,14 @@ inline float4 doSwizzle(thread texture1d<float> tex1d, thread const sampler tex1
|
|||||||
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
||||||
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
||||||
c = texBuffer.read(spvTexelBufferCoord(0));
|
c = texBuffer.read(spvTexelBufferCoord(0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d<float>, float2, int2>(tex2dSmplr, tex2d, float2(0.0), int2(0), component::x, tex2dSwzl);
|
c = spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube<float>, float3>(texCubeSmplr, texCube, float3(0.0), component::y, texCubeSwzl);
|
c = spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d_array<float>, float2, uint, int2>(tex2dArraySmplr, tex2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, tex2dArraySwzl);
|
c = spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube_array<float>, float3, uint>(texCubeArraySmplr, texCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, texCubeArraySwzl);
|
c = spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)));
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d<float>, float2, float>(depth2dSmplr, depth2d, float2(0.0), 1.0, depth2dSwzl);
|
c = spvGatherCompareSwizzle(depth2d, depth2dSmplr, depth2dSwzl, float2(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube<float>, float3, float>(depthCubeSmplr, depthCube, float3(0.0), 1.0, depthCubeSwzl);
|
c = spvGatherCompareSwizzle(depthCube, depthCubeSmplr, depthCubeSwzl, float3(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d_array<float>, float2, uint, float>(depth2dArraySmplr, depth2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0, depth2dArraySwzl);
|
c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySmplr, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube_array<float>, float3, uint, float>(depthCubeArraySmplr, depthCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0, depthCubeArraySwzl);
|
c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -34,6 +23,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -72,8 +72,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -108,29 +108,6 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d<uint> tex1d [[texture(0)]], texture2d<uint> tex2d [[texture(1)]], texture3d<uint> tex3d [[texture(2)]], texturecube<uint> texCube [[texture(3)]], texture2d_array<uint> tex2dArray [[texture(4)]], texturecube_array<uint> texCubeArray [[texture(5)]], texture2d<uint> texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]])
|
fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d<uint> tex1d [[texture(0)]], texture2d<uint> tex2d [[texture(1)]], texture3d<uint> tex3d [[texture(2)]], texturecube<uint> texCube [[texture(3)]], texture2d_array<uint> tex2dArray [[texture(4)]], texturecube_array<uint> texCubeArray [[texture(5)]], texture2d<uint> texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]])
|
||||||
{
|
{
|
||||||
constant uint& tex1dSwzl = spvSwizzleConstants[0];
|
constant uint& tex1dSwzl = spvSwizzleConstants[0];
|
||||||
@ -162,9 +139,9 @@ fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d
|
|||||||
c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl));
|
c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl));
|
||||||
c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl));
|
c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl));
|
||||||
c = float4(texBuffer.read(spvTexelBufferCoord(0)));
|
c = float4(texBuffer.read(spvTexelBufferCoord(0)));
|
||||||
c = float4(spvGatherSwizzle<uint, metal::texture2d<uint>, float2, int2>(tex2dSmplr, tex2d, float2(0.0), int2(0), component::x, tex2dSwzl));
|
c = float4(spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0)));
|
||||||
c = float4(spvGatherSwizzle<uint, metal::texturecube<uint>, float3>(texCubeSmplr, texCube, float3(0.0), component::y, texCubeSwzl));
|
c = float4(spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0)));
|
||||||
c = float4(spvGatherSwizzle<uint, metal::texture2d_array<uint>, float2, uint, int2>(tex2dArraySmplr, tex2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, tex2dArraySwzl));
|
c = float4(spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)));
|
||||||
c = float4(spvGatherSwizzle<uint, metal::texturecube_array<uint>, float3, uint>(texCubeArraySmplr, texCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, texCubeArraySwzl));
|
c = float4(spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -34,6 +23,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -72,8 +72,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -109,8 +109,8 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
// Wrapper function that swizzles depth texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
inline vec<T, 4> spvGatherCompareSwizzle(const thread Tex<T>& t, sampler s, uint sw, Ts... params)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -177,13 +177,13 @@ fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d
|
|||||||
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl);
|
||||||
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl);
|
||||||
c = texBuffer.read(spvTexelBufferCoord(0));
|
c = texBuffer.read(spvTexelBufferCoord(0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d<float>, float2, int2>(tex2dSmplr, tex2d, float2(0.0), int2(0), component::x, tex2dSwzl);
|
c = spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube<float>, float3>(texCubeSmplr, texCube, float3(0.0), component::y, texCubeSwzl);
|
c = spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d_array<float>, float2, uint, int2>(tex2dArraySmplr, tex2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, tex2dArraySwzl);
|
c = spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube_array<float>, float3, uint>(texCubeArraySmplr, texCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, texCubeArraySwzl);
|
c = spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)));
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d<float>, float2, float>(depth2dSmplr, depth2d, float2(0.0), 1.0, depth2dSwzl);
|
c = spvGatherCompareSwizzle(depth2d, depth2dSmplr, depth2dSwzl, float2(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube<float>, float3, float>(depthCubeSmplr, depthCube, float3(0.0), 1.0, depthCubeSwzl);
|
c = spvGatherCompareSwizzle(depthCube, depthCubeSmplr, depthCubeSwzl, float3(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d_array<float>, float2, uint, float>(depth2dArraySmplr, depth2dArray, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0, depth2dArraySwzl);
|
c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySmplr, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube_array<float>, float3, uint, float>(depthCubeArraySmplr, depthCubeArray, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0, depthCubeArraySwzl);
|
c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,17 +16,6 @@ inline uint2 spvTexelBufferCoord(uint tc)
|
|||||||
return uint2(tc % 4096, tc / 4096);
|
return uint2(tc % 4096, tc / 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
||||||
@ -39,6 +28,17 @@ template<typename T> inline constexpr thread T&& spvForward(thread typename spvR
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -77,8 +77,8 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
// Wrapper function that swizzles texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
inline vec<T, 4> spvGatherSwizzle(const thread Tex<T>& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -114,8 +114,8 @@ inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
// Wrapper function that swizzles depth texture gathers.
|
||||||
template<typename T, typename Tex, typename... Ts>
|
template<typename T, template<typename, access = access::sample, typename = void> class Tex, typename... Ts>
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
inline vec<T, 4> spvGatherCompareSwizzle(const thread Tex<T>& t, sampler s, uint sw, Ts... params)
|
||||||
{
|
{
|
||||||
if (sw)
|
if (sw)
|
||||||
{
|
{
|
||||||
@ -172,14 +172,14 @@ inline float4 do_samples(thread const texture1d<float> t1, thread const sampler
|
|||||||
c = spvTextureSwizzle(t3.read(uint3(int3(0)), 0), t3Swzl);
|
c = spvTextureSwizzle(t3.read(uint3(int3(0)), 0), t3Swzl);
|
||||||
c = spvTextureSwizzle(t2a.read(uint2(int3(0).xy), uint(int3(0).z), 0), t2aSwzl);
|
c = spvTextureSwizzle(t2a.read(uint2(int3(0).xy), uint(int3(0).z), 0), t2aSwzl);
|
||||||
c = tb.read(spvTexelBufferCoord(0));
|
c = tb.read(spvTexelBufferCoord(0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d<float>, float2, int2>(defaultSampler, t2, float2(0.0), int2(0), component::x, t2Swzl);
|
c = spvGatherSwizzle(t2, defaultSampler, t2Swzl, component::x, float2(0.0), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube<float>, float3>(defaultSampler, tc, float3(0.0), component::y, tcSwzl);
|
c = spvGatherSwizzle(tc, defaultSampler, tcSwzl, component::y, float3(0.0));
|
||||||
c = spvGatherSwizzle<float, metal::texture2d_array<float>, float2, uint, int2>(t2aSmplr, t2a, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0), component::z, t2aSwzl);
|
c = spvGatherSwizzle(t2a, t2aSmplr, t2aSwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0));
|
||||||
c = spvGatherSwizzle<float, metal::texturecube_array<float>, float3, uint>(tcaSmplr, tca, float4(0.0).xyz, uint(round(float4(0.0).w)), component::w, tcaSwzl);
|
c = spvGatherSwizzle(tca, tcaSmplr, tcaSwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)));
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d<float>, float2, float>(d2Smplr, d2, float2(0.0), 1.0, d2Swzl);
|
c = spvGatherCompareSwizzle(d2, d2Smplr, d2Swzl, float2(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube<float>, float3, float>(dcSmplr, dc, float3(0.0), 1.0, dcSwzl);
|
c = spvGatherCompareSwizzle(dc, dcSmplr, dcSwzl, float3(0.0), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depth2d_array<float>, float2, uint, float>(shadowSampler, d2a, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0, d2aSwzl);
|
c = spvGatherCompareSwizzle(d2a, shadowSampler, d2aSwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0);
|
||||||
c = spvGatherCompareSwizzle<float, metal::depthcube_array<float>, float3, uint, float>(dcaSmplr, dca, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0, dcaSwzl);
|
c = spvGatherCompareSwizzle(dca, dcaSmplr, dcaSwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,17 +5,6 @@
|
|||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -65,66 +65,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> foo [[texture(0)]], texture2d<float, access::write> bar [[texture(1)]], sampler fooSmplr [[sampler(0)]])
|
kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d<float> foo [[texture(0)]], texture2d<float, access::write> bar [[texture(1)]], sampler fooSmplr [[sampler(0)]])
|
||||||
{
|
{
|
||||||
constant uint& fooSwzl = spvSwizzleConstants[0];
|
constant uint& fooSwzl = spvSwizzleConstants[0];
|
||||||
|
@ -22,17 +22,6 @@ struct main0_in
|
|||||||
float2 vUV [[user(locn0)]];
|
float2 vUV [[user(locn0)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -82,66 +82,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float4 sample_in_func_1(thread const array<texture2d<float>, 4> uSampler0, thread const array<sampler, 4> uSampler0Smplr, constant uint* uSampler0Swzl, thread float2& vUV)
|
inline float4 sample_in_func_1(thread const array<texture2d<float>, 4> uSampler0, thread const array<sampler, 4> uSampler0Smplr, constant uint* uSampler0Swzl, thread float2& vUV)
|
||||||
{
|
{
|
||||||
return spvTextureSwizzle(uSampler0[2].sample(uSampler0Smplr[2], vUV), uSampler0Swzl[2]);
|
return spvTextureSwizzle(uSampler0[2].sample(uSampler0Smplr[2], vUV), uSampler0Swzl[2]);
|
||||||
|
@ -15,17 +15,6 @@ struct main0_in
|
|||||||
float2 vUV [[user(locn0)]];
|
float2 vUV [[user(locn0)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class spvSwizzle : uint
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
zero,
|
|
||||||
one,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> struct spvRemoveReference { typedef T type; };
|
template<typename T> struct spvRemoveReference { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };
|
||||||
template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };
|
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
|
|||||||
return static_cast<thread T&&>(x);
|
return static_cast<thread T&&>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class spvSwizzle : uint
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
zero,
|
||||||
|
one,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)
|
||||||
{
|
{
|
||||||
@ -75,66 +75,6 @@ inline T spvTextureSwizzle(T x, uint s)
|
|||||||
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function that swizzles texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) METAL_CONST_ARG(c)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
case spvSwizzle::red:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case spvSwizzle::green:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case component::x:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::x);
|
|
||||||
case component::y:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::y);
|
|
||||||
case component::z:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::z);
|
|
||||||
case component::w:
|
|
||||||
return t.gather(s, spvForward<Ts>(params)..., component::w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function that swizzles depth texture gathers.
|
|
||||||
template<typename T, typename Tex, typename... Ts>
|
|
||||||
inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw)
|
|
||||||
{
|
|
||||||
if (sw)
|
|
||||||
{
|
|
||||||
switch (spvSwizzle(sw & 0xFF))
|
|
||||||
{
|
|
||||||
case spvSwizzle::none:
|
|
||||||
case spvSwizzle::red:
|
|
||||||
break;
|
|
||||||
case spvSwizzle::zero:
|
|
||||||
case spvSwizzle::green:
|
|
||||||
case spvSwizzle::blue:
|
|
||||||
case spvSwizzle::alpha:
|
|
||||||
return vec<T, 4>(0, 0, 0, 0);
|
|
||||||
case spvSwizzle::one:
|
|
||||||
return vec<T, 4>(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.gather_compare(s, spvForward<Ts>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float4 sample_in_func(thread const array<texture2d<float>, 4> uSampler, thread const array<sampler, 4> uSamplerSmplr, constant uint* uSamplerSwzl, thread float2& vUV)
|
inline float4 sample_in_func(thread const array<texture2d<float>, 4> uSampler, thread const array<sampler, 4> uSamplerSmplr, constant uint* uSamplerSwzl, thread float2& vUV)
|
||||||
{
|
{
|
||||||
return spvTextureSwizzle(uSampler[2].sample(uSamplerSmplr[2], vUV), uSamplerSwzl[2]);
|
return spvTextureSwizzle(uSampler[2].sample(uSamplerSmplr[2], vUV), uSamplerSwzl[2]);
|
||||||
|
@ -1431,6 +1431,9 @@ enum ExtendedDecorations
|
|||||||
// Used for decorations like resource indices for samplers when part of combined image samplers.
|
// Used for decorations like resource indices for samplers when part of combined image samplers.
|
||||||
// A variable might need to hold two resource indices in this case.
|
// A variable might need to hold two resource indices in this case.
|
||||||
SPIRVCrossDecorationResourceIndexSecondary,
|
SPIRVCrossDecorationResourceIndexSecondary,
|
||||||
|
// Used for resource indices for multiplanar images when part of combined image samplers.
|
||||||
|
SPIRVCrossDecorationResourceIndexTertiary,
|
||||||
|
SPIRVCrossDecorationResourceIndexQuaternary,
|
||||||
|
|
||||||
// Marks a buffer block for using explicit offsets (GLSL/HLSL).
|
// Marks a buffer block for using explicit offsets (GLSL/HLSL).
|
||||||
SPIRVCrossDecorationExplicitOffset,
|
SPIRVCrossDecorationExplicitOffset,
|
||||||
@ -1439,6 +1442,12 @@ enum ExtendedDecorations
|
|||||||
// In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables.
|
// In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables.
|
||||||
SPIRVCrossDecorationBuiltInDispatchBase,
|
SPIRVCrossDecorationBuiltInDispatchBase,
|
||||||
|
|
||||||
|
// Apply to a variable that is a function parameter; marks it as being a "dynamic"
|
||||||
|
// combined image-sampler. In MSL, this is used when a function parameter might hold
|
||||||
|
// either a regular combined image-sampler or one that has an attached sampler
|
||||||
|
// Y'CbCr conversion.
|
||||||
|
SPIRVCrossDecorationDynamicImageSampler,
|
||||||
|
|
||||||
SPIRVCrossDecorationCount
|
SPIRVCrossDecorationCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1152,6 +1152,8 @@ static uint32_t get_default_extended_decoration(ExtendedDecorations decoration)
|
|||||||
{
|
{
|
||||||
case SPIRVCrossDecorationResourceIndexPrimary:
|
case SPIRVCrossDecorationResourceIndexPrimary:
|
||||||
case SPIRVCrossDecorationResourceIndexSecondary:
|
case SPIRVCrossDecorationResourceIndexSecondary:
|
||||||
|
case SPIRVCrossDecorationResourceIndexTertiary:
|
||||||
|
case SPIRVCrossDecorationResourceIndexQuaternary:
|
||||||
case SPIRVCrossDecorationInterfaceMemberIndex:
|
case SPIRVCrossDecorationInterfaceMemberIndex:
|
||||||
return ~(0u);
|
return ~(0u);
|
||||||
|
|
||||||
|
@ -545,6 +545,26 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
|
|||||||
case SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX:
|
case SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX:
|
||||||
options->msl.buffer_size_buffer_index = value;
|
options->msl.buffer_size_buffer_index = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SPVC_COMPILER_OPTION_MSL_MULTIVIEW:
|
||||||
|
options->msl.multiview = value != 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX:
|
||||||
|
options->msl.view_mask_buffer_index = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX:
|
||||||
|
options->msl.device_index = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX:
|
||||||
|
options->msl.view_index_from_device_index = value != 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE:
|
||||||
|
options->msl.dispatch_base = value != 0;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -960,6 +980,22 @@ static void spvc_convert_msl_sampler(MSLConstexprSampler &samp, const spvc_msl_c
|
|||||||
samp.coord = static_cast<MSLSamplerCoord>(sampler->coord);
|
samp.coord = static_cast<MSLSamplerCoord>(sampler->coord);
|
||||||
samp.border_color = static_cast<MSLSamplerBorderColor>(sampler->border_color);
|
samp.border_color = static_cast<MSLSamplerBorderColor>(sampler->border_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spvc_convert_msl_sampler_ycbcr_conversion(MSLConstexprSampler &samp, const spvc_msl_sampler_ycbcr_conversion *conv)
|
||||||
|
{
|
||||||
|
samp.ycbcr_conversion_enable = conv != nullptr;
|
||||||
|
if (conv == nullptr) return;
|
||||||
|
samp.planes = conv->planes;
|
||||||
|
samp.resolution = static_cast<MSLFormatResolution>(conv->resolution);
|
||||||
|
samp.chroma_filter = static_cast<MSLSamplerFilter>(conv->chroma_filter);
|
||||||
|
samp.x_chroma_offset = static_cast<MSLChromaLocation>(conv->x_chroma_offset);
|
||||||
|
samp.y_chroma_offset = static_cast<MSLChromaLocation>(conv->y_chroma_offset);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
samp.swizzle[i] = static_cast<MSLComponentSwizzle>(conv->swizzle[i]);
|
||||||
|
samp.ycbcr_model = static_cast<MSLSamplerYCbCrModelConversion>(conv->ycbcr_model);
|
||||||
|
samp.ycbcr_range = static_cast<MSLSamplerYCbCrRange>(conv->ycbcr_range);
|
||||||
|
samp.bpc = conv->bpc;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id,
|
spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id,
|
||||||
@ -1010,6 +1046,60 @@ spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler c
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id,
|
||||||
|
const spvc_msl_constexpr_sampler *sampler,
|
||||||
|
const spvc_msl_sampler_ycbcr_conversion *conv)
|
||||||
|
{
|
||||||
|
#if SPIRV_CROSS_C_API_MSL
|
||||||
|
if (compiler->backend != SPVC_BACKEND_MSL)
|
||||||
|
{
|
||||||
|
compiler->context->report_error("MSL function used on a non-MSL backend.");
|
||||||
|
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
|
||||||
|
MSLConstexprSampler samp;
|
||||||
|
spvc_convert_msl_sampler(samp, sampler);
|
||||||
|
spvc_convert_msl_sampler_ycbcr_conversion(samp, conv);
|
||||||
|
msl.remap_constexpr_sampler(id, samp);
|
||||||
|
return SPVC_SUCCESS;
|
||||||
|
#else
|
||||||
|
(void)id;
|
||||||
|
(void)sampler;
|
||||||
|
(void)conv;
|
||||||
|
compiler->context->report_error("MSL function used on a non-MSL backend.");
|
||||||
|
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler,
|
||||||
|
unsigned desc_set, unsigned binding,
|
||||||
|
const spvc_msl_constexpr_sampler *sampler,
|
||||||
|
const spvc_msl_sampler_ycbcr_conversion *conv)
|
||||||
|
{
|
||||||
|
#if SPIRV_CROSS_C_API_MSL
|
||||||
|
if (compiler->backend != SPVC_BACKEND_MSL)
|
||||||
|
{
|
||||||
|
compiler->context->report_error("MSL function used on a non-MSL backend.");
|
||||||
|
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
|
||||||
|
MSLConstexprSampler samp;
|
||||||
|
spvc_convert_msl_sampler(samp, sampler);
|
||||||
|
spvc_convert_msl_sampler_ycbcr_conversion(samp, conv);
|
||||||
|
msl.remap_constexpr_sampler_by_binding(desc_set, binding, samp);
|
||||||
|
return SPVC_SUCCESS;
|
||||||
|
#else
|
||||||
|
(void)desc_set;
|
||||||
|
(void)binding;
|
||||||
|
(void)sampler;
|
||||||
|
(void)conv;
|
||||||
|
compiler->context->report_error("MSL function used on a non-MSL backend.");
|
||||||
|
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location,
|
spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location,
|
||||||
unsigned components)
|
unsigned components)
|
||||||
{
|
{
|
||||||
@ -1997,6 +2087,24 @@ void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv)
|
||||||
|
{
|
||||||
|
#if SPIRV_CROSS_C_API_MSL
|
||||||
|
MSLConstexprSampler defaults;
|
||||||
|
conv->planes = defaults.planes;
|
||||||
|
conv->resolution = static_cast<spvc_msl_format_resolution>(defaults.resolution);
|
||||||
|
conv->chroma_filter = static_cast<spvc_msl_sampler_filter>(defaults.chroma_filter);
|
||||||
|
conv->x_chroma_offset = static_cast<spvc_msl_chroma_location>(defaults.x_chroma_offset);
|
||||||
|
conv->y_chroma_offset = static_cast<spvc_msl_chroma_location>(defaults.y_chroma_offset);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
conv->swizzle[i] = static_cast<spvc_msl_component_swizzle>(defaults.swizzle[i]);
|
||||||
|
conv->ycbcr_model = static_cast<spvc_msl_sampler_ycbcr_model_conversion>(defaults.ycbcr_model);
|
||||||
|
conv->ycbcr_range = static_cast<spvc_msl_sampler_ycbcr_range>(defaults.ycbcr_range);
|
||||||
|
#else
|
||||||
|
memset(conv, 0, sizeof(*conv));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler)
|
unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler)
|
||||||
{
|
{
|
||||||
return compiler->compiler->get_current_id_bound();
|
return compiler->compiler->get_current_id_bound();
|
||||||
|
@ -33,7 +33,7 @@ extern "C" {
|
|||||||
/* Bumped if ABI or API breaks backwards compatibility. */
|
/* Bumped if ABI or API breaks backwards compatibility. */
|
||||||
#define SPVC_C_API_VERSION_MAJOR 0
|
#define SPVC_C_API_VERSION_MAJOR 0
|
||||||
/* Bumped if APIs or enumerations are added in a backwards compatible way. */
|
/* Bumped if APIs or enumerations are added in a backwards compatible way. */
|
||||||
#define SPVC_C_API_VERSION_MINOR 16
|
#define SPVC_C_API_VERSION_MINOR 17
|
||||||
/* Bumped if internal implementation details change. */
|
/* Bumped if internal implementation details change. */
|
||||||
#define SPVC_C_API_VERSION_PATCH 0
|
#define SPVC_C_API_VERSION_PATCH 0
|
||||||
|
|
||||||
@ -370,6 +370,55 @@ typedef enum spvc_msl_sampler_border_color
|
|||||||
SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
|
SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
|
||||||
} spvc_msl_sampler_border_color;
|
} spvc_msl_sampler_border_color;
|
||||||
|
|
||||||
|
/* Maps to C++ API. */
|
||||||
|
typedef enum spvc_msl_format_resolution
|
||||||
|
{
|
||||||
|
SPVC_MSL_FORMAT_RESOLUTION_444 = 0,
|
||||||
|
SPVC_MSL_FORMAT_RESOLUTION_422,
|
||||||
|
SPVC_MSL_FORMAT_RESOLUTION_420,
|
||||||
|
SPVC_MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff
|
||||||
|
} spvc_msl_format_resolution;
|
||||||
|
|
||||||
|
/* Maps to C++ API. */
|
||||||
|
typedef enum spvc_msl_chroma_location
|
||||||
|
{
|
||||||
|
SPVC_MSL_CHROMA_LOCATION_COSITED_EVEN = 0,
|
||||||
|
SPVC_MSL_CHROMA_LOCATION_MIDPOINT,
|
||||||
|
SPVC_MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff
|
||||||
|
} spvc_msl_chroma_location;
|
||||||
|
|
||||||
|
/* Maps to C++ API. */
|
||||||
|
typedef enum spvc_msl_component_swizzle
|
||||||
|
{
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_IDENTITY = 0,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_ZERO,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_ONE,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_R,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_G,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_B,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_A,
|
||||||
|
SPVC_MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff
|
||||||
|
} spvc_msl_component_swizzle;
|
||||||
|
|
||||||
|
/* Maps to C++ API. */
|
||||||
|
typedef enum spvc_msl_sampler_ycbcr_model_conversion
|
||||||
|
{
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff
|
||||||
|
} spvc_msl_sampler_ycbcr_model_conversion;
|
||||||
|
|
||||||
|
/* Maps to C+ API. */
|
||||||
|
typedef enum spvc_msl_sampler_ycbcr_range
|
||||||
|
{
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW,
|
||||||
|
SPVC_MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff
|
||||||
|
} spvc_msl_sampler_ycbcr_range;
|
||||||
|
|
||||||
/* Maps to C++ API. */
|
/* Maps to C++ API. */
|
||||||
typedef struct spvc_msl_constexpr_sampler
|
typedef struct spvc_msl_constexpr_sampler
|
||||||
{
|
{
|
||||||
@ -397,6 +446,26 @@ typedef struct spvc_msl_constexpr_sampler
|
|||||||
*/
|
*/
|
||||||
SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler);
|
SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler);
|
||||||
|
|
||||||
|
/* Maps to the sampler Y'CbCr conversion-related portions of MSLConstexprSampler. See C++ API for defaults and details. */
|
||||||
|
typedef struct spvc_msl_sampler_ycbcr_conversion
|
||||||
|
{
|
||||||
|
unsigned planes;
|
||||||
|
spvc_msl_format_resolution resolution;
|
||||||
|
spvc_msl_sampler_filter chroma_filter;
|
||||||
|
spvc_msl_chroma_location x_chroma_offset;
|
||||||
|
spvc_msl_chroma_location y_chroma_offset;
|
||||||
|
spvc_msl_component_swizzle swizzle[4];
|
||||||
|
spvc_msl_sampler_ycbcr_model_conversion ycbcr_model;
|
||||||
|
spvc_msl_sampler_ycbcr_range ycbcr_range;
|
||||||
|
unsigned bpc;
|
||||||
|
} spvc_msl_sampler_ycbcr_conversion;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes the constexpr sampler struct.
|
||||||
|
* The defaults are non-zero.
|
||||||
|
*/
|
||||||
|
SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv);
|
||||||
|
|
||||||
/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */
|
/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */
|
||||||
typedef enum spvc_compiler_option
|
typedef enum spvc_compiler_option
|
||||||
{
|
{
|
||||||
@ -452,6 +521,12 @@ typedef enum spvc_compiler_option
|
|||||||
|
|
||||||
SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES = 37 | SPVC_COMPILER_OPTION_COMMON_BIT,
|
SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES = 37 | SPVC_COMPILER_OPTION_COMMON_BIT,
|
||||||
|
|
||||||
|
SPVC_COMPILER_OPTION_MSL_MULTIVIEW = 38 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||||
|
SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX = 39 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||||
|
SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX = 40 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||||
|
SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX = 41 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||||
|
SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE = 42 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||||
|
|
||||||
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
|
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
|
||||||
} spvc_compiler_option;
|
} spvc_compiler_option;
|
||||||
|
|
||||||
@ -550,6 +625,8 @@ SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compi
|
|||||||
unsigned binding);
|
unsigned binding);
|
||||||
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler);
|
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler);
|
||||||
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler);
|
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler);
|
||||||
|
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv);
|
||||||
|
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv);
|
||||||
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components);
|
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components);
|
||||||
|
|
||||||
SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id);
|
SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id);
|
||||||
|
@ -2719,7 +2719,7 @@ void CompilerGLSL::emit_resources()
|
|||||||
// Returns a string representation of the ID, usable as a function arg.
|
// Returns a string representation of the ID, usable as a function arg.
|
||||||
// Default is to simply return the expression representation fo the arg ID.
|
// Default is to simply return the expression representation fo the arg ID.
|
||||||
// Subclasses may override to modify the return value.
|
// Subclasses may override to modify the return value.
|
||||||
string CompilerGLSL::to_func_call_arg(uint32_t id)
|
string CompilerGLSL::to_func_call_arg(const SPIRFunction::Parameter &, uint32_t id)
|
||||||
{
|
{
|
||||||
// Make sure that we use the name of the original variable, and not the parameter alias.
|
// Make sure that we use the name of the original variable, and not the parameter alias.
|
||||||
uint32_t name_id = id;
|
uint32_t name_id = id;
|
||||||
@ -4796,12 +4796,40 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
|
|||||||
{
|
{
|
||||||
auto *ops = stream(i);
|
auto *ops = stream(i);
|
||||||
auto op = static_cast<Op>(i.op);
|
auto op = static_cast<Op>(i.op);
|
||||||
uint32_t length = i.length;
|
|
||||||
|
|
||||||
SmallVector<uint32_t> inherited_expressions;
|
SmallVector<uint32_t> inherited_expressions;
|
||||||
|
|
||||||
uint32_t result_type_id = ops[0];
|
uint32_t result_type_id = ops[0];
|
||||||
uint32_t id = ops[1];
|
uint32_t id = ops[1];
|
||||||
|
|
||||||
|
bool forward = false;
|
||||||
|
string expr = to_texture_op(i, &forward, inherited_expressions);
|
||||||
|
emit_op(result_type_id, id, expr, forward);
|
||||||
|
for (auto &inherit : inherited_expressions)
|
||||||
|
inherit_expression_dependencies(id, inherit);
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case OpImageSampleDrefImplicitLod:
|
||||||
|
case OpImageSampleImplicitLod:
|
||||||
|
case OpImageSampleProjImplicitLod:
|
||||||
|
case OpImageSampleProjDrefImplicitLod:
|
||||||
|
register_control_dependent_expression(id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CompilerGLSL::to_texture_op(const Instruction &i, bool *forward,
|
||||||
|
SmallVector<uint32_t> &inherited_expressions)
|
||||||
|
{
|
||||||
|
auto *ops = stream(i);
|
||||||
|
auto op = static_cast<Op>(i.op);
|
||||||
|
uint32_t length = i.length;
|
||||||
|
|
||||||
|
uint32_t result_type_id = ops[0];
|
||||||
uint32_t img = ops[2];
|
uint32_t img = ops[2];
|
||||||
uint32_t coord = ops[3];
|
uint32_t coord = ops[3];
|
||||||
uint32_t dref = 0;
|
uint32_t dref = 0;
|
||||||
@ -4942,12 +4970,11 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
|
|||||||
test(minlod, ImageOperandsMinLodMask);
|
test(minlod, ImageOperandsMinLodMask);
|
||||||
|
|
||||||
string expr;
|
string expr;
|
||||||
bool forward = false;
|
|
||||||
expr += to_function_name(img, imgtype, !!fetch, !!gather, !!proj, !!coffsets, (!!coffset || !!offset),
|
expr += to_function_name(img, imgtype, !!fetch, !!gather, !!proj, !!coffsets, (!!coffset || !!offset),
|
||||||
(!!grad_x || !!grad_y), !!dref, lod, minlod);
|
(!!grad_x || !!grad_y), !!dref, lod, minlod);
|
||||||
expr += "(";
|
expr += "(";
|
||||||
expr += to_function_args(img, imgtype, fetch, gather, proj, coord, coord_components, dref, grad_x, grad_y, lod,
|
expr += to_function_args(img, imgtype, fetch, gather, proj, coord, coord_components, dref, grad_x, grad_y, lod,
|
||||||
coffset, offset, bias, comp, sample, minlod, &forward);
|
coffset, offset, bias, comp, sample, minlod, forward);
|
||||||
expr += ")";
|
expr += ")";
|
||||||
|
|
||||||
// texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here.
|
// texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here.
|
||||||
@ -4989,22 +5016,7 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
|
|||||||
if (op == OpImageRead)
|
if (op == OpImageRead)
|
||||||
expr = remap_swizzle(result_type, 4, expr);
|
expr = remap_swizzle(result_type, 4, expr);
|
||||||
|
|
||||||
emit_op(result_type_id, id, expr, forward);
|
return expr;
|
||||||
for (auto &inherit : inherited_expressions)
|
|
||||||
inherit_expression_dependencies(id, inherit);
|
|
||||||
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case OpImageSampleDrefImplicitLod:
|
|
||||||
case OpImageSampleImplicitLod:
|
|
||||||
case OpImageSampleProjImplicitLod:
|
|
||||||
case OpImageSampleProjDrefImplicitLod:
|
|
||||||
register_control_dependent_expression(id);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerGLSL::expression_is_constant_null(uint32_t id) const
|
bool CompilerGLSL::expression_is_constant_null(uint32_t id) const
|
||||||
@ -8078,7 +8090,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|||||||
if (skip_argument(arg[i]))
|
if (skip_argument(arg[i]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
arglist.push_back(to_func_call_arg(arg[i]));
|
arglist.push_back(to_func_call_arg(callee.arguments[i], arg[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &combined : callee.combined_parameters)
|
for (auto &combined : callee.combined_parameters)
|
||||||
@ -10126,7 +10138,7 @@ void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t in
|
|||||||
if (var_id)
|
if (var_id)
|
||||||
flush_variable_declaration(var_id);
|
flush_variable_declaration(var_id);
|
||||||
|
|
||||||
arglist.push_back(to_func_call_arg(arg.id));
|
arglist.push_back(to_func_call_arg(arg, arg.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +244,8 @@ protected:
|
|||||||
|
|
||||||
virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id);
|
virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id);
|
||||||
virtual void emit_texture_op(const Instruction &i);
|
virtual void emit_texture_op(const Instruction &i);
|
||||||
|
virtual std::string to_texture_op(const Instruction &i, bool *forward,
|
||||||
|
SmallVector<uint32_t> &inherited_expressions);
|
||||||
virtual void emit_subgroup_op(const Instruction &i);
|
virtual void emit_subgroup_op(const Instruction &i);
|
||||||
virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0);
|
virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0);
|
||||||
virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage);
|
virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage);
|
||||||
@ -256,7 +258,7 @@ protected:
|
|||||||
virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
|
virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
|
||||||
virtual void emit_fixup();
|
virtual void emit_fixup();
|
||||||
virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
|
virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
|
||||||
virtual std::string to_func_call_arg(uint32_t id);
|
virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id);
|
||||||
virtual std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
|
virtual std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
|
||||||
bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad,
|
bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad,
|
||||||
bool has_dref, uint32_t lod, uint32_t minlod);
|
bool has_dref, uint32_t lod, uint32_t minlod);
|
||||||
|
@ -2014,9 +2014,9 @@ void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string CompilerHLSL::to_func_call_arg(uint32_t id)
|
string CompilerHLSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id)
|
||||||
{
|
{
|
||||||
string arg_str = CompilerGLSL::to_func_call_arg(id);
|
string arg_str = CompilerGLSL::to_func_call_arg(arg, id);
|
||||||
|
|
||||||
if (hlsl_options.shader_model <= 30)
|
if (hlsl_options.shader_model <= 30)
|
||||||
return arg_str;
|
return arg_str;
|
||||||
|
@ -145,7 +145,7 @@ private:
|
|||||||
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
|
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
|
||||||
std::string to_interpolation_qualifiers(const Bitset &flags) override;
|
std::string to_interpolation_qualifiers(const Bitset &flags) override;
|
||||||
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
|
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
|
||||||
std::string to_func_call_arg(uint32_t id) override;
|
std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
|
||||||
std::string to_sampler_expression(uint32_t id);
|
std::string to_sampler_expression(uint32_t id);
|
||||||
std::string to_resource_binding(const SPIRVariable &var);
|
std::string to_resource_binding(const SPIRVariable &var);
|
||||||
std::string to_resource_binding_sampler(const SPIRVariable &var);
|
std::string to_resource_binding_sampler(const SPIRVariable &var);
|
||||||
|
1536
spirv_msl.cpp
1536
spirv_msl.cpp
File diff suppressed because it is too large
Load Diff
119
spirv_msl.hpp
119
spirv_msl.hpp
@ -122,6 +122,50 @@ enum MSLSamplerBorderColor
|
|||||||
MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
|
MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum MSLFormatResolution
|
||||||
|
{
|
||||||
|
MSL_FORMAT_RESOLUTION_444 = 0,
|
||||||
|
MSL_FORMAT_RESOLUTION_422,
|
||||||
|
MSL_FORMAT_RESOLUTION_420,
|
||||||
|
MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MSLChromaLocation
|
||||||
|
{
|
||||||
|
MSL_CHROMA_LOCATION_COSITED_EVEN = 0,
|
||||||
|
MSL_CHROMA_LOCATION_MIDPOINT,
|
||||||
|
MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MSLComponentSwizzle
|
||||||
|
{
|
||||||
|
MSL_COMPONENT_SWIZZLE_IDENTITY = 0,
|
||||||
|
MSL_COMPONENT_SWIZZLE_ZERO,
|
||||||
|
MSL_COMPONENT_SWIZZLE_ONE,
|
||||||
|
MSL_COMPONENT_SWIZZLE_R,
|
||||||
|
MSL_COMPONENT_SWIZZLE_G,
|
||||||
|
MSL_COMPONENT_SWIZZLE_B,
|
||||||
|
MSL_COMPONENT_SWIZZLE_A,
|
||||||
|
MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MSLSamplerYCbCrModelConversion
|
||||||
|
{
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0,
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY,
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709,
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601,
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020,
|
||||||
|
MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MSLSamplerYCbCrRange
|
||||||
|
{
|
||||||
|
MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0,
|
||||||
|
MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW,
|
||||||
|
MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff
|
||||||
|
};
|
||||||
|
|
||||||
struct MSLConstexprSampler
|
struct MSLConstexprSampler
|
||||||
{
|
{
|
||||||
MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED;
|
MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED;
|
||||||
@ -137,9 +181,39 @@ struct MSLConstexprSampler
|
|||||||
float lod_clamp_max = 1000.0f;
|
float lod_clamp_max = 1000.0f;
|
||||||
int max_anisotropy = 1;
|
int max_anisotropy = 1;
|
||||||
|
|
||||||
|
// Sampler Y'CbCr conversion parameters
|
||||||
|
uint32_t planes = 0;
|
||||||
|
MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444;
|
||||||
|
MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST;
|
||||||
|
MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN;
|
||||||
|
MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN;
|
||||||
|
MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY
|
||||||
|
MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||||
|
MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||||
|
uint32_t bpc = 8;
|
||||||
|
|
||||||
bool compare_enable = false;
|
bool compare_enable = false;
|
||||||
bool lod_clamp_enable = false;
|
bool lod_clamp_enable = false;
|
||||||
bool anisotropy_enable = false;
|
bool anisotropy_enable = false;
|
||||||
|
bool ycbcr_conversion_enable = false;
|
||||||
|
|
||||||
|
MSLConstexprSampler()
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < 4; i++)
|
||||||
|
swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
}
|
||||||
|
bool swizzle_is_identity() const
|
||||||
|
{
|
||||||
|
return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY &&
|
||||||
|
swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY);
|
||||||
|
}
|
||||||
|
bool swizzle_has_one_or_zero() const
|
||||||
|
{
|
||||||
|
return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE ||
|
||||||
|
swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE ||
|
||||||
|
swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE ||
|
||||||
|
swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special constant used in a MSLResourceBinding desc_set
|
// Special constant used in a MSLResourceBinding desc_set
|
||||||
@ -349,6 +423,14 @@ public:
|
|||||||
// sampler's binding is returned instead. For any other resource type, -1 is returned.
|
// sampler's binding is returned instead. For any other resource type, -1 is returned.
|
||||||
uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
|
uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
|
||||||
|
|
||||||
|
// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images,
|
||||||
|
// in which case the second plane's binding is returned instead. For any other resource type, -1 is returned.
|
||||||
|
uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const;
|
||||||
|
|
||||||
|
// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images,
|
||||||
|
// in which case the third plane's binding is returned instead. For any other resource type, -1 is returned.
|
||||||
|
uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const;
|
||||||
|
|
||||||
// Compiles the SPIR-V code into Metal Shading Language.
|
// Compiles the SPIR-V code into Metal Shading Language.
|
||||||
std::string compile() override;
|
std::string compile() override;
|
||||||
|
|
||||||
@ -395,7 +477,14 @@ protected:
|
|||||||
SPVFuncImplInverse4x4,
|
SPVFuncImplInverse4x4,
|
||||||
SPVFuncImplInverse3x3,
|
SPVFuncImplInverse3x3,
|
||||||
SPVFuncImplInverse2x2,
|
SPVFuncImplInverse2x2,
|
||||||
|
// It is very important that this come before *Swizzle and ChromaReconstruct*, to ensure it's
|
||||||
|
// emitted before them.
|
||||||
|
SPVFuncImplForwardArgs,
|
||||||
|
// Likewise, this must come before *Swizzle.
|
||||||
|
SPVFuncImplGetSwizzle,
|
||||||
SPVFuncImplTextureSwizzle,
|
SPVFuncImplTextureSwizzle,
|
||||||
|
SPVFuncImplGatherSwizzle,
|
||||||
|
SPVFuncImplGatherCompareSwizzle,
|
||||||
SPVFuncImplSubgroupBallot,
|
SPVFuncImplSubgroupBallot,
|
||||||
SPVFuncImplSubgroupBallotBitExtract,
|
SPVFuncImplSubgroupBallotBitExtract,
|
||||||
SPVFuncImplSubgroupBallotFindLSB,
|
SPVFuncImplSubgroupBallotFindLSB,
|
||||||
@ -405,6 +494,27 @@ protected:
|
|||||||
SPVFuncImplReflectScalar,
|
SPVFuncImplReflectScalar,
|
||||||
SPVFuncImplRefractScalar,
|
SPVFuncImplRefractScalar,
|
||||||
SPVFuncImplFaceForwardScalar,
|
SPVFuncImplFaceForwardScalar,
|
||||||
|
SPVFuncImplChromaReconstructNearest2Plane,
|
||||||
|
SPVFuncImplChromaReconstructNearest3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear422CositedEven2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear422CositedEven3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear422Midpoint2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear422Midpoint3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane,
|
||||||
|
SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane,
|
||||||
|
SPVFuncImplExpandITUFullRange,
|
||||||
|
SPVFuncImplExpandITUNarrowRange,
|
||||||
|
SPVFuncImplConvertYCbCrBT709,
|
||||||
|
SPVFuncImplConvertYCbCrBT601,
|
||||||
|
SPVFuncImplConvertYCbCrBT2020,
|
||||||
|
SPVFuncImplDynamicImageSampler,
|
||||||
|
|
||||||
SPVFuncImplArrayCopyMultidimMax = 6
|
SPVFuncImplArrayCopyMultidimMax = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -418,6 +528,8 @@ protected:
|
|||||||
void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
|
void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
|
||||||
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
|
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
|
||||||
void emit_subgroup_op(const Instruction &i) override;
|
void emit_subgroup_op(const Instruction &i) override;
|
||||||
|
std::string to_texture_op(const Instruction &i, bool *forward,
|
||||||
|
SmallVector<uint32_t> &inherited_expressions) override;
|
||||||
void emit_fixup() override;
|
void emit_fixup() override;
|
||||||
std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||||
const std::string &qualifier = "");
|
const std::string &qualifier = "");
|
||||||
@ -428,7 +540,7 @@ protected:
|
|||||||
std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
|
std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||||
std::string sampler_type(const SPIRType &type);
|
std::string sampler_type(const SPIRType &type);
|
||||||
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
|
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
|
||||||
std::string to_func_call_arg(uint32_t id) override;
|
std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
|
||||||
std::string to_name(uint32_t id, bool allow_alias = true) const override;
|
std::string to_name(uint32_t id, bool allow_alias = true) const override;
|
||||||
std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
|
std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
|
||||||
bool has_array_offsets, bool has_offset, bool has_grad, bool has_dref, uint32_t lod,
|
bool has_array_offsets, bool has_offset, bool has_grad, bool has_dref, uint32_t lod,
|
||||||
@ -513,7 +625,7 @@ protected:
|
|||||||
std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
|
std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
|
||||||
std::string argument_decl(const SPIRFunction::Parameter &arg);
|
std::string argument_decl(const SPIRFunction::Parameter &arg);
|
||||||
std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
|
std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
|
||||||
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype);
|
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
|
||||||
uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
|
uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
|
||||||
|
|
||||||
// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
|
// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
|
||||||
@ -652,6 +764,7 @@ protected:
|
|||||||
std::string sampler_name_suffix = "Smplr";
|
std::string sampler_name_suffix = "Smplr";
|
||||||
std::string swizzle_name_suffix = "Swzl";
|
std::string swizzle_name_suffix = "Swzl";
|
||||||
std::string buffer_size_name_suffix = "BufferSize";
|
std::string buffer_size_name_suffix = "BufferSize";
|
||||||
|
std::string plane_name_suffix = "Plane";
|
||||||
std::string input_wg_var_name = "gl_in";
|
std::string input_wg_var_name = "gl_in";
|
||||||
std::string output_buffer_var_name = "spvOut";
|
std::string output_buffer_var_name = "spvOut";
|
||||||
std::string patch_output_buffer_var_name = "spvPatchOut";
|
std::string patch_output_buffer_var_name = "spvPatchOut";
|
||||||
@ -676,6 +789,8 @@ protected:
|
|||||||
|
|
||||||
bool suppress_missing_prototypes = false;
|
bool suppress_missing_prototypes = false;
|
||||||
|
|
||||||
|
void add_spv_func_and_recompile(SPVFuncImpl spv_func);
|
||||||
|
|
||||||
// OpcodeHandler that handles several MSL preprocessing operations.
|
// OpcodeHandler that handles several MSL preprocessing operations.
|
||||||
struct OpCodePreprocessor : OpcodeHandler
|
struct OpCodePreprocessor : OpcodeHandler
|
||||||
{
|
{
|
||||||
|
103
tests-other/msl_ycbcr_conversion_test.cpp
Normal file
103
tests-other/msl_ycbcr_conversion_test.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Testbench for MSL constexpr samplers, with Y'CbCr conversion.
|
||||||
|
// It does not validate output, but it's useful for ad-hoc testing.
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spirv_cross_c.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define SPVC_CHECKED_CALL(x) do { \
|
||||||
|
if ((x) != SPVC_SUCCESS) { \
|
||||||
|
fprintf(stderr, "Failed at line %d.\n", __LINE__); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#define SPVC_CHECKED_CALL_NEGATIVE(x) do { \
|
||||||
|
g_fail_on_error = SPVC_FALSE; \
|
||||||
|
if ((x) == SPVC_SUCCESS) { \
|
||||||
|
fprintf(stderr, "Failed at line %d.\n", __LINE__); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
g_fail_on_error = SPVC_TRUE; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static std::vector<SpvId> read_file(const char *path)
|
||||||
|
{
|
||||||
|
long len;
|
||||||
|
FILE *file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
len = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
std::vector<SpvId> buffer(len / sizeof(SpvId));
|
||||||
|
if (fread(buffer.data(), 1, len, file) != (size_t)len)
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
auto buffer = read_file(argv[1]);
|
||||||
|
if (buffer.empty())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
spvc_context ctx;
|
||||||
|
spvc_parsed_ir parsed_ir;
|
||||||
|
spvc_compiler compiler;
|
||||||
|
spvc_compiler_options options;
|
||||||
|
|
||||||
|
SPVC_CHECKED_CALL(spvc_context_create(&ctx));
|
||||||
|
SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir));
|
||||||
|
SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_MSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler));
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler, &options));
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_VERSION, SPVC_MAKE_MSL_VERSION(2, 0, 0)));
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler, options));
|
||||||
|
|
||||||
|
spvc_msl_resource_binding binding;
|
||||||
|
spvc_msl_resource_binding_init(&binding);
|
||||||
|
binding.desc_set = 1;
|
||||||
|
binding.binding = 2;
|
||||||
|
binding.stage = SpvExecutionModelFragment;
|
||||||
|
binding.msl_texture = 0;
|
||||||
|
binding.msl_sampler = 0;
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding));
|
||||||
|
|
||||||
|
spvc_msl_constexpr_sampler samp;
|
||||||
|
spvc_msl_sampler_ycbcr_conversion conv;
|
||||||
|
spvc_msl_constexpr_sampler_init(&samp);
|
||||||
|
spvc_msl_sampler_ycbcr_conversion_init(&conv);
|
||||||
|
conv.planes = 3;
|
||||||
|
conv.resolution = SPVC_MSL_FORMAT_RESOLUTION_422;
|
||||||
|
conv.chroma_filter = SPVC_MSL_SAMPLER_FILTER_LINEAR;
|
||||||
|
conv.x_chroma_offset = SPVC_MSL_CHROMA_LOCATION_MIDPOINT;
|
||||||
|
conv.ycbcr_model = SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020;
|
||||||
|
conv.ycbcr_range = SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||||
|
conv.bpc = 8;
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(compiler, 1, 2, &samp, &conv));
|
||||||
|
|
||||||
|
const char *str;
|
||||||
|
SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str));
|
||||||
|
|
||||||
|
// Should be marked, as a sanity check.
|
||||||
|
if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 2))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
fprintf(stderr, "Output:\n%s\n", str);
|
||||||
|
}
|
||||||
|
|
BIN
tests-other/msl_ycbcr_conversion_test.spv
Normal file
BIN
tests-other/msl_ycbcr_conversion_test.spv
Normal file
Binary file not shown.
BIN
tests-other/msl_ycbcr_conversion_test_2.spv
Normal file
BIN
tests-other/msl_ycbcr_conversion_test_2.spv
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user