Search Unity

How StructuredBuffer can be used in shaders for Android?

Discussion in 'Shaders' started by kraihd, May 20, 2018.

  1. kraihd

    kraihd

    Joined:
    Sep 10, 2015
    Posts:
    20
    When I use StructuredBuffer in shaders for Android, they always draw objects purple, and their Shader.isSupported return false.
    My Android device supports OpenGL ES 3.2, and SystemInfo.supportsComputeShaders returns true, though.
    Of course it passes target 4.5. Drawing fails only if the shader contains StructuredBuffer.
    I tried to turn on Requires ES 3.1 check at player settings, which had no effect.
    What is the way to make them work?

    These are the codes:
    Code (CSharp):
    1. Shader "TestShader" {
    2.     Properties{
    3.         _MainTex("Texture", 2D) = "white" {}
    4.     }
    5.  
    6.     Category{
    7.         Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
    8.         Blend SrcAlpha OneMinusSrcAlpha
    9.         Cull Off
    10.         Lighting Off
    11.         ZWrite On
    12.         ZTest On
    13.      
    14.         SubShader{
    15.             Pass{
    16.                 CGPROGRAM
    17.                 #pragma vertex vert
    18.                 #pragma fragment frag
    19.                 #pragma target 4.5
    20.  
    21.                 #include "UnityCG.cginc"
    22.  
    23.                 sampler2D _MainTex;
    24.                 StructuredBuffer<float> _Colors;
    25.  
    26.                 struct appdata_t {
    27.                     float4 vertex : POSITION;
    28.                     float2 texcoord : TEXCOORD0;
    29.                 };
    30.  
    31.                 struct v2f {
    32.                     float4 vertex : SV_POSITION;
    33.                     float2 texcoord : TEXCOORD0;
    34.                     float4 color : TEXCOORD1;
    35.                 };
    36.  
    37.                 v2f vert(appdata_t v) {
    38.                     v2f o;
    39.                     o.vertex = UnityObjectToClipPos(v.vertex);
    40.                     o.texcoord = v.texcoord;
    41.                     o.color = float4(_Colors[0], _Colors[1], _Colors[2], 1);
    42.                     return o;
    43.                 }
    44.  
    45.                 fixed4 frag(v2f i) : SV_Target {
    46.                     float4 color = tex2D(_MainTex, i.texcoord) * i.color;
    47.                     clip(color.a - 0.001);
    48.                     return color;
    49.                 }
    50.  
    51.                 ENDCG
    52.             }
    53.         }
    54.     }
    55. }
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TestComponent : MonoBehaviour {
    4.     ComputeBuffer buffer;
    5.     float[] array;
    6.     float time;
    7.  
    8.     void Start() {
    9.         buffer = new ComputeBuffer(4, sizeof(float));
    10.  
    11.         var mr = GetComponent<MeshRenderer>();
    12.         mr.material.SetBuffer("_Colors", buffer);
    13.  
    14.         array = new float[3];
    15.     }
    16.  
    17.     void Update() {
    18.         time += Time.deltaTime;
    19.         if (time > 0.1f) {
    20.             array[0] = Random.Range(0.5f, 1f);
    21.             array[1] = Random.Range(0.5f, 1f);
    22.             array[2] = Random.Range(0.5f, 1f);
    23.             buffer.SetData(array);
    24.             time -= 0.1f;
    25.         }
    26.     }
    27.  
    28.     void OnDestroy() {
    29.         buffer.Release();
    30.     }
    31.  
    32.     void OnGUI() {
    33.         var mr = GetComponent<MeshRenderer>();
    34.         GUILayout.Label("ComputeShader is supported: " + SystemInfo.supportsComputeShaders.ToString());
    35.         GUILayout.Label("This shader is supported: " + mr.sharedMaterial.shader.isSupported.ToString());
    36.     }
    37. }
     
    Last edited: May 20, 2018
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    Hi!
    Most likely your device doesn't support StructuredBuffers in vertex shaders. It's not a requirement for OpenGL ES 3.1+ to have it.
    Using them in fragment shaders would be fine, though.
     
  3. kraihd

    kraihd

    Joined:
    Sep 10, 2015
    Posts:
    20
    Thanks for the information!
     
  4. DocTatur

    DocTatur

    Joined:
    Aug 13, 2015
    Posts:
    1
    Hello , I have a similar issue and I want to be sure it's not a player setting probleme or else :
    I am working on android note 8, OpenGL ES3.2, I use computebuffers in a shader for DrawMeshInstancedIndirect to draw several colored spheres.
    and I have the following error read in adb shell:
    "GLSL link error: The number of vertex shader storage blocks is greater than the maximum number allowed
    and of course nothing of what I want to show appears on the screen"

    Is it my device which lack computebuffer support or a software pb ?
    What can I do to replace my current solution to adapt for Android devices to draw many spheres without the DrawMeshInstancedIndirect and vertex shader , which works great on pc?
    thanks for you help !
     
  5. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    @DocTatur yes, this is the same thing. Basically, many devices support 0 SSBOs in VS.
     
  6. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    862
    Is there a reliable source of information on which platforms support StructuredBuffer reads in vertex shaders? I'm running into the same issue on Android now.
     
  7. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    jperry_oddgames and cecarlsen like this.
  8. bhavyanshmishra

    bhavyanshmishra

    Joined:
    Dec 12, 2019
    Posts:
    7
    Is it possible to use StructuredBuffer to send data from C# script to a GLSL domain shader by any chance? Maybe as a shader storage buffer?
     
  9. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    @bhavyanshmishra why are you writing GLSL shaders? :)
    Wouldn't it be easier to use HLSL?
     
  10. bhavyanshmishra

    bhavyanshmishra

    Joined:
    Dec 12, 2019
    Posts:
    7
    @aleksandrk Hm, I thought HLSL was mainly for Direct3D applications and I had to use OpenGL. I don't have much experience with HLSL so I'd appreciate any information on that. However, I do already have a shader in GLSL which performs tessellation for me on Unity Linux. I'd open to port over the code to HLSL if that's my only option but is there no way to send arrays of floats or structs to GLSL domain shader from Unity C#, efficiently at regular intervals at a minimum rate of 30 Hz?

    Here is my current GLSL shader:

    Code (CSharp):
    1. Shader "Custom/Tessellation"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _factor("Tessellation scale",Range(1.0,64.0)) = 1.0
    7.     }
    8.     SubShader
    9.     {
    10.         //Cull Off
    11.         Pass
    12.         {
    13.             GLSLPROGRAM
    14.             #version 460  
    15.             uniform float _factor;
    16.             uniform sampler2D _MainTex;
    17.             uniform float regions[1536];
    18.  
    19.             layout (std430, binding=2) buffer shader_data
    20.             {
    21.               vec4 camera_position;
    22.               vec4 light_position;
    23.               vec4 light_diffuse;
    24.             };
    25.          
    26.             #ifdef VERTEX
    27.                 in  vec4 in_POSITION0;
    28.                 void main()
    29.                 {
    30.                     gl_Position =  in_POSITION0;
    31.                 }
    32.             #endif
    33.  
    34.             #ifdef HULL          //GLSL Tessellation Control Shader
    35.  
    36.                 layout (vertices = 4) out;
    37.                 void main()
    38.                 {
    39.                     if (gl_InvocationID == 0)
    40.                     {
    41.                         float tessLevel = 16.0;
    42.                         gl_TessLevelInner[0] = tessLevel;   //Inside tessellation factor
    43.                         gl_TessLevelInner[1] = tessLevel;   //Inside tessellation factor
    44.  
    45.                         gl_TessLevelOuter[0] = tessLevel;   //Edge tessellation factor
    46.                         gl_TessLevelOuter[1] = tessLevel;   //Edge tessellation factor
    47.                         gl_TessLevelOuter[2] = tessLevel;   //Edge tessellation factor
    48.                         gl_TessLevelOuter[3] = tessLevel;   //Edge tessellation factor
    49.                     }
    50.                     gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    51.                 }
    52.             #endif
    53.  
    54.             #ifdef DOMAIN        //GLSL Tessellation Evaluation Shader
    55.                 layout (quads) in;
    56.                 void main()
    57.                 {  
    58.                     vec4 a = gl_in[0].gl_Position;
    59.                     vec4 b = gl_in[1].gl_Position;
    60.                     vec4 c = gl_in[2].gl_Position;
    61.                     vec4 d = gl_in[3].gl_Position;
    62.                     vec4 e = gl_in[4].gl_Position;
    63.                     vec4 f = gl_in[5].gl_Position;
    64.                     vec4 g = gl_in[6].gl_Position;
    65.                     vec4 h = gl_in[7].gl_Position;
    66.  
    67.                     float u = gl_TessCoord.x;
    68.                     float v = gl_TessCoord.y;
    69.  
    70.  
    71.                     // Quad
    72.                     //vec4 p1 = mix(c, e, u);
    73.                     //vec4 p2 = mix(b, f, u);
    74.                     //vec3 n0 = cross(c.xyz-e.xyz,e.xyz-d.xyz);
    75.                     //vec3 n1 = cross(b.xyz-c.xyz,e.xyz-f.xyz);
    76.                     //vec3 n2 = cross(c.xyz-d.xyz,f.xyz-a.xyz);
    77.  
    78.                     // QuadPlane
    79.                     vec4 p1 = mix(b, a, u);
    80.                     vec4 p2 = mix(c, d, u);
    81.                     vec3 n0 = cross(a.xyz-b.xyz,c.xyz-b.xyz);
    82.                     vec3 n1 = cross(b.xyz-c.xyz,e.xyz-f.xyz);
    83.                     vec3 n2 = cross(c.xyz-d.xyz,f.xyz-a.xyz);
    84.  
    85.                     // Plane
    86.                     //vec4 p1 = mix(a, c, u);
    87.                     //vec4 p2 = mix(a, b, u);      
    88.                     //vec3 n0 = cross(a.xyz-b.xyz,b.xyz-c.xyz);          
    89.  
    90.  
    91.                     vec4 normal = vec4(normalize(n0),1);
    92.  
    93.                     float scale = 0.0005;
    94.  
    95.                     float x = u*10 - 5;
    96.                     float y = v*10 - 5;
    97.  
    98.                     vec4 plow = texture(_MainTex, vec2(0,0));
    99.                     vec4 phigh= texture(_MainTex, vec2(62,0));
    100.  
    101.                     float height = scale * (pow(x,3) + pow(y,3));
    102.  
    103.                     vec4 pos = mix(p1, p2, v) + normal*((-0.012))*height;
    104.  
    105.                     gl_Position = gl_ModelViewProjectionMatrix * pos;
    106.                 }
    107.             #endif
    108.  
    109.             #ifdef GEOMETRY      //geometry shader for rendering wireframe
    110.                 layout(triangles) in;
    111.                 layout(line_strip, max_vertices = 3) out;
    112.                 void main()
    113.                 {
    114.                     for(int i = 0; i < gl_in.length(); ++i)
    115.                     {
    116.                         gl_Position = gl_in[i].gl_Position;
    117.                         EmitVertex();
    118.                     }
    119.                     gl_Position = gl_in[0].gl_Position;
    120.                     EmitVertex();
    121.                     EndPrimitive();
    122.                 }  
    123.             #endif
    124.                  
    125.             #ifdef FRAGMENT
    126.                 out vec4 color;
    127.                 void main()
    128.                 {
    129.                     color = vec4(1,1,1,1);
    130.                 }
    131.             #endif
    132.          
    133.             ENDGLSL
    134.             }
    135.     }
    136. }
     
    Last edited: Mar 23, 2020
  11. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    @bhavyanshmishra check https://docs.unity3d.com/Manual/SL-ShadingLanguage.html, this covers the shading language used in Unity pretty well. Given shader source in this language, Unity translates it to whatever platform you're using.

    You can, of course, write shaders in GLSL, but that's limiting you to those platforms that have OpenGL support, and makes other interactions (like passing StructuredBuffers there) awkward, as this is a very infrequently used functionality.
     
  12. bhavyanshmishra

    bhavyanshmishra

    Joined:
    Dec 12, 2019
    Posts:
    7
    @aleksandrk Ahh, that made perfect sense after your comments and once I read up a little more about Unity's way of using Shading Languages. So, then I went ahead and ported over my GLSL shader to HLSL which almost perfectly replicates my GLSL shader. I've tested it thoroughly. Now, though using HLSL, how would I use a ComputeBuffer as StructuredBuffer in normal shaders like domain, hull and geometry shaders to send float array to the shader at more than 30 Hz? I really appreciate your comments. I've learned a lot from them!

    Code (CSharp):
    1.  
    2. Shader "Custom/QuadTessellationHLSL"
    3. {
    4.     SubShader
    5.     {
    6.         Pass
    7.         {
    8.             Cull Off
    9.             CGPROGRAM
    10.             #pragma vertex TessellationVertexProgram
    11.             #pragma hull HullProgram
    12.             #pragma domain DomainProgram
    13.             #pragma fragment FragmentProgram
    14.             #pragma target 4.6
    15. // ----------------------------------------------------------------
    16.  
    17.             struct appdata {
    18.                 float4 vertex : POSITION;
    19.                 float2 uv : TEXCOORD0;
    20.             };
    21.             struct ControlPoint
    22.             {
    23.                 float4 vertex : INTERNALTESSPOS;
    24.                 float2 uv : TEXCOORD0;
    25.             };
    26.             struct hsConstOut
    27.             {
    28.                 float Edges[4] : SV_TessFactor;
    29.                 float Inside[2] : SV_InsideTessFactor;
    30.             };
    31.             struct v2f
    32.             {
    33.                 float4 vertex : SV_POSITION;
    34.                 float2 uv : TEXCOORD0;
    35.             };
    36.  
    37.             StructuredBuffer<float> params;
    38. // ---------------------------------------------------------------
    39.  
    40.  
    41.             v2f VertexProgram(appdata v) // Not the primary vertex program.
    42.             {
    43.                 v2f o;
    44.                 o.vertex = UnityObjectToClipPos(v.vertex);
    45.                 o.uv = v.uv;
    46.                 return o;
    47.             }
    48.  
    49.             ControlPoint TessellationVertexProgram(appdata v){
    50.                 ControlPoint p;
    51.                 p.vertex = v.vertex;
    52.                 p.uv = v.uv;
    53.                 return p;          
    54.             };
    55.          
    56.             hsConstOut hull_constant_function(InputPatch<ControlPoint, 4> patch)
    57.             {
    58.                 hsConstOut output;
    59.                 output.Edges[0] = output.Edges[1] = output.Edges[2] = output.Edges[3] = output.Inside[0] = output.Inside[1] = 16;
    60.                 return output;
    61.             }
    62.                 [domain("quad")]
    63.                 [partitioning("integer")]
    64.                 [outputtopology("triangle_cw")]
    65.                 [outputcontrolpoints(4)]
    66.                 [patchconstantfunc("hull_constant_function")]          
    67.             ControlPoint HullProgram(InputPatch<ControlPoint, 4>patch, uint id: SV_OutputControlPointID)
    68.             {
    69.                 return patch[id];
    70.             }
    71.                 [domain("quad")]
    72.             v2f DomainProgram(hsConstOut factors,
    73.                                 const OutputPatch<ControlPoint, 4>patch,
    74.                                 float2 UV:SV_DomainLocation)
    75.             {
    76.                 float4 a = patch[0].vertex;
    77.                 float4 b = patch[1].vertex;
    78.                 float4 c = patch[2].vertex;
    79.                 float4 d = patch[3].vertex;
    80.  
    81.                 float4 v0 = lerp(a,b,UV.x);
    82.                 float4 v1 = lerp(d,c,UV.x);
    83.                 float4 vFinal = lerp(v0,v1,UV.y);
    84.  
    85.                 float2 uv0 = lerp(a,b,UV.x);
    86.                 float2 uv1 = lerp(d,c,UV.x);
    87.                 float2 uvFinal = lerp(uv0,uv1,UV.y);
    88.  
    89.                 float3 n0 = cross(a.xyz-b.xyz,a.xyz-d.xyz);
    90.                 float4 normal = float4(normalize(n0),1);
    91.  
    92.                 float scale = 0.000005;
    93.                 float x = UV.x*10 - 5;
    94.                 float y = UV.y*10 - 5;
    95.                 //float height = scale * (pow(x,3) + pow(y,3));
    96.  
    97.                 int i = int(a.x);
    98.                 int j = int(a.y);
    99.                 float height = params[i*16+j];
    100.  
    101.                 appdata data;
    102.                    data.vertex = vFinal + height * normal;
    103.                    data.uv = uvFinal;
    104.  
    105.                 return VertexProgram(data);
    106.             }
    107.  
    108.  
    109.             fixed4 FragmentProgram(v2f i) : SV_TARGET
    110.             {
    111.                 //float4 col = tex2D(_MainTex, i.uv);
    112.                 fixed4 col = fixed4(0.5,0.8,0.4,1.0);
    113.                 return col;
    114.             }
    115.             ENDCG
    116.         }
    117.     }
    118. }
    On the C# side I create the ComputeBuffer and assign it to the material buffer using SetBuffer("params", cb_params). However, I feel that the params buffer in the shader is still set to zeros.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. using map_sense = RosSharp.RosBridgeClient.MessageTypes.MapSense;
    6. using sensor = RosSharp.RosBridgeClient.MessageTypes.Sensor;
    7. using RosSharp.RosBridgeClient;
    8.  
    9. [RequireComponent(typeof(RosConnector))]
    10. public class PlanarRegionSubscriber : MonoBehaviour
    11. {
    12.     private RosConnector rosConnector;
    13.     private Texture2D dtex;
    14.     //private GameObject pointMap;
    15.     public MeshRenderer renderer;
    16.  
    17.     private float[] paramData;
    18.     public ComputeBuffer cb_params;
    19.  
    20.  
    21.  
    22.     // Start is called before the first frame update
    23.     void Start()
    24.     {
    25.         rosConnector = GetComponent<RosConnector>();
    26.         string subscription_id = rosConnector.RosSocket.Subscribe<map_sense.PlanarRegions>("/map/regions", RegionMsgHandler);
    27.      
    28.         cb_params = new ComputeBuffer(192, sizeof(float));
    29.         renderer = GameObject.FindWithTag("QuadMap").GetComponent<MeshRenderer>();
    30.         // cb_params.Release();
    31.         Debug.Log("Subscribed:"+subscription_id);
    32.                 renderer.material.SetBuffer("params", cb_params);
    33.  
    34.         paramData = new float[192];
    35.         for(int i = 0; i<192; i++){
    36.             paramData[i] = (float)i;
    37.         }
    38.         cb_params.SetData(paramData);
    39.     }
    40.  
    41.     private void RegionMsgHandler(map_sense.PlanarRegions message)
    42.     {
    43.         Debug.Log(message.data.Length);
    44.         renderer.material.SetBuffer("params", cb_params);
    45.         for(int i = 0; i<192; i++){
    46.             paramData[i] = (float)i;
    47.         }
    48.         cb_params.SetData(paramData);
    49.  
    50.         // ImageConversion.LoadImage(dtex, msgData);
    51.         // renderer.material.SetTexture("_MainTex", dtex);
    52.  
    53.     }
    54.  
    55.     // void OnDestroy() {
    56.     //     cb_params.Release();
    57.     // }
    58.  
    59.  
    60. }
    61.  
     
    Last edited: Mar 27, 2020
  13. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    @bhavyanshmishra usually compute buffers are filled from compute shaders, to bypass a copy between CPU visible memory and GPU visible memory. This speeds up things quite a bit :)
    To debug this, I would fist make sure a simpler case works, and then write a more complex shader.
     
  14. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    It works on those APIs as well. Please check if you're using it correctly.