Atlas - SDL_shaders_metal.metal
Home / ext / SDL / src / render / metal Lines: 1 | Size: 12181 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1#include <metal_common> 2#include <metal_texture> 3#include <metal_matrix> 4#include <metal_stdlib> 5 6using namespace metal; 7 8// These should mirror the definitions in SDL_render_metal.m 9#define TONEMAP_NONE 0 10#define TONEMAP_LINEAR 1 11#define TONEMAP_CHROME 2 12 13#define TEXTURETYPE_NONE 0 14#define TEXTURETYPE_RGB 1 15#define TEXTURETYPE_RGB_PIXELART 2 16#define TEXTURETYPE_PALETTE_NEAREST 3 17#define TEXTURETYPE_PALETTE_LINEAR 4 18#define TEXTURETYPE_PALETTE_PIXELART 5 19#define TEXTURETYPE_NV12 6 20#define TEXTURETYPE_NV21 7 21#define TEXTURETYPE_YUV 8 22 23#define INPUTTYPE_UNSPECIFIED 0 24#define INPUTTYPE_SRGB 1 25#define INPUTTYPE_SCRGB 2 26#define INPUTTYPE_HDR10 3 27 28struct ShaderConstants 29{ 30 float scRGB_output; 31 float texture_type; 32 float input_type; 33 float color_scale; 34 float4 texel_size; 35 36 float tonemap_method; 37 float tonemap_factor1; 38 float tonemap_factor2; 39 float sdr_white_point; 40}; 41 42struct YUVDecode 43{ 44 float3 offset; 45 float3x3 matrix; 46}; 47 48float sRGBtoLinear(float v) 49{ 50 if (v <= 0.04045) { 51 v = (v / 12.92); 52 } else { 53 v = pow(abs(v + 0.055) / 1.055, 2.4); 54 } 55 return v; 56} 57 58float sRGBfromLinear(float v) 59{ 60 if (v <= 0.0031308) { 61 v = (v * 12.92); 62 } else { 63 v = (pow(abs(v), 1.0 / 2.4) * 1.055 - 0.055); 64 } 65 return v; 66} 67 68float3 PQtoLinear(float3 v, float sdr_white_point) 69{ 70 const float c1 = 0.8359375; 71 const float c2 = 18.8515625; 72 const float c3 = 18.6875; 73 const float oo_m1 = 1.0 / 0.1593017578125; 74 const float oo_m2 = 1.0 / 78.84375; 75 76 float3 num = max(pow(abs(v), oo_m2) - c1, 0.0); 77 float3 den = c2 - c3 * pow(abs(v), oo_m2); 78 return (10000.0 * pow(abs(num / den), oo_m1) / sdr_white_point); 79} 80 81float3 ApplyTonemap(float3 v, float input_type, float tonemap_method, float tonemap_factor1, float tonemap_factor2) 82{ 83 const float3x3 mat709to2020 = { 84 { 0.627404, 0.329283, 0.043313 }, 85 { 0.069097, 0.919541, 0.011362 }, 86 { 0.016391, 0.088013, 0.895595 } 87 }; 88 const float3x3 mat2020to709 = { 89 { 1.660496, -0.587656, -0.072840 }, 90 { -0.124547, 1.132895, -0.008348 }, 91 { -0.018154, -0.100597, 1.118751 } 92 }; 93 94 if (tonemap_method == TONEMAP_LINEAR) { 95 v *= tonemap_factor1; 96 } else if (tonemap_method == TONEMAP_CHROME) { 97 if (input_type == INPUTTYPE_SCRGB) { 98 // Convert to BT.2020 colorspace for tone mapping 99 v = v * mat709to2020; 100 } 101 102 float vmax = max(v.r, max(v.g, v.b)); 103 if (vmax > 0.0) { 104 float scale = (1.0 + tonemap_factor1 * vmax) / (1.0 + tonemap_factor2 * vmax); 105 v *= scale; 106 } 107 108 if (input_type == INPUTTYPE_SCRGB) { 109 // Convert to BT.709 colorspace after tone mapping 110 v = v * mat2020to709; 111 } 112 } 113 return v; 114} 115 116float4 SamplePaletteNearest(texture2d<float> tex0, texture2d<float> tex1, sampler s0, sampler s1, float2 uv) 117{ 118 float index = tex0.sample(s0, uv).r * 255; 119 return tex1.sample(s1, float2((index + 0.5) / 256, 0.5)); 120} 121 122// Implementation with thanks from bgolus: 123// https://discussions.unity.com/t/how-to-make-data-shader-support-bilinear-trilinear/598639/8 124float4 SamplePaletteLinear(texture2d<float> tex0, texture2d<float> tex1, sampler s0, sampler s1, float2 uv, float4 texel_size) 125{ 126 // scale & offset uvs to integer values at texel centers 127 float2 uv_texels = uv * texel_size.zw + 0.5; 128 129 // get uvs for the center of the 4 surrounding texels by flooring 130 float4 uv_min_max = float4((floor(uv_texels) - 0.5) * texel_size.xy, (floor(uv_texels) + 0.5) * texel_size.xy); 131 132 // blend factor 133 float2 uv_frac = fract(uv_texels); 134 135 // sample all 4 texels 136 float4 texelA = SamplePaletteNearest(tex0, tex1, s0, s1, uv_min_max.xy); 137 float4 texelB = SamplePaletteNearest(tex0, tex1, s0, s1, uv_min_max.xw); 138 float4 texelC = SamplePaletteNearest(tex0, tex1, s0, s1, uv_min_max.zy); 139 float4 texelD = SamplePaletteNearest(tex0, tex1, s0, s1, uv_min_max.zw); 140 141 // bilinear interpolation 142 return mix(mix(texelA, texelB, uv_frac.y), mix(texelC, texelD, uv_frac.y), uv_frac.x); 143} 144 145float2 GetPixelArtUV(float2 uv, float4 texel_size) 146{ 147 // box filter size in texel units 148 float2 boxSize = clamp(fwidth(uv) * texel_size.zw, 1e-5, 1); 149 150 // scale uv by texture size to get texel coordinate 151 float2 tx = uv * texel_size.zw - 0.5 * boxSize; 152 153 // compute offset for pixel-sized box filter 154 float2 txOffset = smoothstep(1 - boxSize, 1, fract(tx)); 155 156 // compute bilinear sample uv coordinates 157 return (floor(tx) + 0.5 + txOffset) * texel_size.xy; 158} 159 160float4 GetOutputColorSimple(float4 rgba, float color_scale) 161{ 162 float4 output; 163 164 output.rgb = rgba.rgb * color_scale; 165 output.a = rgba.a; 166 167 return output; 168} 169 170float3 GetOutputColorFromSRGB(float3 rgb, float scRGB_output, float color_scale) 171{ 172 float3 output; 173 174 if (scRGB_output) { 175 rgb.r = sRGBtoLinear(rgb.r); 176 rgb.g = sRGBtoLinear(rgb.g); 177 rgb.b = sRGBtoLinear(rgb.b); 178 } 179 180 output = rgb * color_scale; 181 182 return output; 183} 184 185float3 GetOutputColorFromLinear(float3 rgb, float scRGB_output, float color_scale) 186{ 187 float3 output; 188 189 output = rgb * color_scale; 190 191 if (!scRGB_output) { 192 output.r = sRGBfromLinear(output.r); 193 output.g = sRGBfromLinear(output.g); 194 output.b = sRGBfromLinear(output.b); 195 output = clamp(output, 0.0, 1.0); 196 } 197 198 return output; 199} 200 201float4 GetOutputColor(float4 rgba, constant ShaderConstants &c) 202{ 203 const float3x3 mat2020to709 = { 204 { 1.660496, -0.587656, -0.072840 }, 205 { -0.124547, 1.132895, -0.008348 }, 206 { -0.018154, -0.100597, 1.118751 } 207 }; 208 float4 output; 209 210 if (c.input_type == INPUTTYPE_HDR10) { 211 rgba.rgb = PQtoLinear(rgba.rgb, c.sdr_white_point); 212 } 213 214 if (c.tonemap_method != TONEMAP_NONE) { 215 rgba.rgb = ApplyTonemap(rgba.rgb, c.input_type, c.tonemap_method, c.tonemap_factor1, c.tonemap_factor2); 216 } 217 218 if (c.input_type == INPUTTYPE_SRGB) { 219 if (c.texture_type == TEXTURETYPE_RGB) { 220 // The sampler has already converted to linear if necessary 221 output.rgb = rgba.rgb * c.color_scale; 222 } else { 223 output.rgb = GetOutputColorFromSRGB(rgba.rgb, c.scRGB_output, c.color_scale); 224 } 225 } else if (c.input_type == INPUTTYPE_SCRGB) { 226 output.rgb = GetOutputColorFromLinear(rgba.rgb, c.scRGB_output, c.color_scale); 227 } else if (c.input_type == INPUTTYPE_HDR10) { 228 rgba.rgb = rgba.rgb * mat2020to709; 229 output.rgb = GetOutputColorFromLinear(rgba.rgb, c.scRGB_output, c.color_scale); 230 } else { 231 // Unexpected input type, use magenta error color 232 output.rgb = float3(1.0, 0.0, 1.0); 233 } 234 output.a = rgba.a; 235 236 return output; 237} 238 239struct SolidVertexInput 240{ 241 float2 position [[attribute(0)]]; 242 float4 color [[attribute(1)]]; 243}; 244 245struct SolidVertexOutput 246{ 247 float4 position [[position]]; 248 float4 color; 249 float pointSize [[point_size]]; 250}; 251 252vertex SolidVertexOutput SDL_Solid_vertex(SolidVertexInput in [[stage_in]], 253 constant float4x4 &projection [[buffer(2)]], 254 constant float4x4 &transform [[buffer(3)]]) 255{ 256 SolidVertexOutput v; 257 v.position = (projection * transform) * float4(in.position, 0.0f, 1.0f); 258 v.color = in.color; 259 v.pointSize = 1.0f; 260 return v; 261} 262 263fragment float4 SDL_Solid_fragment(SolidVertexInput in [[stage_in]], 264 constant ShaderConstants &c [[buffer(0)]]) 265{ 266 return GetOutputColorSimple(1.0, c.color_scale) * in.color; 267} 268 269struct CopyVertexInput 270{ 271 float2 position [[attribute(0)]]; 272 float4 color [[attribute(1)]]; 273 float2 texcoord [[attribute(2)]]; 274}; 275 276struct CopyVertexOutput 277{ 278 float4 position [[position]]; 279 float4 color; 280 float2 texcoord; 281}; 282 283vertex CopyVertexOutput SDL_Copy_vertex(CopyVertexInput in [[stage_in]], 284 constant float4x4 &projection [[buffer(2)]], 285 constant float4x4 &transform [[buffer(3)]]) 286{ 287 CopyVertexOutput v; 288 v.position = (projection * transform) * float4(in.position, 0.0f, 1.0f); 289 v.color = in.color; 290 v.texcoord = in.texcoord; 291 return v; 292} 293 294fragment float4 SDL_Palette_fragment(CopyVertexOutput vert [[stage_in]], 295 constant ShaderConstants &c [[buffer(0)]], 296 texture2d<float> tex0 [[texture(0)]], 297 texture2d<float> tex1 [[texture(1)]], 298 sampler s0 [[sampler(0)]], 299 sampler s1 [[sampler(1)]]) 300{ 301 float4 rgba; 302 303 if (c.texture_type == TEXTURETYPE_PALETTE_NEAREST) { 304 rgba = SamplePaletteNearest(tex0, tex1, s0, s1, vert.texcoord); 305 } else if (c.texture_type == TEXTURETYPE_PALETTE_LINEAR) { 306 rgba = SamplePaletteLinear(tex0, tex1, s0, s1, vert.texcoord, c.texel_size); 307 } else if (c.texture_type == TEXTURETYPE_PALETTE_PIXELART) { 308 float2 uv = GetPixelArtUV(vert.texcoord, c.texel_size); 309 rgba = SamplePaletteLinear(tex0, tex1, s0, s1, uv, c.texel_size); 310 } else { 311 // Unexpected texture type, use magenta error color 312 rgba = float4(1.0, 0.0, 1.0, 1.0); 313 } 314 return GetOutputColor(rgba, c) * vert.color; 315} 316 317fragment float4 SDL_Copy_fragment(CopyVertexOutput vert [[stage_in]], 318 constant ShaderConstants &c [[buffer(0)]], 319 texture2d<float> tex [[texture(0)]], 320 sampler s [[sampler(0)]]) 321{ 322 float4 rgba; 323 324 if (c.texture_type == TEXTURETYPE_RGB) { 325 rgba = tex.sample(s, vert.texcoord); 326 } else if (c.texture_type == TEXTURETYPE_RGB_PIXELART) { 327 float2 uv = GetPixelArtUV(vert.texcoord, c.texel_size); 328 rgba = tex.sample(s, uv, gradient2d(dfdx(vert.texcoord), dfdy(vert.texcoord))); 329 } else { 330 // Unexpected texture type, use magenta error color 331 rgba = float4(1.0, 0.0, 1.0, 1.0); 332 } 333 return GetOutputColor(rgba, c) * vert.color; 334} 335 336fragment float4 SDL_YUV_fragment(CopyVertexOutput vert [[stage_in]], 337 constant ShaderConstants &c [[buffer(0)]], 338 constant YUVDecode &decode [[buffer(1)]], 339 texture2d<float> texY [[texture(0)]], 340 texture2d_array<float> texUV [[texture(1)]], 341 sampler s [[sampler(0)]]) 342{ 343 float3 yuv; 344 yuv.x = texY.sample(s, vert.texcoord).r; 345 yuv.y = texUV.sample(s, vert.texcoord, 0).r; 346 yuv.z = texUV.sample(s, vert.texcoord, 1).r; 347 348 float4 rgba; 349 rgba.rgb = (yuv + decode.offset) * decode.matrix; 350 rgba.a = 1.0; 351 352 return GetOutputColor(rgba, c) * vert.color; 353} 354 355fragment float4 SDL_NV12_fragment(CopyVertexOutput vert [[stage_in]], 356 constant ShaderConstants &c [[buffer(0)]], 357 constant YUVDecode &decode [[buffer(1)]], 358 texture2d<float> texY [[texture(0)]], 359 texture2d<float> texUV [[texture(1)]], 360 sampler s [[sampler(0)]]) 361{ 362 float4 rgba; 363 if (c.texture_type == TEXTURETYPE_NV12) { 364 float3 yuv; 365 yuv.x = texY.sample(s, vert.texcoord).r; 366 yuv.yz = texUV.sample(s, vert.texcoord).rg; 367 368 rgba.rgb = (yuv + decode.offset) * decode.matrix; 369 } else if (c.texture_type == TEXTURETYPE_NV21) { 370 float3 yuv; 371 yuv.x = texY.sample(s, vert.texcoord).r; 372 yuv.yz = texUV.sample(s, vert.texcoord).gr; 373 374 rgba.rgb = (yuv + decode.offset) * decode.matrix; 375 } else { 376 // Unexpected texture type, use magenta error color 377 rgba.rgb = float3(1.0, 0.0, 1.0); 378 } 379 rgba.a = 1.0; 380 381 return GetOutputColor(rgba, c) * vert.color; 382} 383[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.