Particle mesh GPU Instancing

To be or not to be enabled

I am testing my Firecracker asset to compare the draw call batching in 5.6 and 2018.1.0b2.

I only initiated the middle segments, which are generated (emitted) by particle systems segment by segment, without anything else and captured the stats before the explosion effect kicks in.

The result is baffling. It seems unchecking GPU Instancing in particle system renderer module gives better batching result than enabling it. The same option in the material property has no effect on the result.

Did you add instancing support correctly to your shader? (Diffuse Intensify). Read the particle link here for instructions: Preview of 2018.1 Features

Not sure what the 3 screenshots from 2018.1 represent, other than the instancing checkbox is checked in 1 of them.

That Particles/Diffuse Intensify shader is a surface shader. The GPU instancing manual says that surface shader doesn’t need to be modified for enabling it, if I understand it correctly.

Shader "Particles/Diffuse Intensify" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glow ("Intensity", Range(0, 9)) = 1
        _Offset ("Diffuse Offset", Range(0, 1)) = 0
    }
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" "PreviewType"="Plane" }
        LOD 200
       
        CGPROGRAM
        #pragma surface surf Standard addshadow fullforwardshadows
        #pragma target 3.0
        #include "UnityCG.cginc"
        #include "UnityStandardParticleInstancing.cginc"

        sampler2D _MainTex;
        half _Glow;
        half _Offset;

        struct Input {
            float2 uv_MainTex;
            float3 color:COLOR;
        };

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = (c.rgb * IN.color.rgb - _Offset) * (1 + _Glow);
        }
        ENDCG
    }
    FallBack "Unlit/Texture"
}

Particles use Procedural Instancing, which does require a shader modification. The previous link I posted explains and provides an example. The real docs + manual pages will follow later in the 2018.1 Beta.

1 Like

I just added #pragma instancing_options procedural:vertInstancingSetup and #include “UnityStandardParticleInstancing.cginc” to my surface shader and after enabling (particle renderer) GPU Instancing, the “saved by batch” stat skyrocketed, I assume this is the correct result then?

Another question is, do you recommend particle mesh GPU instancing for mobile or VR?

Sounds likely. This view will tell you for sure: https://docs.unity3d.com/Manual/ProfilerRendering.html (the screenshot on the page looks out of date, but in Unity, it should tell you how much instancing was performed.)

If you want to draw many particle meshes, then it will be the only way to get reasonable performance on all supported platforms/devices. Even in my conservative tests using cubes, (see the Beta doc linked above), I am seeing a 100x performance improvement.

If you only want to draw a small number of particle meshes, then it depends where you have spare performance in your game as a whole:

  • game is GPU limited? May not want to use this new feature, as it has a small extra GPU cost
  • game is CPU limited? This is a great way to optimize CPU usage

So it’s not about recommending it for a particular platform, but more about the use-cases and the overall game performance.

1 Like

In addition to the Profiler, Unity’s Frame Debugger is also a great tool when it comes to rendering:
https://docs.unity3d.com/Manual/FrameDebugger.html

Just encountered a glitch that whenever the mesh particles, using the amended surface shader, receive point light, the actual mesh particles don’t receive it but instead a new mesh is duplicated at world origin (0,0,0) and receives the point light. Should I use a different instancing procedural function or just file a bug report? At first glance I don’t see any other instancing setup lying around in UnityStandardParticleInstancing.cginc.

I think I recall an issue with selection outline or picking volume, it’s probably that. (it requires some other work to be done to properly support it)

Hi, I just added #pragma instancing_options procedural:vertInstancingSetup and #include “UnityStandardParticleInstancing.cginc” like ifurkend did. But I need to use Custom Vertex Streams for my shaders. And if I enable them (even with no additional streams, just default ones), everything becomes a complete mess. Here is a short video:

You’ll need to add the custom data to the instancing struct in the shader. I’m not in the office currently so can’t send you an example, sorry. I think there is a define in the cginc you can override, to allow you to declare your own struct layout.

No, please disregard all this.

Particles use Procedural Instancing, which works differently. This way guarantees 1 draw call, which reduces the CPU work needed for large instance counts, compared to the cbuffer approach used for the “normal” instancing. But it is a bit more fiddly to set up :slight_smile:

The particle instancing cginc sets up the defaults for you. If you want to have custom inputs, you can override it like this:

#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
#endif
struct MyParticleInstanceData
{
    float3x4 transform;
    uint color;
    float animFrame;
    float4 myCustomData;
};

Add it before including the instancing cginc. The vertex stream UI provides info on the correct order/contents for the struct.

My bad, I totally didnt read the particle part!

1 Like