| /* |
| * Copyright 2023 Rive |
| */ |
| |
| // This header provides Metal-specific #defines and declarations that enable our |
| // shaders to be compiled on MSL and GLSL both. |
| |
| #define METAL |
| |
| // #define native metal types if their names are being rewritten. |
| #define _ARE_TOKEN_NAMES_PRESERVED |
| #ifndef $_ARE_TOKEN_NAMES_PRESERVED |
| #define half $half |
| #define half2 $half2 |
| #define half3 $half3 |
| #define half4 $half4 |
| #define short $short |
| #define short2 $short2 |
| #define short3 $short3 |
| #define short4 $short4 |
| #define ushort $ushort |
| #define ushort2 $ushort2 |
| #define ushort3 $ushort3 |
| #define ushort4 $ushort4 |
| #define float2 $float2 |
| #define float3 $float3 |
| #define packed_float3 $packed_float3 |
| #define float4 $float4 |
| #define bool2 $bool2 |
| #define bool3 $bool3 |
| #define bool4 $bool4 |
| #define uint2 $uint2 |
| #define uint3 $uint3 |
| #define uint4 $uint4 |
| #define int2 $int2 |
| #define int3 $int3 |
| #define int4 $int4 |
| #define float4x2 $float4x2 |
| #define ushort $ushort |
| #define float2x2 $float2x2 |
| #define half3x3 $half3x3 |
| #define half2x3 $half2x3 |
| #define half4x4 $half4x4 |
| #endif |
| |
| #define INLINE $inline |
| #define OUT(ARG_TYPE) $thread ARG_TYPE& |
| #define INOUT(ARG_TYPE) $thread ARG_TYPE& |
| |
| #define equal(A, B) ((A) == (B)) |
| #define notEqual(A, B) ((A) != (B)) |
| #define lessThanEqual(A, B) ((A) <= (B)) |
| #define lessThan(A, B) ((A) < (B)) |
| #define greaterThan(A, B) ((A) > (B)) |
| #define greaterThanEqual(A, B) ((A) >= (B)) |
| #define MUL(A, B) ((A) * (B)) |
| #define inversesqrt $rsqrt |
| |
| #define UNIFORM_BLOCK_BEGIN(IDX, NAME) \ |
| struct NAME \ |
| { |
| #define UNIFORM_BLOCK_END(NAME) \ |
| } \ |
| ; |
| |
| #define ATTR_BLOCK_BEGIN(NAME) \ |
| struct NAME \ |
| { |
| #define ATTR(IDX, TYPE, NAME) TYPE NAME |
| #define ATTR_BLOCK_END \ |
| } \ |
| ; |
| #define ATTR_UNPACK(ID, attrs, NAME, TYPE) TYPE NAME = attrs[ID].NAME |
| |
| #define VARYING_BLOCK_BEGIN \ |
| struct Varyings \ |
| { |
| #define VARYING(IDX, TYPE, NAME) TYPE NAME |
| #define FLAT [[flat]] |
| #define NO_PERSPECTIVE [[$center_no_perspective]] |
| #ifndef @OPTIONALLY_FLAT |
| // Don't use no-perspective interpolation for varyings that need to be flat. |
| // No-persective interpolation appears to break the guarantee that a varying == |
| // "x" when all barycentric values also == "x". Default (perspective-correct) |
| // interpolation does preserve this guarantee, and seems to be faster faster |
| // than flat on Apple Silicon. |
| #define @OPTIONALLY_FLAT |
| #endif |
| #define VARYING_BLOCK_END \ |
| float4 _pos [[$position]] [[$invariant]]; \ |
| } \ |
| ; |
| |
| #define VARYING_INIT(NAME, TYPE) $thread TYPE& NAME = _varyings.NAME |
| #define VARYING_PACK(NAME) |
| #define VARYING_UNPACK(NAME, TYPE) TYPE NAME = _varyings.NAME |
| |
| #define VERTEX_STORAGE_BUFFER_BLOCK_BEGIN \ |
| struct VertexStorageBuffers \ |
| { |
| #define VERTEX_STORAGE_BUFFER_BLOCK_END \ |
| } \ |
| ; |
| |
| #define FRAG_STORAGE_BUFFER_BLOCK_BEGIN \ |
| struct FragmentStorageBuffers \ |
| { |
| #define FRAG_STORAGE_BUFFER_BLOCK_END \ |
| } \ |
| ; |
| |
| #define STORAGE_BUFFER_U32x2(IDX, GLSL_STRUCT_NAME, NAME) \ |
| $constant uint2* NAME [[$buffer(METAL_BUFFER_IDX(IDX))]] |
| #define STORAGE_BUFFER_U32x4(IDX, GLSL_STRUCT_NAME, NAME) \ |
| $constant uint4* NAME [[$buffer(METAL_BUFFER_IDX(IDX))]] |
| #define STORAGE_BUFFER_F32x4(IDX, GLSL_STRUCT_NAME, NAME) \ |
| $constant float4* NAME [[$buffer(METAL_BUFFER_IDX(IDX))]] |
| #define STORAGE_BUFFER_LOAD4(NAME, I) _buffers.NAME[I] |
| #define STORAGE_BUFFER_LOAD2(NAME, I) _buffers.NAME[I] |
| |
| #define VERTEX_TEXTURE_BLOCK_BEGIN \ |
| struct VertexTextures \ |
| { |
| #define VERTEX_TEXTURE_BLOCK_END \ |
| } \ |
| ; |
| |
| #define FRAG_TEXTURE_BLOCK_BEGIN \ |
| struct FragmentTextures \ |
| { |
| #define FRAG_TEXTURE_BLOCK_END \ |
| } \ |
| ; |
| |
| #define DYNAMIC_SAMPLER_BLOCK_BEGIN \ |
| struct DynamicSamplers \ |
| { |
| #define DYNAMIC_SAMPLER_BLOCK_END \ |
| } \ |
| ; |
| |
| #define TEXTURE_RGBA32UI(SET, IDX, NAME) [[$texture(IDX)]] $texture2d<uint> NAME |
| #define TEXTURE_RGBA32F(SET, IDX, NAME) [[$texture(IDX)]] $texture2d<float> NAME |
| #define TEXTURE_RGBA8(SET, IDX, NAME) [[$texture(IDX)]] $texture2d<half> NAME |
| #define TEXTURE_R16F(SET, IDX, NAME) [[$texture(IDX)]] $texture2d<half> NAME |
| #define TEXTURE_R16F_1D_ARRAY(SET, IDX, NAME) \ |
| [[$texture(IDX)]] $texture1d_array<half> NAME |
| |
| #define SAMPLER_LINEAR(TEXTURE_IDX, NAME) \ |
| $constexpr $sampler NAME($filter::$linear, $mip_filter::$none); |
| #define SAMPLER_MIPMAP(TEXTURE_IDX, NAME) \ |
| $constexpr $sampler NAME($filter::$linear, $mip_filter::$linear); |
| #define SAMPLER_DYNAMIC(SET, IDX, NAME) [[$sampler(IDX)]] $sampler NAME; |
| #define TEXEL_FETCH(TEXTURE, COORD) _textures.TEXTURE.$read(uint2(COORD)) |
| #define TEXTURE_SAMPLE(TEXTURE, SAMPLER_NAME, COORD) \ |
| _textures.TEXTURE.$sample(SAMPLER_NAME, COORD) |
| #define TEXTURE_SAMPLE_LOD(TEXTURE, SAMPLER_NAME, COORD, LOD) \ |
| _textures.TEXTURE.$sample(SAMPLER_NAME, COORD, $level(LOD)) |
| #define TEXTURE_SAMPLE_LODBIAS(TEXTURE, SAMPLER_NAME, COORD, LODBIAS) \ |
| _textures.TEXTURE.$sample(SAMPLER_NAME, COORD, $bias(LODBIAS)) |
| #define TEXTURE_SAMPLE_GRAD(TEXTURE, SAMPLER_NAME, COORD, DDX, DDY) \ |
| _textures.TEXTURE.$sample(SAMPLER_NAME, COORD, $gradient2d(DDX, DDY)) |
| #define TEXTURE_GATHER(TEXTURE, SAMPLER_NAME, COORD, TEXTURE_INVERSE_SIZE) \ |
| _textures.TEXTURE.$gather(SAMPLER_NAME, (COORD) * (TEXTURE_INVERSE_SIZE)) |
| #define TEXTURE_SAMPLE_DYNAMIC(TEXTURE, SAMPLER_NAME, COORD) \ |
| _textures.TEXTURE.$sample(_dynamicSampler.SAMPLER_NAME, COORD) |
| #define TEXTURE_SAMPLE_DYNAMIC_LOD(TEXTURE, SAMPLER_NAME, COORD, LOD) \ |
| _textures.TEXTURE.$sample(_dynamicSampler.SAMPLER_NAME, COORD, $level(LOD)) |
| #define TEXTURE_SAMPLE_DYNAMIC_LODBIAS(TEXTURE, SAMPLER_NAME, COORD, LODBIAS) \ |
| _textures.TEXTURE.$sample(_dynamicSampler.SAMPLER_NAME, \ |
| COORD, \ |
| $bias(LODBIAS)) |
| #define TEXTURE_SAMPLE_LOD_1D_ARRAY(TEXTURE, \ |
| SAMPLER_NAME, \ |
| X, \ |
| ARRAY_INDEX, \ |
| ARRAY_INDEX_NORMALIZED, \ |
| LOD) \ |
| _textures.TEXTURE.$sample(SAMPLER_NAME, X, ARRAY_INDEX) |
| |
| #define VERTEX_CONTEXT_DECL \ |
| , $constant @FlushUniforms &uniforms, VertexTextures _textures, \ |
| VertexStorageBuffers _buffers |
| #define VERTEX_CONTEXT_UNPACK , uniforms, _textures, _buffers |
| |
| #ifdef @ENABLE_INSTANCE_INDEX |
| #define VERTEX_MAIN(NAME, Attrs, attrs, _vertexID, _instanceID) \ |
| $__attribute__(($visibility("default"))) Varyings $vertex NAME( \ |
| uint _vertexID [[$vertex_id]], \ |
| uint _instanceID [[$instance_id]], \ |
| $constant uint& _baseInstance \ |
| [[$buffer(METAL_BUFFER_IDX(PATH_BASE_INSTANCE_UNIFORM_BUFFER_IDX))]], \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| $constant Attrs* attrs [[$buffer(0)]], \ |
| VertexTextures _textures, \ |
| VertexStorageBuffers _buffers) \ |
| { \ |
| _instanceID += _baseInstance; \ |
| Varyings _varyings; |
| #else |
| #define VERTEX_MAIN(NAME, Attrs, attrs, _vertexID, _instanceID) \ |
| $__attribute__(($visibility("default"))) Varyings $vertex NAME( \ |
| uint _vertexID [[$vertex_id]], \ |
| uint _instanceID [[$instance_id]], \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| $constant Attrs* attrs [[$buffer(0)]], \ |
| VertexTextures _textures, \ |
| VertexStorageBuffers _buffers) \ |
| { \ |
| Varyings _varyings; |
| #endif |
| |
| #define IMAGE_RECT_VERTEX_MAIN(NAME, Attrs, attrs, _vertexID, _instanceID) \ |
| $__attribute__(($visibility("default"))) Varyings $vertex NAME( \ |
| uint _vertexID [[$vertex_id]], \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| $constant @ImageDrawUniforms& imageDrawUniforms \ |
| [[$buffer(METAL_BUFFER_IDX(IMAGE_DRAW_UNIFORM_BUFFER_IDX))]], \ |
| $constant Attrs* attrs [[$buffer(0)]], \ |
| VertexTextures _textures, \ |
| VertexStorageBuffers _buffers) \ |
| { \ |
| Varyings _varyings; |
| |
| #define IMAGE_MESH_VERTEX_MAIN(NAME, \ |
| PositionAttr, \ |
| position, \ |
| UVAttr, \ |
| uv, \ |
| _vertexID) \ |
| $__attribute__(($visibility("default"))) Varyings $vertex NAME( \ |
| uint _vertexID [[$vertex_id]], \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| $constant @ImageDrawUniforms& imageDrawUniforms \ |
| [[$buffer(METAL_BUFFER_IDX(IMAGE_DRAW_UNIFORM_BUFFER_IDX))]], \ |
| $constant PositionAttr* position [[$buffer(0)]], \ |
| $constant UVAttr* uv [[$buffer(1)]]) \ |
| { \ |
| Varyings _varyings; |
| |
| #define EMIT_VERTEX(POSITION) \ |
| _varyings._pos = POSITION; \ |
| } \ |
| return _varyings; |
| |
| #define FRAG_DATA_MAIN(DATA_TYPE, NAME) \ |
| DATA_TYPE $__attribute__(($visibility("default"))) $fragment NAME( \ |
| Varyings _varyings [[$stage_in]], \ |
| FragmentTextures _textures) \ |
| { |
| |
| #define EMIT_FRAG_DATA(VALUE) \ |
| return VALUE; \ |
| } |
| |
| #define FRAGMENT_CONTEXT_DECL \ |
| , float2 _fragCoord, FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers, DynamicSamplers _dynamicSampler |
| #define FRAGMENT_CONTEXT_UNPACK \ |
| , _fragCoord, _textures, _buffers, _dynamicSampler |
| |
| #define TEXTURE_CONTEXT_DECL , FragmentTextures _textures |
| #define TEXTURE_CONTEXT_FORWARD , _textures |
| |
| #ifdef @PLS_IMPL_DEVICE_BUFFER |
| |
| #define PLS_BLOCK_BEGIN \ |
| struct PLS \ |
| { |
| #ifdef @PLS_IMPL_DEVICE_BUFFER_RASTER_ORDERED |
| // Apple Silicon doesn't support fragment-fragment memory barriers, so on this |
| // hardware we use raster order groups instead. Since the PLS plane indices |
| // collide with other buffer bindings, offset the binding indices of these |
| // buffers by DEFAULT_BINDINGS_SET_SIZE. |
| #define PLS_DECL4F(IDX, NAME) \ |
| $device uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE)), \ |
| $raster_order_group(0)]] |
| #define PLS_DECLUI(IDX, NAME) \ |
| $device uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE)), \ |
| $raster_order_group(0)]] |
| #define PLS_DECLUI_ATOMIC(IDX, NAME) \ |
| $device $atomic_uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE)), \ |
| $raster_order_group(0)]] |
| #else |
| // Since the PLS plane indices collide with other buffer bindings, offset the |
| // binding indices of these buffers by DEFAULT_BINDINGS_SET_SIZE. |
| #define PLS_DECL4F(IDX, NAME) \ |
| $device uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE))]] |
| #define PLS_DECLUI(IDX, NAME) \ |
| $device uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE))]] |
| #define PLS_DECLUI_ATOMIC(IDX, NAME) \ |
| $device $atomic_uint* NAME \ |
| [[$buffer(METAL_BUFFER_IDX(IDX + DEFAULT_BINDINGS_SET_SIZE))]] |
| #endif // @PLS_IMPL_DEVICE_BUFFER_RASTER_ORDERED |
| #define PLS_BLOCK_END \ |
| } \ |
| ; |
| #define PLS_CONTEXT_DECL , PLS _pls, uint _plsIdx |
| #define PLS_CONTEXT_UNPACK , _pls, _plsIdx |
| |
| #define PLS_LOAD4F(PLANE) unpackUnorm4x8(_pls.PLANE[_plsIdx]) |
| #define PLS_LOADUI(PLANE) _pls.PLANE[_plsIdx] |
| #define PLS_LOADUI_ATOMIC(PLANE) \ |
| $atomic_load_explicit(&_pls.PLANE[_plsIdx], \ |
| $memory_order::$memory_order_relaxed) |
| #define PLS_STORE4F(PLANE, VALUE) _pls.PLANE[_plsIdx] = packUnorm4x8(VALUE) |
| #define PLS_STOREUI(PLANE, VALUE) _pls.PLANE[_plsIdx] = (VALUE) |
| #define PLS_STOREUI_ATOMIC(PLANE, VALUE) \ |
| $atomic_store_explicit(&_pls.PLANE[_plsIdx], \ |
| VALUE, \ |
| $memory_order::$memory_order_relaxed) |
| #define PLS_PRESERVE_4F(PLANE) |
| #define PLS_PRESERVE_UI(PLANE) |
| |
| #define PLS_ATOMIC_MAX(PLANE, X) \ |
| $atomic_fetch_max_explicit(&_pls.PLANE[_plsIdx], \ |
| X, \ |
| $memory_order::$memory_order_relaxed) |
| |
| #define PLS_ATOMIC_ADD(PLANE, X) \ |
| $atomic_fetch_add_explicit(&_pls.PLANE[_plsIdx], \ |
| X, \ |
| $memory_order::$memory_order_relaxed) |
| |
| #define PLS_INTERLOCK_BEGIN |
| #define PLS_INTERLOCK_END |
| |
| #define PLS_METAL_MAIN(NAME) \ |
| $__attribute__(($visibility("default"))) $fragment NAME( \ |
| PLS _pls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| FragmentTextures _textures, \ |
| DynamicSamplers _dynamicSampler, \ |
| FragmentStorageBuffers _buffers) \ |
| { \ |
| float2 _fragCoord = _varyings._pos.xy; \ |
| uint2 _plsCoord = uint2($metal::floor(_fragCoord)); \ |
| uint _plsIdx = _plsCoord.y * uniforms.renderTargetWidth + _plsCoord.x; |
| |
| #define PLS_METAL_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| $__attribute__(($visibility("default"))) $fragment NAME( \ |
| PLS _pls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| $constant @ImageDrawUniforms& imageDrawUniforms \ |
| [[$buffer(METAL_BUFFER_IDX(IMAGE_DRAW_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| DynamicSamplers _dynamicSampler, \ |
| FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers) \ |
| { \ |
| float2 _fragCoord = _varyings._pos.xy; \ |
| uint2 _plsCoord = uint2($metal::floor(_fragCoord)); \ |
| uint _plsIdx = _plsCoord.y * uniforms.renderTargetWidth + _plsCoord.x; |
| |
| #define PLS_MAIN(NAME) void PLS_METAL_MAIN(NAME) |
| #define PLS_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| void PLS_METAL_MAIN_WITH_IMAGE_UNIFORMS(NAME) |
| #define EMIT_PLS } |
| |
| #define PLS_FRAG_COLOR_MAIN(NAME) \ |
| half4 PLS_METAL_MAIN(NAME) \ |
| { \ |
| half4 _fragColor; |
| |
| #define PLS_FRAG_COLOR_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| half4 PLS_METAL_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| { \ |
| half4 _fragColor; |
| |
| #define EMIT_PLS_AND_FRAG_COLOR \ |
| } \ |
| return _fragColor; \ |
| EMIT_PLS |
| |
| #else // Default implementation -- framebuffer reads. |
| |
| #define PLS_BLOCK_BEGIN \ |
| struct PLS \ |
| { |
| #define PLS_DECL4F(IDX, NAME) [[$color(IDX)]] half4 NAME |
| #define PLS_DECLUI(IDX, NAME) [[$color(IDX)]] uint NAME |
| #define PLS_DECLUI_ATOMIC PLS_DECLUI |
| #define PLS_BLOCK_END \ |
| } \ |
| ; |
| #define PLS_CONTEXT_DECL , $thread PLS &_inpls, $thread PLS &_pls |
| #define PLS_CONTEXT_UNPACK , _inpls, _pls |
| |
| #define PLS_LOAD4F(PLANE) _inpls.PLANE |
| #define PLS_LOADUI(PLANE) _inpls.PLANE |
| #define PLS_LOADUI_ATOMIC(PLANE) PLS_LOADUI |
| #define PLS_STORE4F(PLANE, VALUE) _pls.PLANE = (VALUE) |
| #define PLS_STOREUI(PLANE, VALUE) _pls.PLANE = (VALUE) |
| #define PLS_STOREUI_ATOMIC(PLANE) PLS_STOREUI |
| #define PLS_PRESERVE_4F(PLANE) _pls.PLANE = _inpls.PLANE |
| #define PLS_PRESERVE_UI(PLANE) _pls.PLANE = _inpls.PLANE |
| |
| INLINE uint pls_atomic_max($thread uint& dst, uint x) |
| { |
| uint originalValue = dst; |
| dst = $metal::max(originalValue, x); |
| return originalValue; |
| } |
| |
| #define PLS_ATOMIC_MAX(PLANE, X) pls_atomic_max(_pls.PLANE, X) |
| |
| INLINE uint pls_atomic_add($thread uint& dst, uint x) |
| { |
| uint originalValue = dst; |
| dst = originalValue + x; |
| return originalValue; |
| } |
| |
| #define PLS_ATOMIC_ADD(PLANE, X) pls_atomic_add(_pls.PLANE, X) |
| |
| #define PLS_INTERLOCK_BEGIN |
| #define PLS_INTERLOCK_END |
| |
| #define PLS_METAL_MAIN(NAME, ...) \ |
| PLS $__attribute__(($visibility("default"))) $fragment NAME($__VA_ARGS__) \ |
| { \ |
| float2 _fragCoord [[$maybe_unused]] = _varyings._pos.xy; \ |
| PLS _pls; |
| |
| #define PLS_MAIN(NAME, ...) \ |
| PLS_METAL_MAIN(NAME, \ |
| PLS _inpls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| DynamicSamplers _dynamicSampler, \ |
| FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers) |
| |
| #define PLS_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| PLS_METAL_MAIN( \ |
| NAME, \ |
| PLS _inpls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers, \ |
| DynamicSamplers _dynamicSampler, \ |
| $constant @ImageDrawUniforms& imageDrawUniforms \ |
| [[$buffer(METAL_BUFFER_IDX(IMAGE_DRAW_UNIFORM_BUFFER_IDX))]]) |
| |
| #define EMIT_PLS \ |
| } \ |
| return _pls; |
| |
| #define PLS_FRAG_COLOR_METAL_MAIN(NAME, ...) \ |
| struct FragmentOut \ |
| { \ |
| half4 _color [[color(0)]]; \ |
| PLS _pls; \ |
| }; \ |
| FragmentOut $__attribute__(($visibility("default"))) $fragment NAME( \ |
| $__VA_ARGS__) \ |
| { \ |
| float2 _fragCoord [[$maybe_unused]] = _varyings._pos.xy; \ |
| half4 _fragColor; \ |
| PLS _pls; |
| |
| #define PLS_FRAG_COLOR_MAIN(NAME) \ |
| PLS_FRAG_COLOR_METAL_MAIN( \ |
| NAME, \ |
| PLS _inpls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers) |
| |
| #define PLS_FRAG_COLOR_MAIN_WITH_IMAGE_UNIFORMS(NAME) \ |
| PLS_FRAG_COLOR_METAL_MAIN( \ |
| NAME, \ |
| PLS _inpls, \ |
| $constant @FlushUniforms& uniforms \ |
| [[$buffer(METAL_BUFFER_IDX(FLUSH_UNIFORM_BUFFER_IDX))]], \ |
| Varyings _varyings [[$stage_in]], \ |
| FragmentTextures _textures, \ |
| FragmentStorageBuffers _buffers, \ |
| $__VA_ARGS__ $constant @ImageDrawUniforms& imageDrawUniforms \ |
| [[$buffer(METAL_BUFFER_IDX(IMAGE_DRAW_UNIFORM_BUFFER_IDX))]]) |
| |
| #define EMIT_PLS_AND_FRAG_COLOR \ |
| } \ |
| return {._color = _fragColor, ._pls = _pls}; |
| |
| #endif // PLS_IMPL_DEVICE_BUFFER |
| |
| #define PLS_DECL4F_READONLY PLS_DECL4F |
| |
| #define discard $discard_fragment() |
| |
| $using $namespace $metal; |
| |
| $template<int N> INLINE $vec<uint, N> floatBitsToUint($vec<float, N> x) |
| { |
| return $as_type<$vec<uint, N>>(x); |
| } |
| |
| $template<int N> INLINE $vec<int, N> floatBitsToInt($vec<float, N> x) |
| { |
| return $as_type<$vec<int, N>>(x); |
| } |
| |
| INLINE uint floatBitsToUint(float x) { return $as_type<uint>(x); } |
| |
| INLINE int floatBitsToInt(float x) { return $as_type<int>(x); } |
| |
| $template<int N> INLINE $vec<float, N> uintBitsToFloat($vec<uint, N> x) |
| { |
| return $as_type<$vec<float, N>>(x); |
| } |
| |
| INLINE float uintBitsToFloat(uint x) { return $as_type<float>(x); } |
| INLINE half2 unpackHalf2x16(uint x) { return $as_type<half2>(x); } |
| INLINE uint packHalf2x16(half2 x) { return $as_type<uint>(x); } |
| INLINE half4 unpackUnorm4x8(uint x) { return $unpack_unorm4x8_to_half(x); } |
| INLINE uint packUnorm4x8(half4 x) { return $pack_half_to_unorm4x8(x); } |
| |
| INLINE float2x2 inverse(float2x2 m) |
| { |
| float2x2 m_ = float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]); |
| float det = (m_[0][0] * m[0][0]) + (m_[0][1] * m[1][0]); |
| return m_ * (1 / det); |
| } |
| |
| INLINE half3 mix(half3 a, half3 b, bool3 c) |
| { |
| half3 result; |
| for (int i = 0; i < 3; ++i) |
| result[i] = c[i] ? b[i] : a[i]; |
| return result; |
| } |
| |
| INLINE float2 mix(float2 a, float2 b, bool2 c) |
| { |
| float2 result; |
| for (int i = 0; i < 2; ++i) |
| result[i] = c[i] ? b[i] : a[i]; |
| return result; |
| } |
| |
| INLINE float2 mix(float2 a, float2 b, float t) { return mix(a, b, float2(t)); } |
| |
| INLINE float mod(float x, float y) { return $fmod(x, y); } |