Hello community!
I’ve coded simple shader for overlay blend mode for ui sprites. I have here apple iPad 3 device with A7 CPU. Looks like it is the oldest device with Metal support and the weakest one our app must support. I have coded 3 subshaders. First for devices with advanced blending support, where I use macro
UNITY_REQUIRE_ADVANCED_BLEND(Overlay);
Second is for devices supporting framebuffer fetch, it has
#pragma require framebufferfetch
inside CGPROGRAM
and the last one using GrabPass technique if nothing supported.
If I build for metal on that iPad 3, it always uses subshader #0 (frame debugger says so), but the device do not support advanced blending and the picture is wrong, without any blend, as it was UI/Default shaded. If I build for opengl 3 it chooses the correct subshader, but the goal is to support metal.
What am I missing? How can I make Unity to choose correct subshader for this device? Here’s full code I’m playing with.
Shader "BlendModes/Overlay"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Tint("Tint", Color) = ( 1, 1, 1, 1 )
_StencilComp ("Stencil Comparison", Float) = 8 // Always
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 // Disable
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
}
CGINCLUDE
#include "UnityCG.cginc"
float compOverlay(float src, float dst) {
if (dst < 0.5) {
return 2.0 * src * dst;
}
return 1.0 - 2.0 * (1.0 - src) * (1.0 - dst);
}
float3 overlay(float3 src, float3 dst) {
float3 mix;
mix.r = compOverlay(src.r, dst.r);
mix.g = compOverlay(src.g, dst.g);
mix.b = compOverlay(src.b, dst.b);
return mix;
}
struct appdata {
float4 vertex : POSITION;
float4 color : COLOR;
half4 uv0 : TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float4 color : COLOR;
half4 uv0 : TEXCOORD0;
};
struct v2f_grab {
float4 vertex : POSITION;
fixed4 color : COLOR;
half4 uv0 : TEXCOORD0;
half4 grabPos : TEXCOORD1;
};
sampler2D _MainTex;
fixed4 _Tint;
v2f vert (appdata vertexInput) {
v2f vertexOutput;
vertexOutput.vertex = UnityObjectToClipPos(vertexInput.vertex);
vertexOutput.uv0 = vertexInput.uv0;
vertexOutput.color = vertexInput.color;
return vertexOutput;
}
v2f_grab vert_grab (appdata vertexInput) {
v2f_grab vertexOutput;
vertexOutput.vertex = UnityObjectToClipPos(vertexInput.vertex);
vertexOutput.grabPos = ComputeGrabScreenPos(vertexOutput.vertex);
vertexOutput.uv0 = vertexInput.uv0;
vertexOutput.color = vertexInput.color;
return vertexOutput;
}
fixed4 frag (v2f vertexOutput) : SV_Target {
float4 src = tex2D(_MainTex, vertexOutput.uv0);
src.rgb = src.rgb * vertexOutput.color.rgb * _Tint.rgb;
src.a = src.a * vertexOutput.color.a * _Tint.a;
return src;
}
void frag_fetch(v2f vertexOutput, inout float4 dst : SV_Target) {
float4 src = tex2D(_MainTex, vertexOutput.uv0);
src.rgb = src.rgb * vertexOutput.color.rgb * _Tint.rgb;
src.a = src.a * vertexOutput.color.a * _Tint.a;
dst.rgb = overlay(src, dst);
dst.a = src.a;
}
ENDCG
SubShader // Advanced Blending
{
Tags
{
"IgnoreProjector" = "True"
"Queue" = "Transparent"
"RenderType" = "Transparent"
"ForceNoShadowCasting" = "True"
"PreviewType" = "Plane"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
ZWrite Off
ZTest Off
Lighting Off
Cull Off
Fog { Mode Off }
Pass {
BlendOp Overlay
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
UNITY_REQUIRE_ADVANCED_BLEND(Overlay);
ENDCG
}
}
SubShader // Framebuffer Fetch
{
Tags {
"IgnoreProjector" = "True"
"Queue" = "Transparent"
"RenderType" = "Transparent"
"ForceNoShadowCasting" = "True"
"PreviewType" = "Plane"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
ZWrite Off
ZTest Off
Lighting Off
Cull Off
Fog { Mode Off }
Pass {
BlendOp Add
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma require framebufferfetch
#pragma vertex vert
#pragma fragment frag_fetch
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
SubShader // Grab Pass
{
Tags {
"IgnoreProjector" = "True"
"Queue" = "Transparent"
"RenderType" = "Transparent"
"ForceNoShadowCasting" = "True"
"PreviewType" = "Plane"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
ZWrite Off
ZTest Off
Lighting Off
Cull Off
Fog { Mode Off }
GrabPass { }
Pass {
BlendOp Add
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert_grab
#pragma fragment frag_grab
#pragma fragmentoption ARB_precision_hint_fastest
sampler2D _GrabTexture;
float4 frag_grab(v2f_grab vertexOutput) : SV_Target {
float4 src = tex2D(_MainTex, vertexOutput.uv0);
float4 dst = tex2Dproj(_GrabTexture, vertexOutput.grabPos);
src.rgb = overlay(src, dst);
src.a = src.a * _Tint.a * vertexOutput.color.a;
return src;
}
ENDCG
}
}
Fallback "UI/Default"
}