Search Unity

Draw Mesh Instanced Indirect does not behave the same way on different versions of the same device

Discussion in 'Android' started by unity_17victork, Dec 6, 2021.

  1. unity_17victork

    unity_17victork

    Joined:
    Nov 24, 2021
    Posts:
    3
    Hello,
    I am attempting to use DrawMeshInstancedIndirect to render objects, and I got it to work the way I want it in editor, showing all of the objects in places I need it to.

    When I built it for my android device Samsung s21 5g (model: SM-G991U1) and it worked just like in the editor.
    However, when handing this to a tester it does not display anything where the instanced meshes are supposed to appear. There are no error logs and no warnings shown on device. This happens, despite them also using Samsung s21 5g but of a European model (model: SM-G991B ).

    What could this be caused by?

    I will paste the code I use below

    This is DMIIRendererManager.cs which is meant to just abstract making something use DrawMeshInstancedIndirect. It also extends SingletonBehaviour class which just allows me to call its instance from anywhere and should not be important for the DMII code.
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Sirenix.OdinInspector;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.Rendering;
    6.  
    7. public class DMIIPayload
    8. {
    9.     public List<DMIIRenderer> childRenderers = new List<DMIIRenderer>();
    10.     public Mesh instanceMesh;
    11.     public int subMeshIndex = 0;
    12.     public Material instanceMaterial;
    13.     public ComputeBuffer positionBuffer;
    14.     public ComputeBuffer texturePositionBuffer;
    15.     public ComputeBuffer visualSettingsBuffer;
    16.     public ComputeBuffer argsBuffer;
    17.     private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    18.  
    19.     private const string POSITION_BUFFER_ID = "positionBuffer";
    20.     private const string TEXTURE_POSITION_BUFFER_ID = "texturePositionBuffer";
    21.     private const string VISUAL_SETTINGS_BUFFER_ID = "visualSettingsBuffer";
    22.  
    23.     public int instanceCount
    24.     {
    25.         get { return childRenderers.Count; }
    26.     }
    27.  
    28.     public DMIIPayload()
    29.     {
    30.         argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
    31.     }
    32.  
    33.     public void UpdatePositionBuffer()
    34.     {
    35.         // Positions
    36.         //clear the previously set position
    37.         if (positionBuffer != null) positionBuffer.Release();
    38.         positionBuffer = new ComputeBuffer(instanceCount, sizeof(float) * 4); // instanceCount is how many entries will be given, sizeof(float) * 3 is the size of each entry in bytes
    39.         Vector4[] positions = new Vector4[instanceCount];
    40.         for (int i = 0; i < instanceCount; i++)
    41.         {
    42.             positions[i] = new Vector4(childRenderers[i].parentTransform.position.x, childRenderers[i].parentTransform.position.y, childRenderers[i].parentTransform.position.z, 1);
    43.         }
    44.         positionBuffer.SetData(positions);
    45.         instanceMaterial.SetBuffer(POSITION_BUFFER_ID, positionBuffer);
    46.     }
    47.  
    48.     public void UpdateTexturePositionBuffer()
    49.     {
    50.         if (texturePositionBuffer != null) texturePositionBuffer.Release();
    51.         texturePositionBuffer = new ComputeBuffer(instanceCount, sizeof(float) * 4);
    52.         Vector4[] newUVSettings = new Vector4[instanceCount];
    53.         for (int i = 0; i < instanceCount; i++)
    54.         {
    55.             Vector4 newUV = childRenderers[i].fRenderer.uvCoordinates;
    56.             if (newUV == Vector4.zero)
    57.             {
    58.                 newUV.x = 1;
    59.                 newUV.y = 1;
    60.             }
    61.             newUVSettings[i] = newUV;
    62.         }
    63.         texturePositionBuffer.SetData(newUVSettings);
    64.  
    65.         instanceMaterial.SetBuffer(TEXTURE_POSITION_BUFFER_ID, texturePositionBuffer);
    66.     }
    67.  
    68.     public void UpdateVisualSettingsBuffer()
    69.     {
    70.         if (visualSettingsBuffer != null) visualSettingsBuffer.Release();
    71.         visualSettingsBuffer = new ComputeBuffer(instanceCount, sizeof(float) * 4);
    72.  
    73.         Vector4[] visualSettings = new Vector4[instanceCount];
    74.  
    75.         for (int i = 0; i < instanceCount; i++)
    76.         {
    77.             visualSettings[i] =
    78.                 new Vector4(childRenderers[i].brightness,
    79.                     childRenderers[i].contrast,
    80.                     childRenderers[i].saturation,
    81.                     childRenderers[i].value); // will contain <brightness, contrast, saturation, value> in that alphabetical order
    82.         }
    83.  
    84.         visualSettingsBuffer.SetData(visualSettings);
    85.  
    86.         instanceMaterial.SetBuffer(VISUAL_SETTINGS_BUFFER_ID, visualSettingsBuffer);
    87.     }
    88.  
    89.     public void UpdateBuffers()
    90.     {
    91.         // Ensure submesh index is in range
    92.         if (instanceMesh != null) subMeshIndex = Mathf.Clamp(subMeshIndex, 0, instanceMesh.subMeshCount - 1);
    93.  
    94.         UpdateTexturePositionBuffer();
    95.         UpdateVisualSettingsBuffer();
    96.         UpdatePositionBuffer();
    97.  
    98.         // Indirect args
    99.         if (instanceMesh != null)
    100.         {
    101.             args[0] = (uint)instanceMesh.GetIndexCount(subMeshIndex);
    102.             args[1] = (uint)instanceCount;
    103.             args[2] = (uint)instanceMesh.GetIndexStart(subMeshIndex);
    104.             args[3] = (uint)instanceMesh.GetBaseVertex(subMeshIndex);
    105.         }
    106.         else
    107.         {
    108.             args[0] = args[1] = args[2] = args[3] = 0;
    109.         }
    110.         argsBuffer.SetData(args);
    111.     }
    112.  
    113.     public void DrawMeshsInstancedIndirect()
    114.     {
    115.         Mesh _mesh = instanceMesh;
    116.         Material _material = instanceMaterial;
    117.         int _subMeshIndex = subMeshIndex;
    118.         Bounds _bounds = new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f));
    119.         ComputeBuffer _argsBuffer = argsBuffer;
    120.         int _argsOffset = 0;
    121.         MaterialPropertyBlock _materialPropertyBlock = null;
    122.         ShadowCastingMode _shadowCastingMode = ShadowCastingMode.On;
    123.         bool _receiveShadows = true;
    124.         int _layer = 0;
    125. #if UNITY_EDITOR
    126.         var sceneCameras = SceneView.GetAllSceneCameras();
    127.         if (sceneCameras.Length > 0)
    128.         {
    129.             // allows us to render to the scene camera
    130.             Graphics.DrawMeshInstancedIndirect(_mesh,
    131.                 _subMeshIndex,
    132.                 _material,
    133.                 _bounds,
    134.                 _argsBuffer,
    135.                 _argsOffset,
    136.                 _materialPropertyBlock,
    137.                 _shadowCastingMode,
    138.                 _receiveShadows,
    139.                 _layer,
    140.                 sceneCameras[0]);
    141.         }
    142. #endif
    143.         Graphics.DrawMeshInstancedIndirect(_mesh,
    144.             _subMeshIndex,
    145.             _material,
    146.             _bounds,
    147.             _argsBuffer,
    148.             _argsOffset,
    149.             _materialPropertyBlock,
    150.             _shadowCastingMode,
    151.             _receiveShadows,
    152.             _layer,
    153.             MainCameraManager.MainCam);
    154.     }
    155. }
    156.  
    157. public class DMIIRendererManager : SingletonBehaviour<DMIIRendererManager>
    158. {
    159.     private Dictionary<string, DMIIPayload> instancedRenderTargets = new Dictionary<string, DMIIPayload>();
    160.  
    161.     private int cachedInstanceCount = -1;
    162.     private int cachedSubMeshIndex = -1;
    163.     private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    164.  
    165.     private bool needsBufferUpdate = false;
    166.  
    167.     private void Update()
    168.     {
    169.         if (needsBufferUpdate)
    170.         {
    171.             foreach (DMIIPayload dmiiPayload in instancedRenderTargets.Values)
    172.             {
    173.                 dmiiPayload.UpdateBuffers();
    174.             }
    175.  
    176.             needsBufferUpdate = false;
    177.         }
    178.         _testForceUpdateBuffers();
    179.         foreach (DMIIPayload dmiiPayload in instancedRenderTargets.Values)
    180.         {
    181.             dmiiPayload.DrawMeshsInstancedIndirect();
    182.         }
    183.     }
    184.  
    185.     public void RequestBufferUpdate()
    186.     {
    187.         needsBufferUpdate = true;
    188.     }
    189.  
    190.     [Button("Force update buffers"), ConsoleButton("Force redraw meshes", "DEBUG")]
    191.     public static void _testForceUpdateBuffers()
    192.     {
    193.         foreach (DMIIPayload dmiiPayload in Instance.instancedRenderTargets.Values)
    194.         {
    195.             dmiiPayload.UpdateBuffers();
    196.         }
    197.     }
    198.  
    199.     public void AddChildRenderer(DMIIRenderer renderer)
    200.     {
    201.         if (!renderer)
    202.         {
    203.             D.LogError("Trying to pass in null as renderer. This should not happen!");
    204.             return;
    205.         }
    206.         string id = renderer.id;
    207.         if (!instancedRenderTargets.ContainsKey(id))
    208.         {
    209.             instancedRenderTargets.Add(id, new DMIIPayload());
    210.             instancedRenderTargets[id].instanceMesh = renderer.instanceMesh;
    211.             instancedRenderTargets[id].instanceMaterial = renderer.instanceMaterial;
    212.         }
    213.         instancedRenderTargets[id].subMeshIndex = renderer.subMeshIndex;
    214.         instancedRenderTargets[id].childRenderers.Add(renderer);
    215.         instancedRenderTargets[id].UpdateTexturePositionBuffer();
    216.         instancedRenderTargets[id].UpdateBuffers();
    217.     }
    218.  
    219.     public void RemoveChildRenderer(DMIIRenderer renderer)
    220.     {
    221.         if (!renderer)
    222.         {
    223.             D.LogError("Trying to pass in null as renderer. This should not happen!");
    224.             return;
    225.         }
    226.         string id = renderer.id;
    227.         if (!instancedRenderTargets.ContainsKey(id))
    228.         {
    229.             D.LogError("Trying to pass in renderer that is not being used");
    230.             return;
    231.         }
    232.         instancedRenderTargets[id].childRenderers.Remove(renderer);
    233.         if (instancedRenderTargets[id].instanceCount <= 0)
    234.         {
    235.             instancedRenderTargets.Remove(id);
    236.         }
    237.         instancedRenderTargets[id].UpdateTexturePositionBuffer();
    238.         instancedRenderTargets[id].UpdateBuffers();
    239.     }
    240.  
    241.     [Button("test")]
    242.     public void _debugPrint()
    243.     {
    244.         foreach (DMIIPayload dmiiPayload in instancedRenderTargets.Values)
    245.         {
    246.             foreach (var child in dmiiPayload.childRenderers)
    247.             {
    248.                 D.Log(child.parentTransform.position);
    249.             }
    250.         }
    251.     }
    252. }
    DMIIRenderer.cs which just talks to the manager and sets itself up:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Sirenix.OdinInspector;
    5. using UnityEngine;
    6. using Random = UnityEngine.Random;
    7.  
    8. public class DMIIRenderer : MonoBehaviour
    9. {
    10.     [Title("Visual settings")]
    11.     public float brightness = 0f;
    12.     public float contrast = 1f;
    13.     public float saturation = 1f;
    14.     public float value = 1f;
    15.     [Title("Components for renderer")]
    16.     public Mesh instanceMesh;
    17.     public Material instanceMaterial;
    18.     public int subMeshIndex = 0; // unused but if we ever want to use submeshindex it should be easy to add support
    19.     public Transform parentTransform;
    20.     public FRenderer fRenderer;
    21.  
    22.     public string id
    23.     {
    24.         get { return instanceMesh.name + instanceMaterial.name + subMeshIndex; }
    25.     }
    26.  
    27.     public bool DeviceSupportsInstancing
    28.     {
    29.         get { return SystemInfo.supportsInstancing; }
    30.     }
    31.  
    32.     private void Awake()
    33.     {
    34.         if (!DeviceSupportsInstancing)
    35.         {
    36.             return;
    37.         }
    38.         var renderer = GetComponent<MeshRenderer>();
    39.         if (renderer) renderer.enabled = false;
    40.  
    41.         if (!instanceMesh)
    42.         {
    43.             var meshFilter = GetComponent<MeshFilter>();
    44.             if (meshFilter) instanceMesh = meshFilter.sharedMesh;
    45.         }
    46.         if (renderer)
    47.         {
    48.             instanceMaterial = renderer.sharedMaterial;
    49.         }
    50.         if (!parentTransform)
    51.         {
    52.             parentTransform = transform;
    53.         }
    54.         if (!fRenderer)
    55.         {
    56.             var FRenderer = GetComponent<FRenderer>();
    57.             fRenderer = FRenderer;
    58.         }
    59.         DMIIRendererManager.Instance.AddChildRenderer(this);
    60.     }
    61.  
    62.     private void OnValidate()
    63.     {
    64.         if (!instanceMesh)
    65.         {
    66.             var meshFilter = GetComponent<MeshFilter>();
    67.             if (meshFilter) instanceMesh = meshFilter.sharedMesh; // using shared mesh for editor b/c prevents leak
    68.         }
    69.         if (!instanceMaterial)
    70.         {
    71.             var renderer = GetComponent<MeshRenderer>();
    72.             if (renderer) instanceMaterial = renderer.sharedMaterial; // using shared material for editor b/c prevents leak
    73.         }
    74.         if (!parentTransform)
    75.         {
    76.             parentTransform = transform;
    77.         }
    78.         if (!fRenderer)
    79.         {
    80.             var FRenderer = GetComponent<FRenderer>();
    81.             fRenderer = FRenderer;
    82.         }
    83.     }
    84.  
    85.     private void OnDestroy()
    86.     {
    87.         if (!DeviceSupportsInstancing)
    88.         {
    89.             return;
    90.         }
    91.         DMIIRendererManager.Instance.RemoveChildRenderer(this);
    92.     }
    93.  
    94.     private void OnDrawGizmos()
    95.     {
    96.         // Draw a semitransparent blue cube at the transforms position
    97.         Gizmos.color = Color.red;
    98.         Gizmos.DrawCube(transform.position, new Vector3(1, 1, 1));
    99.     }
    100.  
    101.     [Button("Force Update Buffers")]
    102.     private void _forceUpdateBuffers()
    103.     {
    104.         DMIIRendererManager._testForceUpdateBuffers();
    105.     }
    106. }
    This is the shader I use for drawing instanced objects:
    Code (CSharp):
    1. Shader "Instanced/DMII_compatible_shader"
    2. {
    3.     // maybe gridcell dmii compute?
    4.     Properties
    5.     {
    6.         _MainTex ("Main texture", 2D) = "white" {}
    7.     }
    8.     SubShader
    9.     {
    10.         Tags
    11.         {
    12.             "RenderType"="Opaque"
    13.         }
    14.         LOD 200
    15.  
    16.         CGPROGRAM
    17.         #pragma surface surf Lambert nofog nolightmap nodynlightmap noforwardadd
    18.  
    19.         #pragma multi_compile_instancing
    20.         #pragma instancing_options procedural:setup
    21.  
    22.         sampler2D _MainTex;
    23.  
    24.         struct Input
    25.         {
    26.             float2 uv_MainTex;
    27.         };
    28.  
    29.         #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    30.         StructuredBuffer<float4> positionBuffer;
    31.         StructuredBuffer<float4> texturePositionBuffer;
    32.         StructuredBuffer<float4> visualSettingsBuffer;
    33.         #endif
    34.  
    35.         void setup()
    36.         {
    37.             #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    38.             float4 positionData = positionBuffer[unity_InstanceID];
    39.          
    40.             unity_ObjectToWorld._11_21_31_41 = float4(positionData.w, 0, 0, 0);
    41.             unity_ObjectToWorld._12_22_32_42 = float4(0, positionData.w, 0, 0);
    42.             unity_ObjectToWorld._13_23_33_43 = float4(0, 0, positionData.w, 0);
    43.             unity_ObjectToWorld._14_24_34_44 = float4(positionData.xyz, 1);
    44.             unity_WorldToObject = unity_ObjectToWorld;
    45.             unity_WorldToObject._14_24_34 *= -1;
    46.             unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    47.             #endif
    48.         }
    49.  
    50.         float4 _MainTex_ScaleOffset;
    51.  
    52.         inline half3 applyBrightnessContrast(half3 aColor, float aBrightness, float aContrast)
    53.         {
    54.             return ((aColor - 0.5f) * aContrast + 0.5f) + aBrightness;
    55.         }
    56.  
    57.         //cheaper than saturation grey-scale function
    58.         inline half3 applyGrayscale(half3 aColor, float aSaturation)
    59.         {
    60.             half grayVal = (aColor.r + aColor.g + aColor.b) / 3;
    61.             return lerp(grayVal, aColor, aSaturation);
    62.         }
    63.  
    64.         void surf(Input IN, inout SurfaceOutput o)
    65.         {
    66.             float2 uv = IN.uv_MainTex;
    67.             #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    68.             float4 scaleOffset = texturePositionBuffer[unity_InstanceID];
    69.             uv = uv * scaleOffset.xy + scaleOffset.zw;
    70.          
    71.             half3 main = tex2D(_MainTex, uv).rgb;
    72.  
    73.             float brightness = visualSettingsBuffer[unity_InstanceID].x;
    74.             float contrast = visualSettingsBuffer[unity_InstanceID].y;
    75.             float saturation = visualSettingsBuffer[unity_InstanceID].z;
    76.             float colorValue = visualSettingsBuffer[unity_InstanceID].w;
    77.          
    78.             main = main * colorValue;
    79.             main = applyBrightnessContrast(main, brightness, contrast); //apply contrast and brightness
    80.             main = applyGrayscale(main, saturation); //apply saturation
    81.             o.Albedo = main.rgb;
    82.             #else
    83.             o.Albedo = tex2D(_MainTex, uv).rgb;
    84.             #endif
    85.         }
    86.         ENDCG
    87.     }
    88.     FallBack "Diffuse"
    89. }