Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

GPU Instancing in the 2d renderer?

Discussion in '2D' started by nicmarxp, May 29, 2020.

  1. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    I'm making a game where you can shoot asteroids, and a lot of small asteroid debris fly out. They are just 3-4 different models, but they vary slightly in color and size. That seems ideal for GPU Instancing.

    I made a material with the shader URP/2D/Sprite-Lit-Default and enabled the GPU Instancing checkbox and assigned this to the sprite renderer of the asteroid debris.

    But when I clone a gameobject, the batches still increase. Isn't that the same as draw calls?

    So am I doing this wrong? :)
     
  2. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    556
    I don't think the default sprite shader supports gpu instancing. I'm not completely sure though. I'd be interested in the answer to this.
     
  3. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Interesting, I just started messing with shader graph, but have no idea if that's something that could be made in there :)
     
  4. spryx

    spryx

    Joined:
    Jul 23, 2013
    Posts:
    556
    Doubtful. The shader graph trades complexity and optimization in favor of abstraction and ease of use. I could be completely wrong about the default sprite shader supporting instancing as internally, sprites are represented by simple quads.

    That said, I don't think the tilemap system has this issue as there is a single renderer on each tilemap. Any of the Unity 2d staff want to chime in? @rustum
     
  5. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Tilemaps should have great use of gpu instancing i guess. With the default URP>2d material there is a GPU instancing checkbox, but with my own shader graph material there isn't.
    upload_2020-6-1_7-6-11.png
     
  6. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,319
    https://forum.unity.com/threads/2d-system-optimizations.805905/

    Last word from a Unity dev for Tilemap GPU Instancing support ( Jan 31, 2020 ):
    Maybe there's an update @rustum could share about 2D system optimizations Unity is working on?
     
  7. rustum

    rustum

    Unity Technologies

    Joined:
    Feb 14, 2015
    Posts:
    190
    Hi @Lo-renzo and @spryx!
    We will share some information soon on all the performance and workflow improvements that we are making. As for the specific topic of Tilemap GPU Instancing, I'll let @ChuanXin respond with more detail.
     
    nicmarxp and spryx like this.
  8. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    We are looking into improving performance with the Tilemap and TilemapRenderer. This includes using GPU instancing for the TilemapRenderer. Depending on the situation with GPU instancing, there are issues which we need to look into which include sorting of the different Sprites used within the Tilemap and the sizes of the parameters passed in for instancing compared to standard batched rendering.

    If there are particular use-cases regarding this, do let us know as well so that we can handle this better!

    It does support GPU instancing. You will need to make a new material with the default sprite shader and activate GPU instancing. This should be the same for the URP and the default lit shader as well.

    upload_2020-6-2_15-53-36.png

    Batches are the groups of draw calls that can be done together. I am not certain about your GameObject/Renderer, but assuming that everything can be instanced together, it is still possible that there are enough items where more draw calls are required. Using the Frame Debugger can help identify with that.

    If it does not quite work out for you, do let us know about it! Screenshots and reproduction examples will help as well!
     
    nicmarxp and spryx like this.
  9. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Thank you @ChuanXin and @rustum. I will try to reproduce this in a simpler project when I have the chance.

    Is there anything special to consider when using a tilemap where each tile is 512x512? We're making a cartoon like game, which has quite a high resolution, but if it doesn't work, we would need to scale it down. I guess GPU instancing here would save some performance at least.
     
  10. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    I am not fully certain if GPU instancing would help with high texture resolutions. Also, high texture resolutions do result in less efficient usage of Sprite Atlasing, which is a good technique that can help with performance too. It would be interesting to know what the size of a Tile relative is to the screen, and the general amount of Tiles shown each frame too.

    For performance, it would be great if you could share some profiler screenshots for your project! I can understand if you want to keep things related to your product private also!
     
    nicmarxp likes this.
  11. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,511
    @ChuanXin , @rustum
    Can we please have some information on drawcall batching with Sprite rendering especially in accordance with :

    1. URP
    2. SRP batching
    3. GPU instancing
    4. 2D Renderer
    5. Sorting Group component + sorting order / layer ( does this affect batching at all ? )
     
  12. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Assuming that dynamic batching is running and the Renderers are rendered using the Transparent render queue (eg. 2D Renderers with the default Sprite shader):

    3. GPU instancing
    4. 2D Renderer
    5. Sorting Group component + sorting order / layer ( does this affect batching at all ? )
    Yes, it does! Renderers are batched based on the order they are sorted if their batching criteria is fulfilled. The sorting guide can be found here (https://docs.unity3d.com/Manual/2DSorting.html). From this sorted queue of renderers, the Unity rendering pipeline will try to batch the next renderer in sequence if the criteria matches here (https://docs.unity3d.com/Manual/DrawCallBatching.html). If the next renderer does not match in terms of the criteria (for SpriteRenderers, this is generally due to having different Textures), the current batch will be sent to be drawn and a new batch will be started. If the current batch of Renderers have GPU instancing enabled (and there is more than one Renderer), they will be instanced on the GPU. If not, they will be batched on the CPU. The FrameDebugger can help a lot in identifying why Renderers do not batch.

    2. SRP batching
    Currently, this is not supported for the 2D Renderers (SpriteRenderer, SpriteShapeRenderer and TilemapRenderer).

    1. URP
    @yuanxing_cai @Chris_Chu can explain better regarding URP and 2D lights!
     
    madbuggerswall and nicmarxp like this.
  13. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    @ChuanXin Thank you! I haven't reached a stage where we're optimizing, so I'll start a new thread in the future when we reach that point. :)
     
  14. Tony_Max

    Tony_Max

    Joined:
    Feb 7, 2017
    Posts:
    334
    Any updates on this? I have spent a lot time to figure out that rendering sprite is something very special from rendering mesh and sprite renderer not supports SRP batching even if SPRITE shader has SRP Batcher: compatible label in inspector, which is very confusing! I'm using 2020.3.22 unity.
     
    Jackrabbit82 likes this.
  15. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    No updates on this. SRP Batching is not supported for the 2D Renderers (SpriteRenderer, SpriteShapeRenderer and TilemapRenderer).
     
    Tony_Max likes this.
  16. Djayp

    Djayp

    Joined:
    Feb 16, 2015
    Posts:
    114
    It "just works", I don't know if it is optimal

    Code (CSharp):
    1. Shader "Universal Render Pipeline/2D/Sprite-Lit"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex("Diffuse", 2D) = "white" {}
    6.         _MaskTex("Mask", 2D) = "white" {}
    7.         _NormalMap("Normal Map", 2D) = "bump" {}
    8.     }
    9.  
    10.     HLSLINCLUDE
    11.     #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    12.     ENDHLSL
    13.  
    14.     SubShader
    15.     {
    16.         Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" }
    17.  
    18.         Blend SrcAlpha OneMinusSrcAlpha
    19.         Cull Off
    20.         ZWrite Off
    21.  
    22.         Pass
    23.         {
    24.             Tags { "LightMode" = "Universal2D" }
    25.             HLSLPROGRAM
    26.             #pragma exclude_renderers gles gles3 glcore
    27.             #pragma target 4.5
    28.  
    29.             #pragma vertex CombinedShapeLightVertex
    30.             #pragma fragment CombinedShapeLightFragment
    31.             #pragma multi_compile USE_SHAPE_LIGHT_TYPE_0 __
    32.             #pragma multi_compile USE_SHAPE_LIGHT_TYPE_1 __
    33.             #pragma multi_compile USE_SHAPE_LIGHT_TYPE_2 __
    34.             #pragma multi_compile USE_SHAPE_LIGHT_TYPE_3 __
    35.  
    36.             #pragma multi_compile_instancing
    37.             #pragma multi_compile _ DOTS_INSTANCING_ON
    38.  
    39.             struct Attributes
    40.             {
    41.                 float3 positionOS   : POSITION;
    42.                 float4 color        : COLOR;
    43.                 float2  uv           : TEXCOORD0;
    44.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    45.             };
    46.  
    47.             struct Varyings
    48.             {
    49.                 float4  positionCS  : SV_POSITION;
    50.                 half4   color       : COLOR;
    51.                 float2    uv          : TEXCOORD0;
    52.                 half2    lightingUV  : TEXCOORD1;
    53.                 UNITY_VERTEX_OUTPUT_STEREO
    54.             };
    55.  
    56.             #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/LightingUtility.hlsl"
    57.  
    58.             TEXTURE2D(_MainTex);
    59.             SAMPLER(sampler_MainTex);
    60.             TEXTURE2D(_MaskTex);
    61.             SAMPLER(sampler_MaskTex);
    62.             TEXTURE2D(_NormalMap);
    63.             SAMPLER(sampler_NormalMap);
    64.  
    65.             CBUFFER_START(UnityPerMaterial)
    66.             half4 _MainTex_ST;
    67.             half4 _NormalMap_ST;
    68.             CBUFFER_END
    69.  
    70.             #if USE_SHAPE_LIGHT_TYPE_0
    71.             SHAPE_LIGHT(0)
    72.             #endif
    73.  
    74.             #if USE_SHAPE_LIGHT_TYPE_1
    75.             SHAPE_LIGHT(1)
    76.             #endif
    77.  
    78.             #if USE_SHAPE_LIGHT_TYPE_2
    79.             SHAPE_LIGHT(2)
    80.             #endif
    81.  
    82.             #if USE_SHAPE_LIGHT_TYPE_3
    83.             SHAPE_LIGHT(3)
    84.             #endif
    85.  
    86.             Varyings CombinedShapeLightVertex(Attributes v)
    87.             {
    88.                 Varyings o = (Varyings)0;
    89.                 UNITY_SETUP_INSTANCE_ID(v);
    90.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    91.  
    92.                 o.positionCS = TransformObjectToHClip(v.positionOS);
    93.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    94.                 float4 clipVertex = o.positionCS / o.positionCS.w;
    95.                 o.lightingUV = ComputeScreenPos(clipVertex).xy;
    96.                 o.color = v.color;
    97.                 return o;
    98.             }
    99.  
    100.             #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/CombinedShapeLightShared.hlsl"
    101.  
    102.             half4 CombinedShapeLightFragment(Varyings i) : SV_Target
    103.             {
    104.                 half4 main = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
    105.                 half4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, i.uv);
    106.  
    107.                 return CombinedShapeLightShared(main, mask, i.lightingUV);
    108.             }
    109.             ENDHLSL
    110.         }
    111.  
    112.         Pass
    113.         {
    114.             Tags { "LightMode" = "NormalsRendering"}
    115.             HLSLPROGRAM
    116.             #pragma exclude_renderers gles gles3 glcore
    117.             #pragma target 4.5
    118.  
    119.             #pragma vertex NormalsRenderingVertex
    120.             #pragma fragment NormalsRenderingFragment
    121.  
    122.             #pragma multi_compile_instancing
    123.             #pragma multi_compile _ DOTS_INSTANCING_ON
    124.  
    125.             struct Attributes
    126.             {
    127.                 float3 positionOS   : POSITION;
    128.                 float4 color        : COLOR;
    129.                 float2 uv            : TEXCOORD0;
    130.                 float4 tangent      : TANGENT;
    131.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    132.             };
    133.  
    134.             struct Varyings
    135.             {
    136.                 float4  positionCS        : SV_POSITION;
    137.                 half4   color            : COLOR;
    138.                 float2    uv                : TEXCOORD0;
    139.                 half3   normalWS        : TEXCOORD1;
    140.                 half3   tangentWS        : TEXCOORD2;
    141.                 half3   bitangentWS        : TEXCOORD3;
    142.                 UNITY_VERTEX_OUTPUT_STEREO
    143.             };
    144.  
    145.             TEXTURE2D(_MainTex);
    146.             SAMPLER(sampler_MainTex);
    147.             TEXTURE2D(_NormalMap);
    148.             SAMPLER(sampler_NormalMap);
    149.  
    150.             CBUFFER_START(UnityPerMaterial)
    151.             half4 _MainTex_ST;
    152.             half4 _NormalMap_ST;
    153.             CBUFFER_END
    154.  
    155.             Varyings NormalsRenderingVertex(Attributes attributes)
    156.             {
    157.                 Varyings o = (Varyings)0;
    158.                 UNITY_SETUP_INSTANCE_ID(attributes);
    159.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    160.  
    161.                 o.positionCS = TransformObjectToHClip(attributes.positionOS);
    162.                 o.uv = TRANSFORM_TEX(attributes.uv, _NormalMap);
    163.                 o.uv = attributes.uv;
    164.                 o.color = attributes.color;
    165.                 o.normalWS = TransformObjectToWorldDir(float3(0, 0, -1));
    166.                 o.tangentWS = TransformObjectToWorldDir(attributes.tangent.xyz);
    167.                 o.bitangentWS = cross(o.normalWS, o.tangentWS) * attributes.tangent.w;
    168.                 return o;
    169.             }
    170.  
    171.             #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/NormalsRenderingShared.hlsl"
    172.  
    173.             half4 NormalsRenderingFragment(Varyings i) : SV_Target
    174.             {
    175.                 half4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
    176.                 half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv));
    177.                 return NormalsRenderingShared(mainTex, normalTS, i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz);
    178.             }
    179.             ENDHLSL
    180.         }
    181.         Pass
    182.         {
    183.             Tags { "LightMode" = "UniversalForward" "Queue"="Transparent" "RenderType"="Transparent"}
    184.  
    185.             HLSLPROGRAM
    186.             #pragma exclude_renderers gles gles3 glcore
    187.             #pragma target 4.5
    188.  
    189.             #pragma vertex UnlitVertex
    190.             #pragma fragment UnlitFragment
    191.  
    192.             #pragma multi_compile_instancing
    193.             #pragma multi_compile _ DOTS_INSTANCING_ON
    194.  
    195.             struct Attributes
    196.             {
    197.                 float3 positionOS   : POSITION;
    198.                 float4 color        : COLOR;
    199.                 float2 uv            : TEXCOORD0;
    200.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    201.             };
    202.  
    203.             struct Varyings
    204.             {
    205.                 float4  positionCS        : SV_POSITION;
    206.                 float4  color            : COLOR;
    207.                 float2    uv                : TEXCOORD0;
    208.                 UNITY_VERTEX_OUTPUT_STEREO
    209.             };
    210.  
    211.             TEXTURE2D(_MainTex);
    212.             SAMPLER(sampler_MainTex);
    213.  
    214.             CBUFFER_START(UnityPerMaterial)
    215.             half4 _MainTex_ST;
    216.             half4 _NormalMap_ST;
    217.             CBUFFER_END
    218.  
    219.             Varyings UnlitVertex(Attributes attributes)
    220.             {
    221.                 Varyings o = (Varyings)0;
    222.                 UNITY_SETUP_INSTANCE_ID(attributes);
    223.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    224.  
    225.                 o.positionCS = TransformObjectToHClip(attributes.positionOS);
    226.                 o.uv = TRANSFORM_TEX(attributes.uv, _MainTex);
    227.                 o.uv = attributes.uv;
    228.                 o.color = attributes.color;
    229.                 return o;
    230.             }
    231.  
    232.             float4 UnlitFragment(Varyings i) : SV_Target
    233.             {
    234.                 float4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
    235.                 return mainTex;
    236.             }
    237.             ENDHLSL
    238.         }
    239.     }
    240. }
    241.  
     
  17. AlexVillalba

    AlexVillalba

    Joined:
    Feb 7, 2017
    Posts:
    334
    Sorry for necroing this thread but it's important to me. Was GPU instancing finally applied to TilemapRenderers?
     
    Tony_Max likes this.
  18. msfredb7

    msfredb7

    Joined:
    Nov 1, 2012
    Posts:
    143
    So, after reading around the forum, am I correct to say that:
    Using a 2D renderer (e.g. SpriteRenderer) and a custom sprite shader, sprites cannot be batched if they have different properties?

    Example:
    I have a car sprite and a custom sprite shader that allows me to change the door colors. I want to render 100 cars with different door colors. Is there no way to batch this?
    1. SRP Batcher is not supported
    2. MaterialPropertyBlocks break batching in SRP
    If I'm correct, are there any plans to support this in the future?
     
    Last edited: Aug 24, 2023
  19. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,319
    Added in 2023.1
    • 2D: Added SRP Batching for 2D Renderers and Particle Renderer to support URP.
     
    Djayp, Tony_Max and msfredb7 like this.